Did a consistency pass on documentation markdown.

This commit is contained in:
Eric Traut 2023-03-06 18:10:33 -07:00
parent f7db5822ef
commit 08b2cb4698
14 changed files with 107 additions and 107 deletions

View File

@ -1,16 +1,16 @@
- Getting started
- Getting Started
- [Installation](installation.md)
- [Type Concepts](type-concepts.md)
- [Getting Started with Type Checking](getting-started.md)
- [Pyright Features](features.md)
- [Type Checking Your Code](getting-started.md)
- [Features](features.md)
- [FAQ](faq.md)
- Customization
- [Continuous Integration (CI)](ci-integration.md)
- [Configuration](configuration.md)
- [Configuration Options](configuration.md#main-pyright-config-options)
- [Configuration Options](configuration.md#main-configuration-options)
- [Diagnostic Rules](configuration.md#type-check-diagnostics-settings)
- [Execution Environments](configuration.md#execution-environment-options)
- [Sample pyrightconfig.json](configuration.md#sample-config-file)

View File

@ -1,4 +1,4 @@
# Extending Builtins
## Extending Builtins
The Python interpreter implicitly adds a set of symbols that are available within every module even though they are not explicitly imported. These so-called “built in” symbols include commonly-used types and functions such as “list”, “dict”, “int”, “float”, “min”, and “len”.

View File

@ -1,11 +1,11 @@
# VS Code Commands
## VS Code Commands
Pyright offers the following commands, which can be invoked from VS Codes “Command Palette”, which can be accessed from the View menu or by pressing Cmd-Shift-P.
## Organize Imports
### Organize Imports
This command reorders all imports found in the global (module-level) scope of the source file. As recommended in PEP8, imports are grouped into three groups, each separated by an empty line. The first group includes all built-in modules, the second group includes all third-party modules, and the third group includes all local modules.
Within each group, imports are sorted alphabetically. And within each “from X import Y” statement, the imported symbols are sorted alphabetically. Pyright also rewraps any imports that don't fit within a single line, switching to multi-line formatting.
## Restart Server
### Restart Server
This command forces the type checker to discard all of its cached type information and restart analysis. It is useful in cases where new type stubs or libraries have been installed.

View File

@ -1,8 +1,8 @@
# Comments
## Comments
Some behaviors of pyright can be controlled through the use of comments within the source file.
## Type Annotations
### Type Annotations
Versions of Python prior to 3.6 did not support type annotations for variables. Pyright honors type annotations found within a comment at the end of the same line where a variable is assigned.
```python
@ -13,7 +13,7 @@ self._target = 3 # type: int | str
Future versions of Python will likely deprecate support for type annotation comments. The “reportTypeCommentUsage” diagnostic will report usage of such comments so they can be replaced with inline type annotations.
## File-level Type Controls
### File-level Type Controls
Strict type checking, where most supported type-checking switches generate errors, can be enabled for a file through the use of a special comment. Typically this comment is placed at or near the top of a code file on its own line.
```python
@ -38,7 +38,7 @@ Diagnostic levels are also supported.
# pyright: reportPrivateUsage=warning, reportOptionalCall=error
```
## Line-level Diagnostic Suppression
### Line-level Diagnostic Suppression
PEP 484 defines a special comment `# type: ignore` that can be used at the end of a line to suppress all diagnostics emitted by a type checker on that line. Pyright supports this mechanism.

View File

@ -1,4 +1,4 @@
# Pyright Configuration
## Pyright Configuration
Pyright offers flexible configuration options specified in a JSON-formatted text configuration. By default, the file is called “pyrightconfig.json” and is located within the root directory of your project. Multi-root workspaces (“Add Folder to Workspace…”) are supported, and each workspace root can have its own “pyrightconfig.json” file. For a sample pyrightconfig.json file, see [below](configuration.md#sample-config-file).
@ -6,7 +6,7 @@ Pyright settings can also be specified in a `[tool.pyright]` section of a “pyp
Relative paths specified within the config file are relative to the config files location. Paths with shell variables (including `~`) are not supported. Paths within a the config file should generally be relative paths so the config file can be shared by other developers who contribute to the project.
## Main Pyright Config Options
## Main Configuration Options
**include** [array of paths, optional]: Paths of directories or files that should be included. If no paths are specified, pyright defaults to the directory that contains the config file. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character). If no include paths are specified, the root path for the workspace is assumed.

View File

@ -1,4 +1,4 @@
# Frequently Asked Questions
## Frequently Asked Questions
**Q:** What is the difference between Pyright and [Pylance](https://github.com/microsoft/pylance-release)?

View File

@ -1,4 +1,4 @@
# Pyright Features
## Pyright Features
### Speed
Pyright is a fast type checker meant for large Python source bases. It can run in a “watch” mode and performs fast incremental updates when files are modified.

BIN
docs/img/PyrightSmall.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,6 +1,6 @@
# Import Resolution
## Import Resolution
## Resolution Order
### Resolution Order
If the import is relative (the module name starts with one or more dots), it resolves the import relative to the path of the importing source file.
For absolute (non-relative) imports, Pyright employs the following resolution order:
@ -28,7 +28,7 @@ For absolute (non-relative) imports, Pyright employs the following resolution or
## Configuring Your Python Environment
### Configuring Your Python Environment
Pyright does not require a Python environment to be configured if all imports can be resolved using local files and type stubs. If a Python environment is configured, it will attempt to use the packages installed in the `site-packages` subdirectory during import resolution.
Pyright uses the following mechanisms (in priority order) to determine which Python environment to use:
@ -39,7 +39,7 @@ Pyright uses the following mechanisms (in priority order) to determine which Pyt
3. As a fallback, use the default Python environment (i.e. the one that is invoked when typing `python` in the shell).
## Editable installs
### Editable installs
[PEP 660](https://peps.python.org/pep-0660/) enables build backends (e.g. setuptools) to
use import hooks to direct the [import machinery](https://docs.python.org/3/reference/import.html)
@ -55,21 +55,21 @@ the editable install to use `.pth` files instead of import hooks. See your build
documentation for details on how to do this. We have provided some basic information for
common build backends below.
### Setuptools
#### Setuptools
Setuptools currently supports two ways to request:
[“compat mode”](https://setuptools.pypa.io/en/latest/userguide/development_mode.html#legacy-behavior)
where a `.pth` file will be used -- a config setting and an environment variable. Another
option is [“strict mode”](https://setuptools.pypa.io/en/latest/userguide/development_mode.html#strict-editable-installs)
which uses symlinks instead.
### Hatch/Hatchling
#### Hatch/Hatchling
[Hatchling](https://hatch.pypa.io/latest/config/build/#dev-mode) uses `.pth` files by
default. It will only use import hooks if you set `dev-mode-exact` to `true`.
### PDM
#### PDM
[PDM](https://pdm.fming.dev/latest/pyproject/build/#editable-build-backend) uses `.pth`
files by default. It will only use import hooks if you set `editable-backend` to
`"editables"`.
## Debugging Import Resolution Problems
### Debugging Import Resolution Problems
The import resolution mechanisms in Python are complicated, and Pyright offers many configuration options. If you are encountering problems with import resolution, Pyright provides additional logging that may help you identify the cause. To enable verbose logging, pass `--verbose` as a command-line argument or add the following entry to the config file `"verboseOutput": true`. If you are using the Pyright VS Code extension, the additional logging will appear in the Output tab (select “Pyright” from the menu). Please include this verbose logging when reporting import resolution bugs.

View File

@ -1,18 +1,18 @@
# Differences Between Pyright and Mypy
## Differences Between Pyright and Mypy
## What is Mypy?
### What is Mypy?
Mypy is the “OG” in the world of Python type checkers. It was started by Jukka Lehtosalo in 2012 with contributions from Guido van Rossum, Ivan Levkivskyi, and many others over the years. For a detailed history, refer to [this documentation](http://mypy-lang.org/about.html). The code for mypy can be found in [this github project](https://github.com/python/mypy).
## Why Does Pyrights Behavior Differ from Mypys?
### Why Does Pyrights Behavior Differ from Mypys?
Mypy served as a reference implementation of [PEP 484](https://www.python.org/dev/peps/pep-0484/), which defines standard behaviors for Python static typing. Although PEP 484 spells out many type checking behaviors, it intentionally leaves many other behaviors undefined. This approach has allowed different type checkers to innovate and differentiate.
Pyright generally adheres to the type checking behaviors spelled out in PEP 484 and follow-on typing PEPs (526, 544, 586, 589, etc.). For behaviors that are not explicitly spelled out in these standards, pyright generally tries to adhere to mypys behavior unless there is a compelling justification for deviating. This document discusses these differences and provides the reasoning behind each design choice.
## Design Goals
### Design Goals
Pyright was designed with performance in mind. It is not unusual for pyright to be 3x to 5x faster than mypy when type checking large code bases. Some of its design decisions were motivated by this goal.
@ -23,21 +23,21 @@ To achieve these design goals, pyright is implemented as a “lazy” or “just
Pyright implements its own parser, which recovers gracefully from syntax errors and continues parsing the remainder of the source file. By comparison, mypy uses the parser built in to the Python interpreter, and it does not support recovery after a syntax error.
## Type Checking Unannotated Code
### Type Checking Unannotated Code
By default, pyright performs type checking for all code regardless of whether it contains type annotations. This is important for language server features. It is also important for catching bugs in code that is unannotated.
By default, mypy skips all functions or methods that do not have type annotations. This is a common source of confusion for mypy users who are surprised when type violations in unannotated functions go unreported. If the option `--check-untyped-defs` is enabled, mypy performs type checking for all functions and methods.
## Inferred Return Types
### Inferred Return Types
If a function or method lacks a return type annotation, pyright infers the return type from `return` and `yield` statements within the functions body (including the implied `return None` at the end of the function body). This is important for supporting completion suggestions. It also improves type checking coverage and eliminates the need for developers to needlessly supply return type annotations for trivial return types.
By comparison, mypy never infers return types and assumes that functions without a return type annotation have a return type of `Any`. This was an intentional design decision by mypy developers and is explained in [this thread](https://github.com/python/mypy/issues/10149).
## Unions vs Joins
### Unions vs Joins
When merging two types during code flow analysis or widening types during constraint solving, pyright always uses a union operation. Mypy typically (but not always) uses a “join” operation, which merges types by finding a common supertype. The use of joins discards valuable type information and leads to many false positive errors that are [well documented within the mypy issue tracker](https://github.com/python/mypy/issues?q=is%3Aissue+is%3Aopen+label%3Atopic-join-v-union).
@ -61,7 +61,7 @@ def func2(condition: bool, val1: str, val2: int):
```
## Variable Type Declarations
### Variable Type Declarations
Pyright treats variable type annotations as type declarations. If a variable is not annotated, pyright allows any value to be assigned to that variable, and its type is inferred to be the union of all assigned types.
@ -90,7 +90,7 @@ def func3(condition: bool):
Pyrights behavior is more consistent, is conceptually simpler and more natural for Python developers, leads to fewer false positives, and eliminates the need for many otherwise-necessary variable type annotations.
## Class and Instance Variable Inference
### Class and Instance Variable Inference
Pyright handles instance and class variables consistently with local variables. If a type annotation is provided for an instance or class variable (either within the class or one of its base classes), pyright treats this as a type declaration and enforces it accordingly. If a class implementation does not provide a type annotation for an instance or class variable and its base classes likewise do not provide a type annotation, the variables type is inferred from all assignments within the class implementation.
@ -110,7 +110,7 @@ a.x = 3.0 # Pyright treats this as an error because the type of `x` is `int | st
```
## Class and Instance Variable Enforcement
### Class and Instance Variable Enforcement
Pyright distinguishes between “pure class variables”, “regular class variables”, and “pure instance variable”. For a detailed explanation, refer to [this documentation](type-concepts.md#class-and-instance-variables).
@ -130,7 +130,7 @@ print(A.z) # pyright: error, mypy: no error
```
## Assignment-based Type Narrowing
### Assignment-based Type Narrowing
Pyright applies type narrowing for variable assignments. This is done regardless of whether the assignment statement includes a variable type annotation. Mypy skips assignment-based type narrowing when the target variable includes a type annotation. The consensus of the typing community is that mypys behavior here is inconsistent, and there are [plans to eliminate this inconsistency](https://github.com/python/mypy/issues/2008).
@ -144,7 +144,7 @@ reveal_type(v2) # mypy reveals `Sequence[int]` rather than `list[int]`
```
## Type Guards
### Type Guards
Pyright supports several built-in type guards that mypy does not currently support. For a full list of type guard expression forms supported by pyright, refer to [this documentation](type-concepts.md#type-guards).
@ -156,12 +156,12 @@ The following expression forms are not currently supported by mypy as type guard
* `bool(x)` (where x is any expression that is statically verifiable to be truthy or falsey in all cases)
## Aliased Conditional Expressions
### Aliased Conditional Expressions
Pyright supports the [aliasing of conditional expressions](type-concepts.md#aliased-conditional-expression) used for type guards. Mypy does not currently support this, but it is a frequently-requested feature.
## Narrowing for Implied Else
### Narrowing for Implied Else
Pyright supports a feature called [type narrowing for implied else](type-concepts.md#narrowing-for-implied-else) in cases where an `if` or `elif` clause has no associated `else` clause. This feature allows pyright to determine that all cases have already been handled by the `if` or `elif` statement and that the "implied else" would never be executed if it were present. This eliminates certain false positive errors. Mypy currently does not support this.
@ -178,7 +178,7 @@ def is_red(color: Color) -> bool:
# mypy reports error: Missing return statement
```
## Narrowing Any
### Narrowing Any
Pyright never narrows `Any` when performing type narrowing for assignments. Mypy is inconsistent about when it applies type narrowing to `Any` type arguments.
@ -194,7 +194,7 @@ reveal_type(b) # pyright: list[Any], mypy: list[int]
```
## Inference of List, Set, and Dict Expressions
### Inference of List, Set, and Dict Expressions
Pyrights inference rules for [list, set and dict expressions](type-inference.md#list-expressions) differ from mypys when values with heterogeneous types are used. Mypy uses a join operator to combine the types. Pyright uses either an `Unknown` or a union depending on configuration settings. A join operator often produces a type that is not what was intended, and this leads to false positive errors.
@ -215,7 +215,7 @@ def func(one: Literal[1]):
```
## Inference of Tuple Expressions
### Inference of Tuple Expressions
Pyrights inference rules for [tuple expressions](type-inference.md#tuple-expressions) differ from mypys when tuple entries contain literals. Pyright retains these literal types, but mypy widens the types to their non-literal type. Pyright retains the literal types in this case because tuples are immutable, and more precise (narrower) types are almost always beneficial in this situation.
@ -227,7 +227,7 @@ y: Literal["stop", "go"] = x[1] # mypy: type error
```
## Assignment-Based Narrowing for Literals
### Assignment-Based Narrowing for Literals
When assigning a literal value to a variable, pyright narrows the type to reflect the literal. Mypy does not. Pyright retains the literal types in this case because more precise (narrower) types are typically beneficial and have little or no downside.
@ -250,19 +250,19 @@ def func2():
```
## Type Narrowing for Asymmetric Descriptors
### Type Narrowing for Asymmetric Descriptors
When pyright evaluates a write to a class variable that contains a descriptor object (including properties), it normally applies assignment-based type narrowing. However, when the descriptor is asymmetric — that is, its “getter” type is different from its “setter” type, pyright refrains from applying assignment-based type narrowing. For a full discussion of this, refer to [this issue](https://github.com/python/mypy/issues/3004). Mypy has not yet implemented the agreed-upon behavior, so its type narrowing behavior may differ from pyrights in this case.
## Parameter Type Inference
### Parameter Type Inference
Mypy infers the type of `self` and `cls` parameters in methods but otherwise does not infer any parameter types.
Pyright implements several parameter type inference techniques that improve type checking and language service features in the absence of explicit parameter type annotations. For details, refer to [this documentation](type-inference.md#parameter-type-inference).
## Constraint Solver Behaviors
### Constraint Solver Behaviors
When evaluating a call expression that invokes a generic class constructor or a generic function, a type checker performs a process called “constraint solving” to solve the type variables found within the target function signature. The solved type variables are then applied to the return type of that function to determine the final type of the call expression. This process is called “constraint solving” because it takes into account various constraints that are specified for each type variable. These constraints include variance rules and type variable bounds.
@ -322,7 +322,7 @@ def func2(x: list[int], y: list[str] | int):
```
## Constrained Type Variables
### Constrained Type Variables
When mypy analyzes a class or function that has in-scope constrained TypeVars, it analyzes the class or function multiple times, once for each constraint. This can produce multiple errors.
@ -337,7 +337,7 @@ def func(a: AnyStr, b: T):
Pyright cannot use the same multi-pass technique as mypy in this case. It needs to produce a single type for any given identifier to support language server features. Pyright instead uses a mechanism called [conditional types](type-concepts.md#constrained-type-variables-and-conditional-types). This approach allows pyright to handle some constrained TypeVar use cases that mypy cannot, but there are conversely other use cases that mypy can handle and pyright cannot.
## “Unknown” Type and Strict Mode
### “Unknown” Type and Strict Mode
Pyright differentiates between explicit and implicit forms of `Any`. The implicit form is referred to as [`Unknown`](type-inference.md#unknown-type). For example, if a parameter is annotated as `list[Any]`, that is a use of an explicit `Any`, but if a parameter is annotated as `list`, that is an implicit `Any`, so pyright refers to this type as `list[Unknown]`. Pyright implements several checks that are enabled in “strict” type-checking modes that report the use of an `Unknown` type. Such uses can mask type errors.
@ -346,12 +346,12 @@ Mypy does not track the difference between explicit and implicit `Any` types, bu
Pyrights approach gives developers more control. It provides a way to be explicit about `Any` where that is the intent. When an `Any` is implicitly produced due to an missing type argument or some other condition that produces an `Any` within the type checker logic, the developer is alerted to that condition.
## Overload Resolution
### Overload Resolution
Overload resolution rules are under-specified in PEP 484. Pyright and mypy apply similar rules, but there are likely some complex edge cases where different results will be produced. For full documentation of pyrights overload behaviors, refer to [this documentation](type-concepts.md#overloads).
## Import Statements
### Import Statements
Pyright intentionally does not model implicit side effects of the Python import loading mechanism. In general, such side effects cannot be modeled statically because they depend on execution order. Dependency on such side effects leads to fragile code, so pyright treats these as errors. For more details, refer to [this documentation](import-statements.md).
@ -362,14 +362,14 @@ import collections.abc
collections.deque() # Pyright produces an error here because the `collections` module wasn't explicitly imported
```
## Ellipsis in Function Body
### Ellipsis in Function Body
If Pyright encounters a function body whose implementation is `...`, it does not enforce the return type annotation. The `...` semantically means “this is a code placeholder” — a convention established in type stubs, protocol definitions, and elsewhere.
Mypy treats `...` function bodies as though they are executable and enforces the return type annotation. This was a recent change in mypy — made long after Pyright established a different behavior. Prior to mypys recent change, it did not enforce return types for function bodies consisting of either `...` or `pass`. Now it enforces both.
## Circular References
### Circular References
Because mypy is a multi-pass analyzer, it is able to deal with certain forms of circular references that pyright cannot handle. Here are several examples of circularities that mypy resolves without errors but pyright does not.
@ -398,12 +398,12 @@ def my_decorator(x: Callable[..., "A"]) -> Callable[..., "A"]:
class A: ...
```
## Class Decorator Evaluation
### Class Decorator Evaluation
Pyright honors class decorators. Mypy largely ignores them. See [this issue](https://github.com/python/mypy/issues/3135) for details.
## Support for Type Comments
### Support for Type Comments
Versions of Python prior to 3.0 did not have a dedicated syntax for supplying type annotations. Annotations therefore needed to be supplied using “type comments” of the form `# type: <annotation>`. Python 3.6 added the ability to supply type annotations for variables.

View File

@ -1,4 +1,4 @@
# Pyright Settings
## Pyright Settings
The Pyright language server honors the following settings.

View File

@ -1,6 +1,6 @@
# Understanding Type Inference
## Understanding Type Inference
## Symbols and Scopes
### Symbols and Scopes
In Python, a _symbol_ is any name that is not a keyword. Symbols can represent classes, functions, methods, variables, parameters, modules, type aliases, type variables, etc.
@ -13,7 +13,7 @@ The following constructs within Python define a scope:
4. Each function and lambda defines its own scope. The functions parameters are symbols within its scope, as are any variables defined within the function.
5. List comprehensions define their own scope.
## Type Declarations
### Type Declarations
A symbol can be declared with an explicit type. The “def” and “class” keywords, for example, declare a symbol as a function or a class. Other symbols in Python can be introduced into a scope with no declared type. Newer versions of Python have introduced syntax for declaring the types of input parameters, return parameters, and variables.
@ -43,17 +43,17 @@ var3 | Variable | func1 | <none>
Note that once a symbols type is declared, it cannot be redeclared to a different type.
## Type Inference
### Type Inference
Some languages require every symbol to be explicitly typed. Python allows a symbol to be bound to different values at runtime, so its type can change over time. A symbols type doesnt need to be declared statically.
When Pyright encounters a symbol with no type declaration, it attempts to _infer_ the type based on the values assigned to it. As we will see below, type inference cannot always determine the correct (intended) type, so type annotations are still required in some cases. Furthermore, type inference can require significant computation, so it is much less efficient than when type annotations are provided.
## “Unknown” Type
### “Unknown” Type
If a symbols type cannot be inferred, Pyright sets its type to “Unknown”, which is a special form of “Any”. The “Unknown” type allows Pyright to optionally warn when types are not declared and cannot be inferred, thus leaving potential “blind spots” in type checking.
### Single-Assignment Type Inference
#### Single-Assignment Type Inference
The simplest form of type inference is one that involves a single assignment to a symbol. The inferred type comes from the type of the source expression. Examples include:
@ -66,7 +66,7 @@ for var5 in [3, 4]: ... # Inferred type is int
var6 = [p for p in [1, 2, 3]] # Inferred type is list[int]
```
### Multi-Assignment Type Inference
#### Multi-Assignment Type Inference
When a symbol is assigned values in multiple places within the code, those values may have different types. The inferred type of the variable is the union of all such types.
@ -86,11 +86,11 @@ else:
var2 = Foo()
```
### Ambiguous Type Inference
#### Ambiguous Type Inference
In some cases, an expressions type is ambiguous. For example, what is the type of the expression `[]`? Is it `list[None]`, `list[int]`, `list[Any]`, `Sequence[Any]`, `Iterable[Any]`? These ambiguities can lead to unintended type violations. Pyright uses several techniques for reducing these ambiguities based on contextual information. In the absence of contextual information, heuristics are used.
### Bidirectional Type Inference (Expected Types)
#### Bidirectional Type Inference (Expected Types)
One powerful technique Pyright uses to eliminate type inference ambiguities is _bidirectional inference_. This technique makes use of an “expected type”.
@ -109,7 +109,7 @@ var5 = (3,) # Type is assumed to be tuple[Literal[3]]
var6: tuple[float, ...] = (3,) # Type of RHS is now tuple[float, ...]
```
### Empty list and Dictionary Type Inference
#### Empty list and Dictionary Type Inference
It is common to initialize a local variable or instance variable to an empty list (`[]`) or empty dictionary (`{}`) on one code path but initialize it to a non-empty list or dictionary on other code paths. In such cases, Pyright will infer the type based on the non-empty list or dictionary and suppress errors about a “partially unknown type”.
@ -123,7 +123,7 @@ reveal_type(my_list) # list[str]
```
### Return Type Inference
#### Return Type Inference
As with variable assignments, function return types can be inferred from the `return` statements found within that function. The returned type is assumed to be the union of all types returned from all `return` statements. If a `return` statement is not followed by an expression, it is assumed to return `None`. Likewise, if the function does not end in a `return` statement, and the end of the function is reachable, an implicit `return None` is assumed.
@ -140,7 +140,7 @@ def func1(val: int):
return True
```
### NoReturn return type
#### NoReturn return type
If there is no code path that returns from a function (e.g. all code paths raise an exception), Pyright infers a return type of `NoReturn`. As an exception to this rule, if the function is decorated with `@abstractmethod`, the return type is not inferred as `NoReturn` even if there is no return. This accommodates a common practice where an abstract method is implemented with a `raise NotImplementedError()` statement.
@ -156,11 +156,11 @@ class Foo:
raise NotImplementedError()
```
### Generator return types
#### Generator return types
Pyright can infer the return type for a generator function from the `yield` statements contained within that function.
### Call-site Return Type Inference
#### Call-site Return Type Inference
It is common for input parameters to be unannotated. This can make it difficult for Pyright to infer the correct return type for a function. For example:
@ -191,7 +191,7 @@ def func2(p_int: int, p_str: str, p_flt: float):
var2 = func1(p_str, p_flt, p_int)
```
### Parameter Type Inference
#### Parameter Type Inference
Input parameters for functions and methods typically require type annotations. There are several cases where Pyright may be able to infer a parameters type if it is unannotated.
@ -225,7 +225,7 @@ def func(a, b=0, c=None):
reveal_type(func) # (a: Unknown, b: int, c: Unknown | None) -> None
```
### Literals
#### Literals
Python 3.8 introduced support for _literal types_. This allows a type checker like Pyright to track specific literal values of str, bytes, int, bool, and enum values. As with other types, literal types can be declared.
@ -249,7 +249,7 @@ When Pyright is performing type inference, it generally does not infer literal t
var1 = [4]
```
### Tuple Expressions
#### Tuple Expressions
When inferring the type of a tuple expression (in the absence of bidirectional inference hints), Pyright assumes that the tuple has a fixed length, and each tuple element is typed as specifically as possible.
@ -267,7 +267,7 @@ def func1(a: int):
var3: tuple[int, ...] = (a, a)
```
### List Expressions
#### List Expressions
When inferring the type of a list expression (in the absence of bidirectional inference hints), Pyright uses the following heuristics:
@ -293,7 +293,7 @@ var4: list[float] = [1, 3.4] # Infer list[float]
```
### Set Expressions
#### Set Expressions
When inferring the type of a set expression (in the absence of bidirectional inference hints), Pyright uses the following heuristics:
@ -316,7 +316,7 @@ var3: set[float] = {1, 3.4} # Infer set[float]
```
### Dictionary Expressions
#### Dictionary Expressions
When inferring the type of a dictionary expression (in the absence of bidirectional inference hints), Pyright uses the following heuristics:
@ -340,7 +340,7 @@ var3 = {"a": 3, "b": 3.4} # Infer dict[str, int | float] (on)
var4: dict[str, float] = {"a": 3, "b": 3.4}
```
### Lambdas
#### Lambdas
Lambdas present a particular challenge for a Python type checker because there is no provision in the Python syntax for annotating the types of a lambdas input parameters. The types of these parameters must therefore be inferred based on context using bidirectional type inference. Absent this context, a lambdas input parameters (and often its return type) will be unknown.

View File

@ -1,8 +1,8 @@
# Type Stub Files
## Type Stub Files
Type stubs are “.pyi” files that specify the public interface for a library. They use a variant of the Python syntax that allows for “...” to be used in place of any implementation details. Type stubs define the public contract for the library.
## Importance of Type Stub Files
### Importance of Type Stub Files
Regardless of the search path, Pyright always attempts to resolve an import with a type stub (“.pyi”) file before falling back to a python source (“.py”) file. If a type stub cannot be located for an external import, Pyright will try to use inline type information if the module is part of a package that contains a “py.typed” file (defined in [PEP 561](https://www.python.org/dev/peps/pep-0561/)). If the module is part of a package that doesnt contain a “py.typed” file, Pyright will treat all of the symbols imported from these modules will be of type “Unknown”, and wildcard imports (of the form `from foo import *`) will not populate the modules namespace with specific symbol names.
@ -16,12 +16,12 @@ Why does Pyright not attempt (by default) to determine types from imported pytho
If youre serious about static type checking for your Python source base, its highly recommended that you consume “py.typed” packages or use type stub files for all external imports. If you are unable to find a type stub for a particular library, the recommended approach is to create a custom type stub file that defines the portion of that modules interface used by your code. More library maintainers have started to provide inlined types or type stub files.
## Generating Type Stubs
### Generating Type Stubs
If you use only a few classes, methods or functions within a library, writing a type stub file by hand is feasible. For large libraries, this can become tedious and error-prone. Pyright can generate “draft” versions of type stub files for you.
To generate a type stub file from within VS Code, enable the reportMissingTypeStubs” setting in your pyrightconfig.json file or by adding a comment `# pyright: reportMissingTypeStubs=true` to individual source files. Make sure you have the target library installed in the python environment that pyright is configured to use for import resolution. Optionally specify a “stubPath” in your pyrightconfig.json file. This is where pyright will generate your type stub files. By default, the stubPath is set to "./typings".
### Generating Type Stubs in VS Code
#### Generating Type Stubs in VS Code
If “reportMissingTypeStubs” is enabled, pyright will highlight any imports that have no type stub. Hover over the error message, and you will see a “Quick Fix” link. Clicking on this link will reveal a popup menu item titled “Create Type Stub For XXX”. The example below shows a missing typestub for the `django` library.
![Pyright](img/CreateTypeStub1.png)
@ -30,13 +30,13 @@ Click on the menu item to create the type stub. Depending on the size of the lib
![Pyright](img/CreateTypeStub2.png)
### Generating Type Stubs from Command Line
#### Generating Type Stubs from Command Line
The command-line version of pyright can also be used to generate type stubs. As with the VS Code version, it must be run within the context of your configured project. Then type `pyright --createstub [import-name]`.
For example:
`pyright --createstub django`
### Cleaning Up Generated Type Stubs
#### Cleaning Up Generated Type Stubs
Pyright can give you a head start by creating type stubs, but you will typically need to clean up the first draft, fixing various errors and omissions that pyright was not able to infer from the original library code.
A few common situations that need to be cleaned up:

View File

@ -1,4 +1,4 @@
# Typing Guidance for Python Libraries
## Typing Guidance for Python Libraries
Much of Pythons popularity can be attributed to the rich collection of Python libraries available to developers. Authors of these libraries play an important role in improving the experience for Python developers. This document provides some recommendations and guidance for Python library authors.
@ -10,7 +10,7 @@ These recommendations are intended to provide the following benefits:
4. Library authors should have the benefits of static type checking to produce high-quality, bug-free implementations.
## Inlined Type Annotations and Type Stubs
### Inlined Type Annotations and Type Stubs
[PEP 561](https://www.python.org/dev/peps/pep-0561/) documents several ways type information can be delivered for a library: inlined type annotations, type stub files included in the package, a separate companion type stub package, and type stubs in the typeshed repository. Some of these options fall short on delivering the benefits above. We therefore provide the following more specific guidance to library authors.
All libraries should include inlined type annotations for the functions, classes, methods, and constants that comprise the public interface for the library.
@ -22,7 +22,7 @@ There are cases where inlined type annotations are not possible — most notably
In many existing type stubs (such as those found in typeshed), default parameter values are replaced with with “...” and all docstrings are removed. We recommend that default values and docstrings remain within the type stub file so language servers can display this information to developers.
## Library Interface
### Library Interface
[PEP 561](https://www.python.org/dev/peps/pep-0561/) indicates that a “py.typed” marker file must be included in the package if the author wishes to support type checking of their code.
If a “py.typed” module is present, a type checker will treat all modules within that package (i.e. all files that end in “.py” or “.pyi”) as importable unless the module is marked private. There are two ways to mark a module private: (1) the module's filename begins with an underscore; (2) the module is inside a sub-package marked private. For example:
@ -50,7 +50,7 @@ The following idioms are supported for defining the values contained within `__a
* `__all__.remove('a')`
## Type Completeness
### Type Completeness
A “py.typed” library is said to be “type complete” if all of the symbols that comprise its interface have type annotations that refer to types that are fully known. Private symbols are exempt.
A “known type” is defined as follows:
@ -86,14 +86,14 @@ Type annotations can be omitted in a few specific cases where the type is obviou
* A variable is assigned in only one location using a simple assignment expression and the right-hand side of the assignment is a literal value (e.g. `1`, `3.14`, `"hi"`, or `MyEnum.Value`) or an identifier that has a known type that doesn't depend on type narrowing logic.
### Ambiguous Types
#### Ambiguous Types
When a symbol is missing a type annotation, a type checker may be able to infer its type based on contextual information. However, type inference rules are not standardized and differ between type checkers. A symbol is said to have an “ambiguous type” if its type may be inferred differently between different Python type checkers. This can lead to a bad experience for consumers of the library.
Ambiguous types can be avoided by providing explicit type annotations.
### Examples of known, ambiguous and unknown types
#### Examples of known, ambiguous and unknown types
```python
# Variable with known type (unambiguous because it uses a literal assignment)
@ -206,7 +206,7 @@ class DictSubclass(dict):
```
## Verifying Type Completeness
### Verifying Type Completeness
Pyright provides a feature that allows library authors to verify type completeness for a “py.typed” package. To use this feature, create a clean Python environment and install your package along with all of the other dependent packages. Run the CLI version of pyright with the `--verifytypes` option.
`pyright --verifytypes <lib>`
@ -222,7 +222,7 @@ The `--verifytypes` feature can be integrated into a continuous integration (CI)
If the `--verifytypes` option is combined with `--ignoreexternal`, any incomplete types that are imported from other external packages are ignored. This allows library authors to focus on adding type annotations for the code that is directly under their control.
### Improving Type Completeness
#### Improving Type Completeness
Here are some tips for increasing the type completeness score for your library:
@ -232,19 +232,19 @@ Here are some tips for increasing the type completeness score for your library:
* If your package exposes types from other libraries, work with the maintainers of these other libraries to achieve type completeness.
## Best Practices for Inlined Types
### Best Practices for Inlined Types
### Wide vs. Narrow Types
#### Wide vs. Narrow Types
In type theory, when comparing two types that are related to each other, the “wider” type is the one that is more general, and the “narrower” type is more specific. For example, `Sequence[str]` is a wider type than `list[str]` because all `list` objects are also `Sequence` objects, but the converse is not true. A subclass is narrower than a class it derives from. A union of types is wider than the individual types that comprise the union.
In general, a function input parameter should be annotated with the widest possible type supported by the implementation. For example, if the implementation requires the caller to provide an iterable collection of strings, the parameter should be annotated as `Iterable[str]`, not as `list[str]`. The latter type is narrower than necessary, so if a user attempts to pass a tuple of strings (which is supported by the implementation), a type checker will complain about a type incompatibility.
As a specific application of the “use the widest type possible” rule, libraries should generally use immutable forms of container types instead of mutable forms (unless the function needs to modify the container). Use `Sequence` rather than `list`, `Mapping` rather than `dict`, etc. Immutable containers allow for more flexibility because their type parameters are covariant rather than invariant. A parameter that is typed as `Sequence[str | int]` can accept a `list[str | int]`, `list[int]`, `Sequence[str]`, and a `Sequence[int]`. But a parameter typed as `list[str | int]` is much more restrictive and accepts only a `list[str | int]`.
### Overloads
#### Overloads
If a function or method can return multiple different types and those types can be determined based on the presence or types of certain parameters, use the `@overload` mechanism defined in [PEP 484](https://www.python.org/dev/peps/pep-0484/#id45). When overloads are used within a “.py” file, they must appear prior to the function implementation, which should not have an `@overload` decorator.
### Keyword-only Parameters
#### Keyword-only Parameters
If a function or method is intended to take parameters that are specified only by name, use the keyword-only separator ("*").
```python
@ -252,7 +252,7 @@ def create_user(age: int, *, dob: date | None = None):
...
```
### Positional-only Parameters
#### Positional-only Parameters
If a function or method is intended to take parameters that are specified only by position, use the positional-only separator ("/") as documented in [PEP 570](https://peps.python.org/pep-0570/). If your library needs to run on versions of Python prior to 3.8, you can alternatively name the positional-only parameters with an identifier that begins with a double underscore.
```python
@ -288,10 +288,10 @@ def complex_decorator(*, mode: str) -> Callable[[_F], _F]:
Decorators that mutate the signature of the decorated function present challenges for type annotations. The `ParamSpec` and `Concatenate` mechanisms described in [PEP 612](https://www.python.org/dev/peps/pep-0612/) provide some help here, but these are available only in Python 3.10 and newer. More complex signature mutations may require type annotations that erase the original signature, thus blinding type checkers and other tools that provide signature assistance. As such, library authors are discouraged from creating decorators that mutate function signatures in this manner.
### Generic Classes and Functions
#### Generic Classes and Functions
Classes and functions that can operate in a generic manner on various types should declare themselves as generic using the mechanisms described in [PEP 484](https://www.python.org/dev/peps/pep-0484/). This includes the use of `TypeVar` symbols. Typically, a `TypeVar` should be private to the file that declares it, and should therefore begin with an underscore.
### Type Aliases
#### Type Aliases
Type aliases are symbols that refer to other types. Generic type aliases (those that refer to unspecialized generic classes) are supported by most type checkers. Pyright also provides support for recursive type aliases.
[PEP 613](https://www.python.org/dev/peps/pep-0613/) provides a way to explicitly designate a symbol as a type alias using the new TypeAlias annotation.
@ -310,7 +310,7 @@ TreeNode = LeafNode | list["TreeNode"]
StrOrInt: TypeAlias = str | int
```
### Abstract Classes and Methods
#### Abstract Classes and Methods
Classes that must be subclassed should derive from `ABC`, and methods or properties that must be overridden should be decorated with the `@abstractmethod` decorator. This allows type checkers to validate that the required methods have been overridden and provide developers with useful error messages when they are not. It is customary to implement an abstract method by raising a `NotImplementedError` exception.
```python
@ -329,13 +329,13 @@ class Hashable(ABC):
raise NotImplementedError()
```
### Final Classes and Methods
#### Final Classes and Methods
Classes that are not intended to be subclassed should be decorated as `@final` as described in [PEP 591](https://www.python.org/dev/peps/pep-0591/). The same decorator can also be used to specify methods that cannot be overridden by subclasses.
### Literals
#### Literals
Type annotations should make use of the Literal type where appropriate, as described in [PEP 586](https://www.python.org/dev/peps/pep-0586/). Literals allow for more type specificity than their non-literal counterparts.
### Constants
#### Constants
Constant values (those that are read-only) can be specified using the Final annotation as described in [PEP 591](https://www.python.org/dev/peps/pep-0591/).
Type checkers will also typically treat variables that are named using all upper-case characters as constants.
@ -358,7 +358,7 @@ ColorFormatRgb: Final[Literal["rgb"]] = "rgb"
LATEST_VERSION: Final[tuple[int, int]] = (4, 5)
```
### Typed Dictionaries, Data Classes, and Named Tuples
#### Typed Dictionaries, Data Classes, and Named Tuples
If a library runs only on newer versions of Python, it can use some of the new type-friendly classes.
NamedTuple (described in [PEP 484](https://www.python.org/dev/peps/pep-0484/)) is preferred over namedtuple.
@ -368,10 +368,10 @@ Data classes (described in [PEP 557](https://www.python.org/dev/peps/pep-0557/))
TypedDict (described in [PEP 589](https://www.python.org/dev/peps/pep-0589/)) is preferred over untyped dictionaries.
## Compatibility with Older Python Versions
### Compatibility with Older Python Versions
Each new version of Python from 3.5 onward has introduced new typing constructs. This presents a challenge for library authors who want to maintain runtime compatibility with older versions of Python. This section documents several techniques that can be used to add types while maintaining backward compatibility.
### Quoted Annotations
#### Quoted Annotations
Type annotations for variables, parameters, and return types can be placed in quotes. The Python interpreter will then ignore them, whereas a type checker will interpret them as type annotations.
```python
@ -410,20 +410,20 @@ class Foo:
...
```
### typing_extensions
#### typing_extensions
New type features that require runtime support are typically included in the stdlib `typing` module. Where possible, these new features are back-ported to a runtime library called `typing_extensions` that works with older Python runtimes.
### TYPE_CHECKING
#### TYPE_CHECKING
The `typing` module exposes a variable called `TYPE_CHECKING` which has a value of False within the Python runtime but a value of True when the type checker is performing its analysis. This allows type checking statements to be conditionalized.
Care should be taken when using `TYPE_CHECKING` because behavioral changes between type checking and runtime could mask problems that the type checker would otherwise catch.
## Non-Standard Type Behaviors
### Non-Standard Type Behaviors
Type annotations provide a way to annotate typical type behaviors, but some classes implement specialized, non-standard behaviors that cannot be described using standard type annotations. For now, such types need to be annotated as Any, which is unfortunate because the benefits of static typing are lost.
## Docstrings
### Docstrings
It is recommended that docstrings be provided for all classes, functions, and methods in the interface. They should be formatted according to [PEP 257](https://www.python.org/dev/peps/pep-0257/).
There is currently no single agreed-upon standard for function and method docstrings, but several common variants have emerged. We recommend using one of these variants.