Pyright Static Type Checker

repository·main·Indexed 12 days ago

https://github.com/microsoft/pyright

A high-performance, standards-based static type checker for Python designed for large-scale codebases. It is available as a command-line tool, a Language Server (LSP), and a Visual Studio Code extension (ms-pyright.pyright).

Tokens
36.2K
Snippets
75
Records
138
Agent score
96%

What's inside Pyright

  1. Overview of Pyright

    main

    Pyright is a high-performance, standards-compliant static type checker for Python. It is designed to handle large Python codebases efficiently. Pyright can be consumed in three primary ways:

    1. Command-line tool: For running type checks directly from the terminal.
    2. Language Server: For integration with various editors via the Language Server Protocol (LSP).
    3. Visual Studio Code Extension: A dedicated extension (ms-pyright.pyright) for seamless integration within VS Code.
  2. Use Pyright as a static type checker

    main

    Pyright is a high-performance, standards-based static type checker for Python designed for large codebases. You can use it in two primary ways:

    1. Command-line tool: Run Pyright directly from your terminal for CI/CD integration or local checking.
    2. Visual Studio Code Extension: Install the ms-pyright.pyright extension to get type checking and language service features directly in your editor.

    You can also test Pyright's capabilities in your browser using the Pyright Playground.

  3. Understand Pyright's type checking capabilities

    main

    Pyright is a fast, incremental type checker that supports a wide range of Python typing standards (PEPs) and features:

    Core Features

    • Type Inference: Automatically infers types for function return values, instance variables, class variables, and globals.
    • Control Flow Awareness: Type guards that understand conditional code flow (e.g., if/else statements).
    • Watch Mode: Supports a "watch" mode for fast incremental updates as files are modified.

    PEP Support

    Pyright supports numerous PEPs, including but not limited to:

    • Generics & Annotations: PEP 484 (type hints), PEP 585 (standard collection generics), PEP 695 (type parameter syntax), PEP 613 (explicit type aliases).
    • Structural Typing: PEP 544 (structural subtyping/protocols).
    • Advanced Typing: PEP 589 (TypedDict), PEP 591 (final), PEP 604 (union syntax), PEP 646 (variadic generics), PEP 673 (Self type), PEP 742 (TypeIs).
    • Metadata & Decorators: PEP 681 (dataclass transform), PEP 698 (override decorator), PEP 702 (deprecations).
  4. Understand the differences between Pyright and Mypy

    main

    Pyright and Mypy are both Python static type checkers, but they differ in design goals and implementation.

    Key Differences:

    • Performance: Pyright is designed for high performance (often 3x-5x faster) and is optimized for use as a Language Server (LSP). It uses a 'lazy' or 'just-in-time' evaluation model, whereas Mypy uses a multi-pass architecture.
    • Error Recovery: Pyright implements its own parser that recovers gracefully from syntax errors to continue analysis. Mypy uses the Python interpreter's parser and does not support recovery after a syntax error.
    • Type Checking Unannotated Code: By default, Pyright type checks all code, including unannotated functions. Mypy skips unannotated functions unless the --check-untyped-defs flag is enabled.
    • Plugins: Mypy supports a plugin mechanism for library-specific behaviors. Pyright does not support plugins, preferring to work with the typing community to extend the core typing specification (e.g., via PEP 681) to ensure performance and robustness.
  5. What is the Pyright Type Server and the Type Server Protocol (TSP)?

    main

    The Pyright Type Server is a specialized component that provides direct access to Python type information via the Type Server Protocol (TSP). Unlike the Language Server Protocol (LSP), which is optimized for editor features (like hover or completions), TSP is designed for tools that need deep type information without editor-oriented overhead.

    TSP is a JSON-RPC protocol layered on top of the same transport as LSP. A client can open documents using standard LSP notifications (e.g., textDocument/didOpen) and then query the type server for specific type-related data.

  6. Understand how Pyright resolves imports and type stubs

    main

    Pyright prioritizes type stub (.pyi) files over Python source (.py) files when resolving imports.

    Resolution Order

    1. Type Stubs (.pyi): Pyright always attempts to resolve an import with a .pyi file first.
    2. Inlined Types (py.typed): If no stub is found, Pyright checks if the package contains a py.typed file (per PEP 561). If present, Pyright uses the inline type information from the source.
    3. Fallback to Unknown: If no stub is found and no py.typed file exists, Pyright treats all symbols from that module as type Unknown. Wildcard imports (from foo import *) will not populate the namespace with specific symbol names.

    To ensure high-quality static type checking, it is highly recommended to use packages that include py.typed or provide type stub files.

  7. Clean up unnecessary ignore comments

    main
    If the reportUnnecessaryTypeIgnoreComment configuration option is enabled, Pyright will report any # type: ignore or # pyright: ignore comments that are no longer needed (e.g., because the code no longer violates the rule), allowing you to keep your codebase clean.
  8. How Pyright handles inferred return types

    main

    If a function or method lacks a return type annotation, Pyright infers the return type from return and yield statements within the function body (including the implied return None at the end). This enables better completion suggestions and coverage without requiring manual annotations for trivial types.

    In contrast, Mypy assumes functions without return annotations have a return type of Any.

  9. How Pyright handles the 'Unknown' type

    main

    Pyright distinguishes between explicit and implicit forms of Any:

    • Explicit Any: When a developer explicitly uses the Any type (e.g., list[Any]).
    • Implicit Any (Unknown): When a type is missing or cannot be determined (e.g., list becomes list[Unknown]).

    Pyright's "strict" modes include checks that report the use of Unknown types, helping developers identify where missing type information might be masking errors.

  10. Leverage Narrowing for Implied Else

    main

    Pyright can perform narrowing even when an else block is missing, provided it can statically prove that the code must have entered one of the preceding if or elif blocks. This is known as narrowing for implied else.

    This is particularly useful for:

    • Exhausting Enums: If you check every member of an Enum in an if/elif chain, Pyright knows the variable is fully handled.
    • Exhausting Unions: If you check every type in a Union (e.g., str | int), Pyright knows the variable is handled.

    Limitations:

    • Only works with simple names (not member access or index expressions).
    • Requires the name to have an explicit type annotation.
    • Does not work with simple truthiness/falsiness guards.
    from enum import Enum
    
    class Color(Enum):
        RED = 1
        BLUE = 2
        GREEN = 3
    
    def func3(color: Color) -> str:
        if color == Color.RED or color == Color.BLUE:
            return "yes"
        elif color == Color.GREEN:
            return "no"
        # Pyright knows all Color members are covered, so no 'None' return error
  11. How Pyright handles constructor calls

    main

    Pyright attempts to follow runtime behavior for constructor calls. It evaluates the flow: metaclass.__call__ $\rightarrow$ class.__new__ $\rightarrow$ class.__init__.

    If __new__ returns an instance of the class (or a child), Pyright proceeds to evaluate __init__. If a custom metaclass is present, Pyright evaluates its __call__ method. If a class provides a __new__ method that returns a type other than the class being constructed, Pyright assumes __init__ will not be called.

  12. Configure Pyright execution environments

    main

    Pyright supports configuration files that allow for granular control over settings. You can define different "execution environments" for specific subdirectories within your source base. This is useful for monorepos or projects with mixed requirements. Each environment can specify its own:

    • module search paths
    • python language versions
    • platform targets