Skip to content

Latest commit

 

History

History
169 lines (101 loc) · 10.1 KB

pyright.md

File metadata and controls

169 lines (101 loc) · 10.1 KB

pyright

pyright is Microsoft's Python type checker (requires node). It has active support and deployment, is fast, and detects a lot of issues other type checkers miss. It improves on the python vscode extension with type-checking and the detection of invalid imports.

pylance is a VS Code language server extension that bundles pyright and other goodies like auto-imports, code completion and additional stubs (eg: pandas, matplotlib)

typeCheckingMode

pyright.typeCheckingMode (or python.analysis.typeCheckingMode for pylance) can be:

  • off = all type-checking rules are disabled, but Python syntax and semantic errors are still reported and in pylance auto-complete suggestions are made
  • basic = use when easing into type checking on existing code-bases, or on code-bases that use libraries with poor quality stubs (eg: pandas). Doesn't check for some things that mypy does, like incompatible overrides.
  • strict = new code-bases with high quality stubs should use this. Will error with reportUnknownParameterType when type hints are missing from functions. Finds a lot of things mypy doesn't.

See configOptions.ts for the specific rules turned on and their level (eg: warning/error) for each mode.

Strict mode can be enabled on a per file basis via a comment, and individual rules disabled, see #601.

Configuration

A pyrightconfig.json file in the root configures settings for the project. This is recommended so that everyone working on the project uses the same settings.

VSCode settings.json configures the default rules.

Pyright applies rules settings in the following order:

  1. Default rule settings
  2. Individual overrides in the pyrightconfig.json file
  3. Strict (for all or for specific subdirectories)
  4. File-level comments

Example settings.json for Pylance, using basic mode and enabling additional rules:

  "python.languageServer": "Pylance",
  "python.analysis.typeCheckingMode": "basic",
  "python.analysis.diagnosticSeverityOverrides": {
    "reportIncompatibleMethodOverride": "error"
  }

PyCharm compatibility

To mimic PyCharm as closely as possible, use basic typeCheckingMode and set "strictDictionaryInference": true in pyrightconfig.json

Source code in sub-directories (vscode)

By default, Pyright/Pylance adds the root directory of the workspace to the search path. It also adds src/ if it's present, since it's a common convention.

You can add additional subdirectories to the search path by modifying python.analysis.extraPaths in .vscode/settings.json, eg: to add ./awesome_app/ to the search path (ref):

{
  "python.pythonPath": "/Users/tekumara/.virtualenvs/myapp/bin/python",
  "python.analysis.extraPaths": ["awesome_app"]
}

Understanding Type Inference

Lists with mixed types (eg: [1, "a"]):

  • In basic mode with be inferred as List[Unknown] ie: ignored
  • If strictListInference: "true", will be inferred as as the union of the elements' types, eg: List[int | str]

See List Expression

Unknown is used whenever a type cannot be inferred. It is a special form of Any and so will create blind spots in type checking. Its distinct from Any so Pyright can warn when types are not declared when the reportUnknown* diagnostics are enabled.

For more info see Understanding Type Inference.

Type narrowing and type guards

pyright can narrow a type to a more specific type based on the code path taken. See Type narrowing.

Import Resolution

Pyright locates .pyi stubs in several locations including typeshed stubs it vendors, your project workspace, and lib/site-packages. To find lib/site-packages pyright needs to run inside your virtualenv, or have the venv and venvPath configured in pyrightconfig.json.

Use library code for types

Many libraries lack stubs. However their .py files can contain partial or complete type annotation. To use annotations in .py files, and infer any missing types, when stubs are missing:

  • specify the --lib command-line argument
  • set "python.analysis.useLibraryCodeForTypes": true for the pyright vscode extension. In Pylance this defaults to true.
  • set "useLibraryCodeForTypes": true in pyrightconfig.json. NB: Setting this to false will override Pylance.

useLibraryCodeForTypes is a double-edged sword. On the one hand, it can avoid issues like:

  12:32 - error: "client" is not a known member of module (reportGeneralTypeIssues)
  12:26 - error: Type of "client" is unknown (reportUnknownMemberType)

But on the other hand, the type information is inferred and can be incorrect. Some of these problems can only be fixed by the third party library author, and/or a type stub. Inference from source files can also be slow and can result in a sluggish experience for complex libraries like tensorflow.

The feature was added to provide completion suggestions when using Pylance (see pyright/#945). Setting "useLibraryCodeForTypes": false will prevent type errors from incorrectly typed libraries. But, it will also disable "Go to Definition" behaviour in Pylance (see pylance-release/#278).

As of pyright 1.1.303 useLibraryCodeForTypes defaults to true.

Missing Type Stubs

When pyright is run in strict mode and type stubs are missing it will generate a reportMissingTypeStubs error.

This can be fixed by generating draft type stubs which by default are stored in typings/, eg:

pyright --createstub botocore

These stubs are a first draft intended for type-checking and are meant to be manually improved. --createstub emits a comment with the return type for functions if it can be inferred. Whilst these may be incorrect they can reduce the manual work needed (see #1916).

--createstub differ from useLibraryCodeForTypes which is intended to create low-quality type information that's usually insufficient for type checking but may be sufficient for completion suggestions. (ref)

Compared to stubgen from mypy:

  • --createstub does a better job at inferring function return types
  • stubgen does a better job at inferring some function args
  • --createstub includes docstrings in the generated stubs

Reference:

NB: .pyi stubs in typings/ take precedence over the same stubs in the virtualenv.

Checking a subset of files

pyright can be supplied a set of files on the the command line, in which case it will ignore pyrightconfig.json and use the default configuration. For this reason, when using pyright in a pre-commit hook you probably want to specify pass_filenames: false.

Pylance bundled stubs

Pylance bundles stubs for pandas, matplotlib and other libraries. This stubs are incomplete. If you encounter a type error using these libraries that looks incorrect, first check the open issues on microsoft/pylance-release before reporting it.

Exporting symbols from a typed library

Python doesn't have an explicit export keyword. Instead conventions has arisen on how to specify which modules and symbols in a library are its public interface. These include the following in in __init__.py:

  • specifying exported symbols via the __all__ symbol
  • using a redundant module or symbol alias during the import, eg: from . import box as box. See PEP 484 and the example here in rich #1596.

pyright 1.1.168 and later errors with reportPrivateImportUsage when trying to import symbols that it considers private (ie: haven't be declared as above) from a type stubs or a py.typed library.

For more info see this comment.

Ignore specific type errors

To ignore reportGeneralTypeIssues for the whole file:

# pyright: reportGeneralTypeIssues=false

To ignore on a specific line:

# pyright: ignore[reportOptionalMemberAccess,reportOptionalSubscript,reportArgumentType,reportPrivateImportUsage]

Alternatives to pyright

Pyre can't find modules (#279) without specifying the search_path pointing to site-packages. It's slow (11 secs) and doesn't find any issues out-of-the-box.

Mypy has > 1k open issues.

Pyright strict mode detects the most errors. Issues in pyright are quickly addressed. One drawback is it requires node and doesn't have a pypi distribution #819.