初始化上传
Some checks failed
Run mypy_primer on push / Run mypy_primer on push (push) Has been cancelled
Validation / Typecheck (push) Has been cancelled
Validation / Style (push) Has been cancelled
Validation / Test macos-latest (push) Has been cancelled
Validation / Test ubuntu-latest (push) Has been cancelled
Validation / Test windows-latest (push) Has been cancelled
Validation / Build (push) Has been cancelled
Validation / Required (push) Has been cancelled

This commit is contained in:
2026-07-24 17:08:39 +08:00
commit e9e4693333
7516 changed files with 768821 additions and 0 deletions

0
docs/.nojekyll Normal file
View File

9
docs/README.md Normal file
View File

@@ -0,0 +1,9 @@
![Pyright](img/PyrightLarge.png)
# Static type checker for Python
Pyright is a full-featured, [standards-compliant](https://htmlpreview.github.io/?https://github.com/python/typing/blob/main/conformance/results/results.html) static type checker for Python. It is designed for high performance and can be used with large Python source bases.
Pyright includes a [command-line tool](command-line.md), a language server, and an [extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=ms-pyright.pyright).

1
docs/_navbar.md Normal file
View File

@@ -0,0 +1 @@
[GitHub Site](https://github.com/Microsoft/pyright)

41
docs/_sidebar.md Normal file
View File

@@ -0,0 +1,41 @@
- Getting Started
- [Installing Pyright](installation.md)
- [Getting Started](getting-started.md)
- [Static Typing](type-concepts.md)
- [Features](features.md)
- Customization
- [Configuration](configuration.md)
- [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)
- [Sample pyproject.toml](configuration.md#sample-pyprojecttoml-file)
- [Diagnostic Settings Defaults](configuration.md#diagnostic-settings-defaults)
- [Locale Configuration](configuration.md#locale-configuration)
- [Language Server Settings](settings.md)
- [Command Line Interface](command-line.md)
- [Type Server (TSP)](type-server.md)
- [Controlling Behavior With Comments](comments.md)
- [Continuous Integration](ci-integration.md)
- Usage
- [Advanced Type Concepts](type-concepts-advanced.md)
- [Type Inference](type-inference.md)
- [Import Statements](import-statements.md)
- [Import Resolution](import-resolution.md)
- [Extending Builtins](builtins.md)
- [Type Stubs](type-stubs.md)
- [Types in Libraries](typed-libraries.md)
- [Differences from Mypy](mypy-comparison.md)
- [Commands](commands.md)
- Development
- [Building & Debugging](build-debug.md)
- [Pyright Internals](internals.md)

39
docs/build-debug.md Normal file
View File

@@ -0,0 +1,39 @@
## Building Pyright
To install the dependencies for all packages in the repo:
1. Install [nodejs](https://nodejs.org/en/) version 16.x
2. Open terminal window in main directory of cloned source
3. Execute `npm run install:all` to install dependencies for projects and sub-projects
## Building the CLI
1. cd to the `packages/pyright` directory
2. Execute `npm run build`
Once built, you can run the command-line tool by executing the following:
`node index.js`
## Building the VS Code extension
1. cd to the `packages/vscode-pyright` directory
2. Execute `npm run package`
The resulting package (pyright-X.Y.Z.vsix) can be found in the client directory.
To install in VS Code, go to the extensions panel and choose “Install from VSIX...” from the menu, then select the package.
## Running Pyright tests
1. cd to the `packages/pyright-internal` directory
2. Execute `npm run test`
## Debugging Pyright
To debug pyright, open the root source directory within VS Code. Open the debug sub-panel and choose “Pyright CLI” from the debug target menu. Click on the green “run” icon or press F5 to build and launch the command-line version in the VS Code debugger. There's also a similar option that provides a slightly faster build/debug loop: make sure you've built the pyright-internal project e.g. with Terminal > Run Build Task > tsc: watch, then choose “Pyright CLI (pyright-internal)”.
To debug the VS Code extension, select “Pyright extension” from the debug target menu. Click on the green “run” icon or press F5 to build and launch a second copy of VS Code with the extension. Within the second VS Code instance, open a python source file so the pyright extension is loaded. Return to the first instance of VS Code and select “Pyright extension attach server” from the debug target menu and click the green “run” icon. This will attach the debugger to the process that hosts the type checker. You can now set breakpoints, etc.
To debug the VS Code extension in watch mode, you can do the above, but select “Pyright extension (watch mode)”. When pyright's source is saved, an incremental build will occur, and you can either reload the second VS Code window or relaunch it to start using the updated code. Note that the watcher stays open when debugging stops, so you may need to stop it (or close VS Code) if you want to perform packaging steps without the output potentially being overwritten.

8
docs/builtins.md Normal file
View File

@@ -0,0 +1,8 @@
## 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”.
Pyright gains knowledge of which types are included in “builtins” scope through the type stub file `builtins.pyi`. This stub file comes from the typeshed github repo and is bundled with pyright, along with type stubs that describe other stdlib modules.
Some Python environments are customized to include additional builtins symbols. If you are using such an environment, you may want to tell Pyright about these additional symbols that are available at runtime. To do so, you can add a local type stub file called `__builtins__.pyi`. This file can be placed at the root of your project directory or at the root of the subdirectory specified in the `stubPath` setting (which is named `typings` by default).

129
docs/ci-integration.md Normal file
View File

@@ -0,0 +1,129 @@
## Integrating Pyright into Continuous Integration
### Adding Pyright badge to README.md
[![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/)
To add a “pyright: checked” SVG badge to your projects README.md file, use the following:
```text
[![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/)
```
### Running Pyright as a github action
You can configure pyright to run as a github action.
```yml
- uses: jakebailey/pyright-action@v1
with:
version: 1.1.xxx # Optional (if you want to pin the version)
```
Refer to the [pyright-action project](https://github.com/jakebailey/pyright-action) for more options.
### Running Pyright in gitlab (with code-quality review)
You can configure pyright to run in gitlab, and generate a compatible codequality report.
```yml
job_name:
before_script:
- npm i -g pyright
- npm i -g pyright-to-gitlab-ci
script:
- pyright <python source> --outputjson > report_raw.json
after_script:
- pyright-to-gitlab-ci --src report_raw.json --output report.json --base_path .
artifacts:
paths:
- report.json
reports:
codequality: report.json
```
Refer to the [pyright-to-gitlab-ci](https://www.npmjs.com/package/pyright-to-gitlab-ci) package for more details.
### Running Pyright as a pre-commit hook
You can run pyright as a pre-commit hook using the community-maintained [Python wrapper for pyright](https://github.com/RobertCraigie/pyright-python). For pre-commit configuration instructions, refer to [this documentation](https://github.com/RobertCraigie/pyright-python#pre-commit).
### Running Pyright from a CI script
You can run pyright from a bash script. Here's a sample script that installs the latest version of pyright and runs it.
```bash
#!/bin/bash
PATH_TO_PYRIGHT=`which pyright`
vercomp () {
if [[ $1 == $2 ]]
then
return 0
fi
local IFS=.
local i ver1=($1) ver2=($2)
# fill empty fields in ver1 with zeros
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
do
ver1[i]=0
done
for ((i=0; i<${#ver1[@]}; i++))
do
if [[ -z ${ver2[i]} ]]
then
# fill empty fields in ver2 with zeros
ver2[i]=0
fi
if ((10#${ver1[i]} > 10#${ver2[i]}))
then
return 1
fi
if ((10#${ver1[i]} < 10#${ver2[i]}))
then
return 2
fi
done
return 0
}
# Node version check
echo "Checking node version..."
NODE_VERSION=`node -v | cut -d'v' -f2`
MIN_NODE_VERSION="14.21.3"
vercomp $MIN_NODE_VERSION $NODE_VERSION
# 1 == gt
if [[ $? -eq 1 ]]; then
echo "Node version ${NODE_VERSION} too old, min expected is ${MIN_NODE_VERSION}, run:"
echo " npm -g upgrade node"
exit -1
fi
# Do we need to sudo?
echo "Checking node_modules dir..."
NODE_MODULES=`npm -g root`
SUDO="sudo"
if [ -w "$NODE_MODULES" ]; then
SUDO="" #nop
fi
# If we can't find pyright, install it.
echo "Checking pyright exists..."
if [ -z "$PATH_TO_PYRIGHT" ]; then
echo "...installing pyright"
${SUDO} npm install -g pyright
else
# already installed, upgrade to make sure it's current
# this avoids a sudo on launch if we're already current
echo "Checking pyright version..."
CURRENT=`pyright --version | cut -d' ' -f2`
REMOTE=`npm info pyright version`
if [ "$CURRENT" != "$REMOTE" ]; then
echo "...new version of pyright found, upgrading."
${SUDO} npm upgrade -g pyright
fi
fi
echo "done."
pyright -w
```

96
docs/command-line.md Normal file
View File

@@ -0,0 +1,96 @@
# Pyright Command-Line Options
Usage: pyright [options] [files...] (1)
Pyright can be run as either a VS Code extension or as a node-based command-line tool. The command-line version allows for the following options:
| Flag | Description |
| :--------------------------------- | :--------------------------------------------------- |
| --createstub `<IMPORT>` | Create type stub file(s) for import |
| --dependencies | Emit import dependency information |
| -h, --help | Show help message |
| --ignoreexternal | Ignore external imports for --verifytypes |
| --level <LEVEL> | Minimum diagnostic level (error or warning) |
| --outputjson | Output results in JSON format |
| -p, --project `<FILE OR DIRECTORY>` | Use the configuration file at this location |
| --pythonpath `<FILE>` | Path to the Python interpreter (2) |
| --pythonplatform `<PLATFORM>` | Analyze for platform (Darwin, Linux, Windows, iOS, Android) |
| --pythonversion `<VERSION>` | Analyze for version (3.3, 3.4, etc.) |
| --skipunannotated | Skip type analysis of unannotated functions |
| --stats | Print detailed performance stats |
| -t, --typeshedpath `<DIRECTORY>` | Use typeshed type stubs at this location (3) |
| --threads <optional N> | Use up to N threads to parallelize type checking (4) |
| -v, --venvpath `<DIRECTORY>` | Directory that contains virtual environments (5) |
| --verbose | Emit verbose diagnostics |
| --verifytypes `<IMPORT>` | Verify completeness of types in py.typed package |
| --version | Print pyright version and exit |
| --warnings | Use exit code of 1 if warnings are reported |
| -w, --watch | Continue to run and watch for changes (6) |
| - | Read file or directory list from stdin |
(1) If specific files are specified on the command line, it overrides the files or directories specified in the pyrightconfig.json or pyproject.toml file.
(2) This option is the same as the language server setting `python.pythonPath`. It cannot be used with --venvpath. The --pythonpath options is recommended over --venvpath in most cases. For more details, refer to the [import resolution](import-resolution.md#configuring-your-python-environment) documentation.
(3) Pyright has built-in typeshed type stubs for Python stdlib functionality. To use a different version of typeshed type stubs, specify the directory with this option.
(4) This feature is experimental. If thread count is > 1, multiple copies of pyright are executed in parallel to type check files in a project. If no thread count is specified, the thread count is based on the number of available logical processors (if at least 4) or 1 (if less than 4).
(5) This option is the same as the language server setting `python.venvPath`. It used in conjunction with configuration file, which can refer to different virtual environments by name. For more details, refer to the [configuration](configuration.md) and [import resolution](import-resolution.md#configuring-your-python-environment) documentation. This allows a common config file to be checked in to the project and shared by everyone on the development team without making assumptions about the local paths to the venv directory on each developers computer.
(6) When running in watch mode, pyright will reanalyze only those files that have been modified. These “deltas” are typically much faster than the initial analysis, which needs to analyze all files in the source tree.
# Pyright Exit Codes
| Exit Code | Meaning |
| :---------- | :--------------------------------------------------------------- |
| 0 | No errors reported |
| 1 | One or more errors reported |
| 2 | Fatal error occurred with no errors or warnings reported |
| 3 | Config file could not be read or parsed |
| 4 | Illegal command-line parameters specified |
# JSON Output
If the “--outputjson” option is specified on the command line, diagnostics are output in JSON format. The JSON structure is as follows:
```javascript
{
version: string,
time: string,
generalDiagnostics: Diagnostic[],
summary: {
filesAnalyzed: number,
errorCount: number,
warningCount: number,
informationCount: number,
timeInSec: number
}
}
```
Each Diagnostic is output in the following format:
```javascript
{
file: string,
severity: 'error' | 'warning' | 'information',
message: string,
rule?: string,
range: {
start: {
line: number,
character: number
},
end: {
line: number,
character: number
}
}
}
```
Diagnostic line and character numbers are zero-based.
Not all diagnostics have an associated diagnostic rule. Diagnostic rules are used only for diagnostics that can be disabled or enabled. If a rule is associated with the diagnostic, it is included in the output. If its not, the rule field is omitted from the JSON output.

11
docs/commands.md Normal file
View File

@@ -0,0 +1,11 @@
## 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
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
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.

39
docs/comments.md Normal file
View File

@@ -0,0 +1,39 @@
## Comments
Some behaviors of pyright can be controlled through the use of comments within the source file.
### 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
# pyright: strict
```
Likewise, basic type checking can be enabled for a file. If you use `# pyright: basic`, the settings for the file use the default “basic” settings, not any override settings specified in the configuration file or language server settings. You can override the basic default settings within the file by specifying them individually (see below).
```python
# pyright: basic
```
Individual configuration settings can also be overridden on a per-file basis and optionally combined with “strict” or “basic” type checking. For example, if you want to enable all type checks except for “reportPrivateUsage”, you could add the following comment:
```python
# pyright: strict, reportPrivateUsage=false
```
Diagnostic levels are also supported.
```python
# pyright: reportPrivateUsage=warning, reportOptionalCall=error
```
### 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.
Pyright also supports a `# pyright: ignore` comment at the end of a line to suppress all Pyright diagnostics on that line. This can be useful if you use multiple type checkers on your source base and want to limit suppression of diagnostics to Pyright only.
The `# pyright: ignore` comment accepts an optional list of comma-delimited diagnostic rule names surrounded by square brackets. If such a list is present, only diagnostics within those diagnostic rule categories are suppressed on that line. For example, `# pyright: ignore [reportPrivateUsage, reportGeneralTypeIssues]` would suppress diagnostics related to those two categories but no others.
If the `reportUnnecessaryTypeIgnoreComment` configuration option is enabled, any unnecessary `# type: ignore` and `# pyright: ignore` comments will be reported so they can be removed.

461
docs/configuration.md Normal file
View File

@@ -0,0 +1,461 @@
## 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).
Pyright settings can also be specified in a `[tool.pyright]` section of a “pyproject.toml” file. A “pyrightconfig.json” file always takes precedent over “pyproject.toml” if both are present. For a sample pyproject.toml file, see [below](configuration.md#sample-pyprojecttoml-file).
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 config file should generally be relative paths so the config file can be shared by other developers who contribute to the project.
## Environment Options
The following settings control the *environment* in which Pyright will check for diagnostics. These settings determine how Pyright finds source files, imports, and what Python version specific rules are applied.
- **include** [array of paths, optional]: Paths of directories or files that should be considered part of the project. 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.
- **exclude** [array of paths, optional]: Paths of directories or files that should not be considered part of the project. These override the directories and files that `include` matched, allowing specific subdirectories to be excluded. Note that files in the exclude paths may still be included in the analysis if they are referenced (imported) by source files that are not excluded. 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 exclude paths are specified, Pyright automatically excludes the following: `**/node_modules`, `**/__pycache__`, `**/.*`. Pylance also excludes any virtual environment directories regardless of the exclude paths specified. For more detail on Python environment specification and discovery, refer to the [import resolution](import-resolution.md#configuring-your-python-environment) documentation.
- **strict** [array of paths, optional]: Paths of directories or files that should use “strict” analysis if they are included. This is the same as manually adding a “# pyright: strict” comment. In strict mode, most type-checking rules are enabled. Refer to [this table](configuration.md#diagnostic-settings-defaults) for details about which rules are enabled in strict mode. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character).
- **extends** [path, optional]: Path to another `.json` or `.toml` file that is used as a “base configuration”, allowing this configuration to inherit configuration settings. Top-level keys within this configuration overwrite top-level keys in the base configuration. Multiple levels of inheritance are supported. Relative paths specified in a configuration file are resolved relative to the location of that configuration file.
- **defineConstant** [map of constants to values (boolean or string), optional]: Set of identifiers that should be assumed to contain a constant value wherever used within this program. For example, `{ "DEBUG": true }` indicates that pyright should assume that the identifier `DEBUG` will always be equal to `True`. If this identifier is used within a conditional expression (such as `if not DEBUG:`) pyright will use the indicated value to determine whether the guarded block is reachable or not. Member expressions that reference one of these constants (e.g. `my_module.DEBUG`) are also supported.
- **typeshedPath** [path, optional]: Path to a directory that contains typeshed type stub files. Pyright ships with a bundled copy of typeshed type stubs. If you want to use a different version of typeshed stubs, you can clone the [typeshed github repo](https://github.com/python/typeshed) to a local directory and reference the location with this path. This option is useful if youre actively contributing updates to typeshed.
- **stubPath** [path, optional]: Path to a directory that contains custom type stubs. Each package's type stub file(s) are expected to be in its own subdirectory. The default value of this setting is "./typings". (typingsPath is now deprecated)
- **venvPath** [path, optional]: Path to a directory containing one or more subdirectories, each of which contains a virtual environment. When used in conjunction with a **venv** setting (see below), pyright will search for imports in the virtual environments site-packages directory rather than the paths specified by the default Python interpreter. If you are working on a project with other developers, it is best not to specify this setting in the config file, since this path will typically differ for each developer. Instead, it can be specified on the command line or in a per-user setting. For more details, refer to the [import resolution](import-resolution.md#configuring-your-python-environment) documentation. This setting is ignored when using Pylance. VS Code's python interpreter path is used instead.
- **venv** [string, optional]: Used in conjunction with the venvPath, specifies the virtual environment to use. For more details, refer to the [import resolution](import-resolution.md#configuring-your-python-environment) documentation. This setting is ignored when using Pylance.
- **verboseOutput** [boolean]: Specifies whether output logs should be verbose. This is useful when diagnosing certain problems like import resolution issues.
- **extraPaths** [array of strings, optional]: Additional search paths that will be used when searching for modules imported by files.
- **pythonVersion** [string, optional]: Specifies the version of Python that will be used to execute the source code. The version should be specified as a string in the format "M.m" where M is the major version and m is the minor (e.g. `"3.0"` or `"3.6"`). If a version is provided, pyright will generate errors if the source code makes use of language features that are not supported in that version. It will also tailor its use of type stub files, which conditionalizes type definitions based on the version. If no version is specified, pyright will use the version of the current python interpreter, if one is present.
- **pythonPlatform** [string, optional]: Specifies the target platform that will be used to execute the source code. Should be one of `"Windows"`, `"Darwin"`, `"Linux"`, `"iOS"`, `"Android"`, or `"All"`. If specified, pyright will tailor its use of type stub files, which conditionalize type definitions based on the platform. If no platform is specified, pyright will use the current platform.
- **executionEnvironments** [array of objects, optional]: Specifies a list of execution environments (see [below](configuration.md#execution-environment-options)). Execution environments are searched from start to finish by comparing the path of a source file with the root path specified in the execution environment.
- **useLibraryCodeForTypes** [boolean]: Determines whether pyright reads, parses and analyzes library code to extract type information in the absence of type stub files. Type information will typically be incomplete. We recommend using type stubs where possible. The default value for this option is true.
## Type Evaluation Settings
The following settings determine how different types should be evaluated.
- <a name="strictListInference"></a> **strictListInference** [boolean]: When inferring the type of a list, use strict type assumptions. For example, the expression `[1, 'a', 3.4]` could be inferred to be of type `list[Any]` or `list[int | str | float]`. If this setting is true, it will use the latter (stricter) type. The default value for this setting is `false`.
- <a name="strictDictionaryInference"></a> **strictDictionaryInference** [boolean]: When inferring the type of a dictionarys keys and values, use strict type assumptions. For example, the expression `{'a': 1, 'b': 'a'}` could be inferred to be of type `dict[str, Any]` or `dict[str, int | str]`. If this setting is true, it will use the latter (stricter) type. The default value for this setting is `false`.
- <a name="strictSetInference"></a> **strictSetInference** [boolean]: When inferring the type of a set, use strict type assumptions. For example, the expression `{1, 'a', 3.4}` could be inferred to be of type `set[Any]` or `set[int | str | float]`. If this setting is true, it will use the latter (stricter) type. The default value for this setting is `false`.
- <a name="analyzeUnannotatedFunctions"></a> **analyzeUnannotatedFunctions** [boolean]: Analyze and report errors for functions and methods that have no type annotations for input parameters or return types. The default value for this setting is `true`.
- <a name="strictParameterNoneValue"></a> **strictParameterNoneValue** [boolean]: PEP 484 indicates that when a function parameter is assigned a default value of None, its type should implicitly be Optional even if the explicit type is not. When enabled, this rule requires that parameter type annotations use Optional explicitly in this case. The default value for this setting is `true`.
- <a name="enableTypeIgnoreComments"></a> **enableTypeIgnoreComments** [boolean]: PEP 484 defines support for "# type: ignore" comments. This switch enables or disables support for these comments. The default value for this setting is `true`. This does not affect "# pyright: ignore" comments.
- <a name="deprecateTypingAliases"></a> **deprecateTypingAliases** [boolean]: PEP 585 indicates that aliases to types in standard collections that were introduced solely to support generics are deprecated as of Python 3.9. This switch controls whether these are treated as deprecated. This applies only when pythonVersion is 3.9 or newer. The default value for this setting is `false` but may be switched to `true` in the future.
- <a name="enableReachabilityAnalysis"></a> **enableReachabilityAnalysis** [boolean]: If enabled, code that is determined to be unreachable by type analysis is reported using a tagged hint. This setting does not affect code that is determined to be unreachable independent of type analysis; such code is always reported as unreachable using a tagged hint. This setting also has no effect when using the command-line version of pyright because it never emits tagged hints for unreachable code.
- <a name="enableExperimentalFeatures"></a> **enableExperimentalFeatures** [boolean]: Enables a set of experimental (mostly undocumented) features that correspond to proposed or exploratory changes to the Python typing standard. These features will likely change or be removed, so they should not be used except for experimentation purposes. The default value for this setting is `false`.
- <a name="disableBytesTypePromotions"></a> **disableBytesTypePromotions** [boolean]: Disables legacy behavior where `bytearray` and `memoryview` are considered subtypes of `bytes`. [PEP 688](https://peps.python.org/pep-0688/#no-special-meaning-for-bytes) deprecates this behavior, but this switch is provided to restore the older behavior. The default value for this setting is `true`.
## Type Check Diagnostics Settings
The following settings control pyrights diagnostic output (warnings or errors).
- **typeCheckingMode** ["off", "basic", "standard", "strict"]: Specifies the default rule set to use. Some rules can be overridden using additional configuration flags documented below. The default value for this setting is "standard". If set to "off", all type-checking rules are disabled, but Python syntax and semantic errors are still reported.
- **ignore** [array of paths, optional]: Paths of directories or files whose diagnostic output (errors and warnings) should be suppressed even if they are an included file or within the transitive closure of an included file. Paths may contain wildcard characters ** (a directory or multiple levels of directories), * (a sequence of zero or more characters), or ? (a single character). This setting can be overridden in VS code in your settings.json.
### Type Check Rule Overrides
The following settings allow more fine grained control over the **typeCheckingMode**. Unless otherwise specified, each diagnostic setting can specify a boolean value (`false` indicating that no error is generated and `true` indicating that an error is generated). Alternatively, a string value of `"none"`, `"warning"`, `"information"`, or `"error"` can be used to specify the diagnostic level.
- <a name="reportGeneralTypeIssues"></a> **reportGeneralTypeIssues** [boolean or string, optional]: Generate or suppress diagnostics for general type inconsistencies, unsupported operations, argument/parameter mismatches, etc. This covers all of the basic type-checking rules not covered by other rules. It does not include syntax errors. The default value for this setting is `"error"`.
- <a name="reportPropertyTypeMismatch"></a> **reportPropertyTypeMismatch** [boolean or string, optional]: Generate or suppress diagnostics for properties where the type of the value passed to the setter is not assignable to the value returned by the getter. Such mismatches violate the intended use of properties, which are meant to act like variables. The default value for this setting is `"none"`.
- <a name="reportFunctionMemberAccess"></a> **reportFunctionMemberAccess** [boolean or string, optional]: Generate or suppress diagnostics for non-standard member accesses for functions. The default value for this setting is `"error"`.
- <a name="reportMissingImports"></a> **reportMissingImports** [boolean or string, optional]: Generate or suppress diagnostics for imports that have no corresponding imported python file or type stub file. The default value for this setting is `"error"`.
- <a name="reportMissingModuleSource"></a> **reportMissingModuleSource** [boolean or string, optional]: Generate or suppress diagnostics for imports that have no corresponding source file. This happens when a type stub is found, but the module source file was not found, indicating that the code may fail at runtime when using this execution environment. Type checking will be done using the type stub. The default value for this setting is `"warning"`.
- <a name="reportInvalidTypeForm"></a> **reportInvalidTypeForm** [boolean or string, optional]: Generate or suppress diagnostics for type annotations that use invalid type expression forms or are semantically invalid. The default value for this setting is `"error"`.
- <a name="reportMissingTypeStubs"></a> **reportMissingTypeStubs** [boolean or string, optional]: Generate or suppress diagnostics for imports that have no corresponding type stub file (either a typeshed file or a custom type stub). The type checker requires type stubs to do its best job at analysis. The default value for this setting is `"none"`. Note that there is a corresponding quick fix for this diagnostics that let you generate custom type stub to improve editing experiences.
- <a name="reportImportCycles"></a> **reportImportCycles** [boolean or string, optional]: Generate or suppress diagnostics for cyclical import chains. These are not errors in Python, but they do slow down type analysis and often hint at architectural layering issues. Generally, they should be avoided. The default value for this setting is `"none"`. Note that there are import cycles in the typeshed stdlib typestub files that are ignored by this setting.
- <a name="reportUnusedImport"></a> **reportUnusedImport** [boolean or string, optional]: Generate or suppress diagnostics for an imported symbol that is not referenced within that file. The default value for this setting is `"none"`.
- <a name="reportUnusedClass"></a> **reportUnusedClass** [boolean or string, optional]: Generate or suppress diagnostics for a class with a private name (starting with an underscore) that is not accessed. The default value for this setting is `"none"`.
- <a name="reportUnusedFunction"></a> **reportUnusedFunction** [boolean or string, optional]: Generate or suppress diagnostics for a function or method with a private name (starting with an underscore) that is not accessed. The default value for this setting is `"none"`.
- <a name="reportUnusedVariable"></a> **reportUnusedVariable** [boolean or string, optional]: Generate or suppress diagnostics for a variable that is not accessed. The default value for this setting is `"none"`. Variables whose names begin with an underscore are exempt from this check.
- <a name="reportDuplicateImport"></a> **reportDuplicateImport** [boolean or string, optional]: Generate or suppress diagnostics for an imported symbol or module that is imported more than once. The default value for this setting is `"none"`.
- <a name="reportWildcardImportFromLibrary"></a> **reportWildcardImportFromLibrary** [boolean or string, optional]: Generate or suppress diagnostics for a wildcard import from an external library. The use of this language feature is highly discouraged and can result in bugs when the library is updated. The default value for this setting is `"warning"`.
- <a name="reportAbstractUsage"></a> **reportAbstractUsage** [boolean or string, optional]: Generate or suppress diagnostics for the attempted instantiate an abstract or protocol class or use of an abstract method. The default value for this setting is `"error"`.
- <a name="reportArgumentType"></a> **reportArgumentType** [boolean or string, optional]: Generate or suppress diagnostics for argument type incompatibilities when evaluating a call expression. The default value for this setting is `"error"`.
- <a name="reportAssertTypeFailure"></a> **reportAssertTypeFailure** [boolean or string, optional]: Generate or suppress diagnostics for a type mismatch detected by the `typing.assert_type` call. The default value for this setting is `"error"`.
- <a name="reportAssignmentType"></a> **reportAssignmentType** [boolean or string, optional]: Generate or suppress diagnostics for assignment type incompatibility. The default value for this setting is `"error"`.
- <a name="reportAttributeAccessIssue"></a> **reportAttributeAccessIssue** [boolean or string, optional]: Generate or suppress diagnostics related to attribute accesses. The default value for this setting is `"error"`.
- <a name="reportCallIssue"></a> **reportCallIssue** [boolean or string, optional]: Generate or suppress diagnostics related to call expressions and arguments passed to a call target. The default value for this setting is `"error"`.
- <a name="reportInconsistentOverload"></a> **reportInconsistentOverload** [boolean or string, optional]: Generate or suppress diagnostics for an overloaded function that has overload signatures that are inconsistent with each other or with the implementation. The default value for this setting is `"error"`.
- <a name="reportIndexIssue"></a> **reportIndexIssue** [boolean or string, optional]: Generate or suppress diagnostics related to index operations and expressions. The default value for this setting is `"error"`.
- <a name="reportInvalidTypeArguments"></a> **reportInvalidTypeArguments** [boolean or string, optional]: Generate or suppress diagnostics for invalid type argument usage. The default value for this setting is `"error"`.
- <a name="reportNoOverloadImplementation"></a> **reportNoOverloadImplementation** [boolean or string, optional]: Generate or suppress diagnostics for an overloaded function or method if the implementation is not provided. The default value for this setting is `"error"`.
- <a name="reportOperatorIssue"></a> **reportOperatorIssue** [boolean or string, optional]: Generate or suppress diagnostics related to the use of unary or binary operators (like `*` or `not`). The default value for this setting is `"error"`.
- <a name="reportOptionalSubscript"></a> **reportOptionalSubscript** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to subscript (index) a variable with an Optional type. The default value for this setting is `"error"`.
- <a name="reportOptionalMemberAccess"></a> **reportOptionalMemberAccess** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to access a member of a variable with an Optional type. The default value for this setting is `"error"`.
- <a name="reportOptionalCall"></a> **reportOptionalCall** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to call a variable with an Optional type. The default value for this setting is `"error"`.
- <a name="reportOptionalIterable"></a> **reportOptionalIterable** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to use an Optional type as an iterable value (e.g. within a `for` statement). The default value for this setting is `"error"`.
- <a name="reportOptionalContextManager"></a> **reportOptionalContextManager** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to use an Optional type as a context manager (as a parameter to a `with` statement). The default value for this setting is `"error"`.
- <a name="reportOptionalOperand"></a> **reportOptionalOperand** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to use an Optional type as an operand to a unary operator (like `~`) or the left-hand operator of a binary operator (like `*` or `<<`). The default value for this setting is `"error"`.
- <a name="reportRedeclaration"></a> **reportRedeclaration** [boolean or string, optional]: Generate or suppress diagnostics for a symbol that has more than one type declaration. The default value for this setting is `"error"`.
- <a name="reportReturnType"></a> **reportReturnType** [boolean or string, optional]: Generate or suppress diagnostics related to function return type compatibility. The default value for this setting is `"error"`.
- <a name="reportTypedDictNotRequiredAccess"></a> **reportTypedDictNotRequiredAccess** [boolean or string, optional]: Generate or suppress diagnostics for an attempt to access a non-required field within a TypedDict without first checking whether it is present. The default value for this setting is `"error"`.
- <a name="reportUntypedFunctionDecorator"></a> **reportUntypedFunctionDecorator** [boolean or string, optional]: Generate or suppress diagnostics for function decorators that have no type annotations. These obscure the function type, defeating many type analysis features. The default value for this setting is `"none"`.
- <a name="reportUntypedClassDecorator"></a> **reportUntypedClassDecorator** [boolean or string, optional]: Generate or suppress diagnostics for class decorators that have no type annotations. These obscure the class type, defeating many type analysis features. The default value for this setting is `"none"`.
- <a name="reportUntypedBaseClass"></a> **reportUntypedBaseClass** [boolean or string, optional]: Generate or suppress diagnostics for base classes whose type cannot be determined statically. These obscure the class type, defeating many type analysis features. The default value for this setting is `"none"`.
- <a name="reportUntypedNamedTuple"></a> **reportUntypedNamedTuple** [boolean or string, optional]: Generate or suppress diagnostics when “namedtuple” is used rather than “NamedTuple”. The former contains no type information, whereas the latter does. The default value for this setting is `"none"`.
- <a name="reportPrivateUsage"></a> **reportPrivateUsage** [boolean or string, optional]: Generate or suppress diagnostics for incorrect usage of private or protected variables or functions. Protected class members begin with a single underscore (“_”) and can be accessed only by subclasses. Private class members begin with a double underscore but do not end in a double underscore and can be accessed only within the declaring class. Variables and functions declared outside of a class are considered private if their names start with either a single or double underscore, and they cannot be accessed outside of the declaring module. The default value for this setting is `"none"`.
- <a name="reportTypeCommentUsage"></a> **reportTypeCommentUsage** [boolean or string, optional]: Prior to Python 3.5, the grammar did not support type annotations, so types needed to be specified using “type comments”. Python 3.5 eliminated the need for function type comments, and Python 3.6 eliminated the need for variable type comments. Future versions of Python will likely deprecate all support for type comments. If enabled, this check will flag any type comment usage unless it is required for compatibility with the specified language version. The default value for this setting is `"none"`.
- <a name="reportPrivateImportUsage"></a> **reportPrivateImportUsage** [boolean or string, optional]: Generate or suppress diagnostics for use of a symbol from a "py.typed" module that is not meant to be exported from that module. The default value for this setting is `"error"`.
- <a name="reportConstantRedefinition"></a> **reportConstantRedefinition** [boolean or string, optional]: Generate or suppress diagnostics for attempts to redefine variables whose names are all-caps with underscores and numerals. The default value for this setting is `"none"`.
- <a name="reportDeprecated"></a> **reportDeprecated** [boolean or string, optional]: Generate or suppress diagnostics for use of a class or function that has been marked as deprecated. The default value for this setting is `"none"`.
- <a name="reportIncompatibleMethodOverride"></a> **reportIncompatibleMethodOverride** [boolean or string, optional]: Generate or suppress diagnostics for methods that override a method of the same name in a base class in an incompatible manner (wrong number of parameters, incompatible parameter types, or incompatible return type). The default value for this setting is `"error"`.
- <a name="reportIncompatibleVariableOverride"></a> **reportIncompatibleVariableOverride** [boolean or string, optional]: Generate or suppress diagnostics for class variable declarations that override a symbol of the same name in a base class with a type that is incompatible with the base class symbol type. The default value for this setting is `"error"`.
- <a name="reportInconsistentConstructor"></a> **reportInconsistentConstructor** [boolean or string, optional]: Generate or suppress diagnostics when an `__init__` method signature is inconsistent with a `__new__` signature. The default value for this setting is `"none"`.
- <a name="reportOverlappingOverload"></a> **reportOverlappingOverload** [boolean or string, optional]: Generate or suppress diagnostics for function overloads that overlap in signature and obscure each other or have incompatible return types. The default value for this setting is `"error"`.
- <a name="reportPossiblyUnboundVariable"></a> **reportPossiblyUnboundVariable** [boolean or string, optional]: Generate or suppress diagnostics for variables that are possibly unbound on some code paths. The default value for this setting is `"error"`.
- <a name="reportMissingSuperCall"></a> **reportMissingSuperCall** [boolean or string, optional]: Generate or suppress diagnostics for `__init__`, `__init_subclass__`, `__enter__` and `__exit__` methods in a subclass that fail to call through to the same-named method on a base class. The default value for this setting is `"none"`.
- <a name="reportUninitializedInstanceVariable"></a> **reportUninitializedInstanceVariable** [boolean or string, optional]: Generate or suppress diagnostics for instance variables within a class that are not initialized or declared within the class body or the `__init__` method. The default value for this setting is `"none"`.
- <a name="reportInvalidStringEscapeSequence"></a> **reportInvalidStringEscapeSequence** [boolean or string, optional]: Generate or suppress diagnostics for invalid escape sequences used within string literals. The Python specification indicates that such sequences will generate a syntax error in future versions. The default value for this setting is `"warning"`.
- <a name="reportUnknownParameterType"></a> **reportUnknownParameterType** [boolean or string, optional]: Generate or suppress diagnostics for input or return parameters for functions or methods that have an unknown type. The default value for this setting is `"none"`.
- <a name="reportUnknownArgumentType"></a> **reportUnknownArgumentType** [boolean or string, optional]: Generate or suppress diagnostics for call arguments for functions or methods that have an unknown type. The default value for this setting is `"none"`.
- <a name="reportUnknownLambdaType"></a> **reportUnknownLambdaType** [boolean or string, optional]: Generate or suppress diagnostics for input or return parameters for lambdas that have an unknown type. The default value for this setting is `"none"`.
- <a name="reportUnknownVariableType"></a> **reportUnknownVariableType** [boolean or string, optional]: Generate or suppress diagnostics for variables that have an unknown type. The default value for this setting is `"none"`.
- <a name="reportUnknownMemberType"></a> **reportUnknownMemberType** [boolean or string, optional]: Generate or suppress diagnostics for class or instance variables that have an unknown type. The default value for this setting is `"none"`.
- <a name="reportMissingParameterType"></a> **reportMissingParameterType** [boolean or string, optional]: Generate or suppress diagnostics for input parameters for functions or methods that are missing a type annotation. The `self` and `cls` parameters used within methods are exempt from this check. The default value for this setting is `"none"`.
- <a name="reportMissingTypeArgument"></a> **reportMissingTypeArgument** [boolean or string, optional]: Generate or suppress diagnostics when a generic class is used without providing explicit or implicit type arguments. The default value for this setting is `"none"`.
- <a name="reportInvalidTypeVarUse"></a> **reportInvalidTypeVarUse** [boolean or string, optional]: Generate or suppress diagnostics when a TypeVar is used inappropriately (e.g. if a TypeVar appears only once) within a generic function signature. The default value for this setting is `"warning"`.
- <a name="reportCallInDefaultInitializer"></a> **reportCallInDefaultInitializer** [boolean or string, optional]: Generate or suppress diagnostics for function calls, list expressions, set expressions, or dictionary expressions within a default value initialization expression. Such calls can mask expensive operations that are performed at module initialization time. The default value for this setting is `"none"`.
- <a name="reportUnnecessaryIsInstance"></a> **reportUnnecessaryIsInstance** [boolean or string, optional]: Generate or suppress diagnostics for `isinstance` or `issubclass` calls where the result is statically determined to be always true or always false. Such calls are often indicative of a programming error. The default value for this setting is `"none"`.
- <a name="reportUnnecessaryCast"></a> **reportUnnecessaryCast** [boolean or string, optional]: Generate or suppress diagnostics for `cast` calls that are statically determined to be unnecessary. Such calls are sometimes indicative of a programming error. The default value for this setting is `"none"`.
- <a name="reportUnnecessaryComparison"></a> **reportUnnecessaryComparison** [boolean or string, optional]: Generate or suppress diagnostics for `==` or `!=` comparisons or other conditional expressions that are statically determined to always evaluate to False or True. Such comparisons are sometimes indicative of a programming error. The default value for this setting is `"none"`. Also reports `case` clauses in a `match` statement that can be statically determined to never match (with exception of the `_` wildcard pattern, which is exempted).
- <a name="reportUnnecessaryContains"></a> **reportUnnecessaryContains** [boolean or string, optional]: Generate or suppress diagnostics for `in` operations that are statically determined to always evaluate to False or True. Such operations are sometimes indicative of a programming error. The default value for this setting is `"none"`.
- <a name="reportAssertAlwaysTrue"></a> **reportAssertAlwaysTrue** [boolean or string, optional]: Generate or suppress diagnostics for `assert` statement that will provably always assert because its first argument is a parenthesized tuple (for example, `assert (v > 0, "Bad value")` when the intent was probably `assert v > 0, "Bad value"`). This is a common programming error. The default value for this setting is `"warning"`.
- <a name="reportSelfClsParameterName"></a> **reportSelfClsParameterName** [boolean or string, optional]: Generate or suppress diagnostics for a missing or misnamed “self” parameter in instance methods and “cls” parameter in class methods. Instance methods in metaclasses (classes that derive from “type”) are allowed to use “cls” for instance methods. The default value for this setting is `"warning"`.
- <a name="reportImplicitStringConcatenation"></a> **reportImplicitStringConcatenation** [boolean or string, optional]: Generate or suppress diagnostics for two or more string literals that follow each other, indicating an implicit concatenation. This is considered a bad practice and often masks bugs such as missing commas. The default value for this setting is `"none"`.
- <a name="reportUndefinedVariable"></a> **reportUndefinedVariable** [boolean or string, optional]: Generate or suppress diagnostics for undefined variables. The default value for this setting is `"error"`.
- <a name="reportUnboundVariable"></a> **reportUnboundVariable** [boolean or string, optional]: Generate or suppress diagnostics for unbound variables. The default value for this setting is `"error"`.
- <a name="reportUnhashable"></a> **reportUnhashable** [boolean or string, optional]: Generate or suppress diagnostics for the use of an unhashable object in a container that requires hashability. The default value for this setting is `"error"`.
- <a name="reportInvalidStubStatement"></a> **reportInvalidStubStatement** [boolean or string, optional]: Generate or suppress diagnostics for statements that are syntactically correct but have no purpose within a type stub file. The default value for this setting is `"none"`.
- <a name="reportIncompleteStub"></a> **reportIncompleteStub** [boolean or string, optional]: Generate or suppress diagnostics for a module-level `__getattr__` call in a type stub file, indicating that it is incomplete. The default value for this setting is `"none"`.
- <a name="reportUnsupportedDunderAll"></a> **reportUnsupportedDunderAll** [boolean or string, optional]: Generate or suppress diagnostics for statements that define or manipulate `__all__` in a way that is not allowed by a static type checker, thus rendering the contents of `__all__` to be unknown or incorrect. Also reports names within the `__all__` list that are not present in the module namespace. The default value for this setting is `"warning"`.
- <a name="reportUnusedCallResult"></a> **reportUnusedCallResult** [boolean or string, optional]: Generate or suppress diagnostics for call statements whose return value is not used in any way and is not None. The default value for this setting is `"none"`.
- <a name="reportUnusedCoroutine"></a> **reportUnusedCoroutine** [boolean or string, optional]: Generate or suppress diagnostics for call statements whose return value is not used in any way and is a Coroutine. This identifies a common error where an `await` keyword is mistakenly omitted. The default value for this setting is `"error"`.
- <a name="reportUnusedExcept"></a> **reportUnusedExcept** [boolean or string, optional]: Generate or suppress diagnostics for an `except` clause that will never be reached. The default value for this setting is `"error"`.
- <a name="reportUnusedExpression"></a> **reportUnusedExpression** [boolean or string, optional]: Generate or suppress diagnostics for simple expressions whose results are not used in any way. The default value for this setting is `"none"`.
- <a name="reportUnnecessaryTypeIgnoreComment"></a> **reportUnnecessaryTypeIgnoreComment** [boolean or string, optional]: Generate or suppress diagnostics for a `# type: ignore` or `# pyright: ignore` comment that would have no effect if removed. The default value for this setting is `"none"`.
- <a name="reportMatchNotExhaustive"></a> **reportMatchNotExhaustive** [boolean or string, optional]: Generate or suppress diagnostics for a `match` statement that does not provide cases that exhaustively match against all potential types of the target expression. The default value for this setting is `"none"`.
- <a name="reportUnreachable"></a> **reportUnreachable** [boolean or string, optional]: Generate or suppress diagnostics for code that is determined to be structurally unreachable or unreachable by type analysis. The default value for this setting is `"none"`.
- <a name="reportImplicitOverride"></a> **reportImplicitOverride** [boolean or string, optional]: Generate or suppress diagnostics for overridden methods in a class that are missing an explicit `@override` decorator. The default value for this setting is `"none"`.
## Execution Environment Options
Pyright allows multiple “execution environments” to be defined for different portions of your source tree. For example, a subtree may be designed to run with different import search paths or a different version of the python interpreter than the rest of the source base.
The following settings can be specified for each execution environment. Each source file within a project is associated with at most one execution environment -- the first one whose root directory contains that file.
- **root** [string, required]: Root path for the code that will execute within this execution environment.
- **extraPaths** [array of strings, optional]: Additional search paths (in addition to the root path) that will be used when searching for modules imported by files within this execution environment. If specified, this overrides the default extraPaths setting when resolving imports for files within this execution environment. Note that each files execution environment mapping is independent, so if file A is in one execution environment and imports a second file B within a second execution environment, any imports from B will use the extraPaths in the second execution environment.
- **pythonVersion** [string, optional]: The version of Python used for this execution environment. If not specified, the global `pythonVersion` setting is used instead.
- **pythonPlatform** [string, optional]: Specifies the target platform that will be used for this execution environment. If not specified, the global `pythonPlatform` setting is used instead.
In addition, any of the [type check diagnostics settings](configuration.md#type-check-diagnostics-settings) listed above can be specified. These settings act as overrides for the files in this execution environment.
## Sample Config File
The following is an example of a pyright config file:
```json
{
"include": [
"src"
],
"exclude": [
"**/node_modules",
"**/__pycache__",
"src/experimental",
"src/typestubs"
],
"ignore": [
"src/oldstuff"
],
"defineConstant": {
"DEBUG": true
},
"stubPath": "src/stubs",
"reportMissingImports": "error",
"reportMissingTypeStubs": false,
"pythonVersion": "3.6",
"pythonPlatform": "Linux",
"executionEnvironments": [
{
"root": "src/web",
"pythonVersion": "3.5",
"pythonPlatform": "Windows",
"extraPaths": [
"src/service_libs"
],
"reportMissingImports": "warning"
},
{
"root": "src/sdk",
"pythonVersion": "3.0",
"extraPaths": [
"src/backend"
]
},
{
"root": "src/tests",
"extraPaths": [
"src/tests/e2e",
"src/sdk"
]
},
{
"root": "src"
}
]
}
```
## Sample pyproject.toml File
```toml
[tool.pyright]
include = ["src"]
exclude = ["**/node_modules",
"**/__pycache__",
"src/experimental",
"src/typestubs"
]
ignore = ["src/oldstuff"]
defineConstant = { DEBUG = true }
stubPath = "src/stubs"
reportMissingImports = "error"
reportMissingTypeStubs = false
pythonVersion = "3.6"
pythonPlatform = "Linux"
executionEnvironments = [
{ root = "src/web", pythonVersion = "3.5", pythonPlatform = "Windows", extraPaths = [ "src/service_libs" ], reportMissingImports = "warning" },
{ root = "src/sdk", pythonVersion = "3.0", extraPaths = [ "src/backend" ] },
{ root = "src/tests", extraPaths = ["src/tests/e2e", "src/sdk" ]},
{ root = "src" }
]
```
## Diagnostic Settings Defaults
Each diagnostic setting has a default that is dictated by the specified type checking mode. The default for each rule can be overridden in the configuration file or settings. In strict type checking mode, overrides may only increase the strictness (e.g. increase the severity level from `"warning"` to `"error"`).
The following table lists the default severity levels for each diagnostic rule within each type checking mode (`"off"`, `"basic"`, `"standard"` and `"strict"`).
| Diagnostic Rule | Off | Basic | Standard | Strict |
| :---------------------------------------- | :--------- | :--------- | :--------- | :--------- |
| analyzeUnannotatedFunctions | true | true | true | true |
| disableBytesTypePromotions | true | true | true | true |
| strictParameterNoneValue | true | true | true | true |
| enableTypeIgnoreComments | true | true | true | true |
| enableReachabilityAnalysis | false | true | true | true |
| strictListInference | false | false | false | true |
| strictDictionaryInference | false | false | false | true |
| strictSetInference | false | false | false | true |
| deprecateTypingAliases | false | false | false | false |
| enableExperimentalFeatures | false | false | false | false |
| reportMissingTypeStubs | "none" | "none" | "none" | "error" |
| reportMissingModuleSource | "warning" | "warning" | "warning" | "warning" |
| reportInvalidTypeForm | "warning" | "error" | "error" | "error" |
| reportMissingImports | "warning" | "error" | "error" | "error" |
| reportUndefinedVariable | "warning" | "error" | "error" | "error" |
| reportAssertAlwaysTrue | "none" | "warning" | "warning" | "error" |
| reportInvalidStringEscapeSequence | "none" | "warning" | "warning" | "error" |
| reportInvalidTypeVarUse | "none" | "warning" | "warning" | "error" |
| reportSelfClsParameterName | "none" | "warning" | "warning" | "error" |
| reportUnsupportedDunderAll | "none" | "warning" | "warning" | "error" |
| reportUnusedExpression | "none" | "warning" | "warning" | "error" |
| reportWildcardImportFromLibrary | "none" | "warning" | "warning" | "error" |
| reportAbstractUsage | "none" | "error" | "error" | "error" |
| reportArgumentType | "none" | "error" | "error" | "error" |
| reportAssertTypeFailure | "none" | "error" | "error" | "error" |
| reportAssignmentType | "none" | "error" | "error" | "error" |
| reportAttributeAccessIssue | "none" | "error" | "error" | "error" |
| reportCallIssue | "none" | "error" | "error" | "error" |
| reportGeneralTypeIssues | "none" | "error" | "error" | "error" |
| reportInconsistentOverload | "none" | "error" | "error" | "error" |
| reportIndexIssue | "none" | "error" | "error" | "error" |
| reportInvalidTypeArguments | "none" | "error" | "error" | "error" |
| reportNoOverloadImplementation | "none" | "error" | "error" | "error" |
| reportOperatorIssue | "none" | "error" | "error" | "error" |
| reportOptionalSubscript | "none" | "error" | "error" | "error" |
| reportOptionalMemberAccess | "none" | "error" | "error" | "error" |
| reportOptionalCall | "none" | "error" | "error" | "error" |
| reportOptionalIterable | "none" | "error" | "error" | "error" |
| reportOptionalContextManager | "none" | "error" | "error" | "error" |
| reportOptionalOperand | "none" | "error" | "error" | "error" |
| reportRedeclaration | "none" | "error" | "error" | "error" |
| reportReturnType | "none" | "error" | "error" | "error" |
| reportTypedDictNotRequiredAccess | "none" | "error" | "error" | "error" |
| reportPrivateImportUsage | "none" | "error" | "error" | "error" |
| reportUnboundVariable | "none" | "error" | "error" | "error" |
| reportUnhashable | "none" | "error" | "error" | "error" |
| reportUnusedCoroutine | "none" | "error" | "error" | "error" |
| reportUnusedExcept | "none" | "error" | "error" | "error" |
| reportFunctionMemberAccess | "none" | "none" | "error" | "error" |
| reportIncompatibleMethodOverride | "none" | "none" | "error" | "error" |
| reportIncompatibleVariableOverride | "none" | "none" | "error" | "error" |
| reportOverlappingOverload | "none" | "none" | "error" | "error" |
| reportPossiblyUnboundVariable | "none" | "none" | "error" | "error" |
| reportConstantRedefinition | "none" | "none" | "none" | "error" |
| reportDeprecated | "none" | "none" | "none" | "error" |
| reportDuplicateImport | "none" | "none" | "none" | "error" |
| reportIncompleteStub | "none" | "none" | "none" | "error" |
| reportInconsistentConstructor | "none" | "none" | "none" | "error" |
| reportInvalidStubStatement | "none" | "none" | "none" | "error" |
| reportMatchNotExhaustive | "none" | "none" | "none" | "error" |
| reportMissingParameterType | "none" | "none" | "none" | "error" |
| reportMissingTypeArgument | "none" | "none" | "none" | "error" |
| reportPrivateUsage | "none" | "none" | "none" | "error" |
| reportTypeCommentUsage | "none" | "none" | "none" | "error" |
| reportUnknownArgumentType | "none" | "none" | "none" | "error" |
| reportUnknownLambdaType | "none" | "none" | "none" | "error" |
| reportUnknownMemberType | "none" | "none" | "none" | "error" |
| reportUnknownParameterType | "none" | "none" | "none" | "error" |
| reportUnknownVariableType | "none" | "none" | "none" | "error" |
| reportUnnecessaryCast | "none" | "none" | "none" | "error" |
| reportUnnecessaryComparison | "none" | "none" | "none" | "error" |
| reportUnnecessaryContains | "none" | "none" | "none" | "error" |
| reportUnnecessaryIsInstance | "none" | "none" | "none" | "error" |
| reportUnusedClass | "none" | "none" | "none" | "error" |
| reportUnusedImport | "none" | "none" | "none" | "error" |
| reportUnusedFunction | "none" | "none" | "none" | "error" |
| reportUnusedVariable | "none" | "none" | "none" | "error" |
| reportUntypedBaseClass | "none" | "none" | "none" | "error" |
| reportUntypedClassDecorator | "none" | "none" | "none" | "error" |
| reportUntypedFunctionDecorator | "none" | "none" | "none" | "error" |
| reportUntypedNamedTuple | "none" | "none" | "none" | "error" |
| reportCallInDefaultInitializer | "none" | "none" | "none" | "none" |
| reportImplicitOverride | "none" | "none" | "none" | "none" |
| reportImplicitStringConcatenation | "none" | "none" | "none" | "none" |
| reportImportCycles | "none" | "none" | "none" | "none" |
| reportMissingSuperCall | "none" | "none" | "none" | "none" |
| reportPropertyTypeMismatch | "none" | "none" | "none" | "none" |
| reportUninitializedInstanceVariable | "none" | "none" | "none" | "none" |
| reportUnnecessaryTypeIgnoreComment | "none" | "none" | "none" | "none" |
| reportUnreachable | "none" | "none" | "none" | "none" |
| reportUnusedCallResult | "none" | "none" | "none" | "none" |
## Overriding settings (in VS Code)
If a pyproject.toml (with a pyright section) or a pyrightconfig.json exists, any pyright settings in a VS Code settings.json will be ignored. Pyrightconfig.json is prescribing the environment to be used for a particular project. Changing the environment configuration options per user is not supported.
If a pyproject.toml (with a pyright section) or a pyrightconfig.json does not exist, then the VS Code settings.json settings apply.
## Locale Configuration
Pyright provides diagnostic messages that are translated to multiple languages. By default, pyright uses the default locale of the operating system. You can override the desired locale through the use of one of the following environment variables, listed in priority order.
```
LC_ALL="de"
LC_MESSAGES="en-us"
LANG="zh-cn"
LANGUAGE="fr"
```
When running in VS Code, the editor's locale takes precedence. Setting these environment variables applies only when using pyright outside of VS Code.

67
docs/features.md Normal file
View File

@@ -0,0 +1,67 @@
## 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.
### Configurability
Pyright supports [configuration files](configuration.md) that provide granular control over settings. Different “execution environments” can be associated with subdirectories within a source base. Each environment can specify different module search paths, python language versions, and platform targets.
### Type Checking Features
* [PEP 484](https://www.python.org/dev/peps/pep-0484/) type hints including generics
* [PEP 487](https://www.python.org/dev/peps/pep-0487/) simpler customization of class creation
* [PEP 526](https://www.python.org/dev/peps/pep-0526/) syntax for variable annotations
* [PEP 544](https://www.python.org/dev/peps/pep-0544/) structural subtyping
* [PEP 561](https://www.python.org/dev/peps/pep-0561/) distributing and packaging type information
* [PEP 563](https://www.python.org/dev/peps/pep-0563/) postponed evaluation of annotations
* [PEP 570](https://www.python.org/dev/peps/pep-0570/) position-only parameters
* [PEP 585](https://www.python.org/dev/peps/pep-0585/) type hinting generics in standard collections
* [PEP 586](https://www.python.org/dev/peps/pep-0586/) literal types
* [PEP 589](https://www.python.org/dev/peps/pep-0589/) typed dictionaries
* [PEP 591](https://www.python.org/dev/peps/pep-0591/) final qualifier
* [PEP 593](https://www.python.org/dev/peps/pep-0593/) flexible variable annotations
* [PEP 604](https://www.python.org/dev/peps/pep-0604/) complementary syntax for unions
* [PEP 612](https://www.python.org/dev/peps/pep-0612/) parameter specification variables
* [PEP 613](https://www.python.org/dev/peps/pep-0613/) explicit type aliases
* [PEP 635](https://www.python.org/dev/peps/pep-0635/) structural pattern matching
* [PEP 646](https://www.python.org/dev/peps/pep-0646/) variadic generics
* [PEP 647](https://www.python.org/dev/peps/pep-0647/) user-defined type guards
* [PEP 655](https://www.python.org/dev/peps/pep-0655/) required typed dictionary items
* [PEP 673](https://www.python.org/dev/peps/pep-0673/) Self type
* [PEP 675](https://www.python.org/dev/peps/pep-0675/) arbitrary literal strings
* [PEP 681](https://www.python.org/dev/peps/pep-0681/) dataclass transform
* [PEP 692](https://www.python.org/dev/peps/pep-0692/) TypedDict for kwargs typing
* [PEP 695](https://www.python.org/dev/peps/pep-0695/) type parameter syntax
* [PEP 696](https://www.python.org/dev/peps/pep-0696/) type defaults for TypeVarLikes
* [PEP 698](https://www.python.org/dev/peps/pep-0698/) override decorator for static typing
* [PEP 702](https://www.python.org/dev/peps/pep-0702/) marking deprecations
* [PEP 705](https://www.python.org/dev/peps/pep-0705/) TypedDict: read-only items
* [PEP 728](https://www.python.org/dev/peps/pep-0728/) TypedDict with typed extra items
* [PEP 742](https://www.python.org/dev/peps/pep-0742/) narrowing types with TypeIs
* [PEP 746](https://www.python.org/dev/peps/pep-0746/) (experimental) type checking annotated metadata
* [PEP 747](https://www.python.org/dev/peps/pep-0747/) (experimental) annotating type forms
* [PEP 764](https://www.python.org/dev/peps/pep-0764/) (experimental) inline typed dictionaries
* Type inference for function return values, instance variables, class variables, and globals
* Type guards that understand conditional code flow constructs like if/else statements
### Language Server Support
Pyright ships as both a command-line tool and a language server that provides many powerful features that help improve programming efficiency.
* Intelligent type completion of keywords, symbols, and import names appears when editing
* Import statements are automatically inserted when necessary for type completions
* Signature completion tips help when filling in arguments for a call
* Hover over symbols to provide type information and doc strings
* Find Definitions to quickly go to the location of a symbols definition
* Find References to find all references to a symbol within a code base
* Rename Symbol to rename all references to a symbol within a code base
* Find Symbols within the current document or within the entire workspace
* View call hierarchy information — calls made within a function and places where a function is called
* Organize Imports command for automatically ordering imports according to PEP8 rules
* Type stub generation for third-party libraries
### Built-in Type Stubs
Pyright includes a recent copy of the stdlib type stubs from [Typeshed](https://github.com/python/typeshed). It can be configured to use another (perhaps more recent or modified) copy of the Typeshed type stubs. Of course, it also works with custom type stub files that are part of your project.
## Limitations
Pyright provides support for Python 3.0 and newer. There are no plans to support older versions.

26
docs/getting-started.md Normal file
View File

@@ -0,0 +1,26 @@
## Getting Started with Type Checking
A static type checker like Pyright can add incremental value to your source code as more type information is provided.
Here is a typical progression:
### 1. Initial Type Checking
* Install pyright (either the language server or command-line tool).
* Write a minimal `pyrightconfig.json` that defines `include` entries. Place the config file in your projects top-level directory and commit it to your repo. Alternatively, you can add a pyright section to a `pyproject.toml` file. For additional details and a sample config file, refer to [this documentation](configuration.md).
* Run pyright over your source base with the default settings. Fix any errors and warnings that it emits. Optionally disable specific diagnostic rules if they are generating too many errors. They can be re-enabled at a later time.
### 2. Types For Imported Libraries
* Update dependent libraries to recent versions. Many popular libraries have recently added inlined types, which eliminates the need to install or create type stubs.
* Enable the `reportMissingTypeStubs` setting in the config file and add (minimal) type stub files for the imported files. You may wish to create a stubs directory within your code base — a location for all of your custom type stub files. Configure the “stubPath” config entry to refer to this directory.
* Look for type stubs for the packages you use. Some package authors opt to ship stubs as a separate companion package named that has “-stubs” appended to the name of the original package.
* In cases where type stubs do not yet exist for a package you are using, consider creating a custom type stub that defines the portion of the interface that your source code consumes. Check in your custom type stub files and configure pyright to run as part of your continuous integration (CI) environment to keep the project “type clean”.
### 3. Incremental Typing
* Incrementally add type annotations to your code files. The annotations that provide most value are on function input parameters, instance variables, and return parameters (in that order).
* Enable stricter type checking options like "reportUnknownParameterType", and "reportUntypedFunctionDecorator".
### 4. Strict Typing
* On a file-by-file basis, enable all type checking options by adding the comment `# pyright: strict` somewhere in the file.
* Optionally add entire subdirectories to the `strict` config entry to indicate that all files within those subdirectories should be strictly typed.

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/img/PyrightLarge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/img/PyrightSmall.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,2 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="104" height="20" role="img" aria-label="pyright: checked"><title>pyright: checked</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="104" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="49" height="20" fill="#555"/><rect x="49" width="55" height="20" fill="#4c1"/><rect width="104" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="255" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="390">pyright</text><text x="255" y="140" transform="scale(.1)" fill="#fff" textLength="390">pyright</text><text aria-hidden="true" x="755" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="450">checked</text><text x="755" y="140" transform="scale(.1)" fill="#fff" textLength="450">checked</text></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

75
docs/import-resolution.md Normal file
View File

@@ -0,0 +1,75 @@
## Import Resolution
### 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:
1. Try to resolve using the **stubPath** as defined in the `stubPath` config entry or the `python.analysis.stubPath` setting.
2. Try to resolve using **code within the workspace**.
* Try to resolve relative to the **root directory** of the execution environment. If no execution environments are specified in the config file, use the root of the workspace. For more information about execution environments, refer to the [configuration documentation](configuration.md#execution-environment-options).
* Try to resolve using any of the **extra paths** defined for the execution environment in the config file. If no execution environment applies, use the `python.analysis.extraPaths` setting. Extra paths are searched in the order in which they are provided in the config file or setting.
* If no execution environment is configured, try to resolve using the **local directory `src`**. It is common for Python projects to place local source files within a directory of this name.
3. Try to resolve using **stubs or inlined types found within installed packages**. Pyright uses the configured Python environment to determine whether a package has been installed. For more details about how to configure your Python environment for Pyright, see below. If a Python environment is configured, Pyright looks in the `lib/site-packages`, `Lib/site-packages`, or `python*/site-packages` subdirectory. If no site-packages directory can be found, Pyright attempts to run the configured Python interpreter and ask it for its search paths. If no Python environment is configured, Pyright will use the default Python interpreter by invoking `python`.
* For a given package, try to resolve first using a **stub package**. Stub packages, as defined in [PEP 561](https://www.python.org/dev/peps/pep-0561/#type-checker-module-resolution-order), are named the same as the original package but with “-stubs” appended.
* Try to resolve using an **inline stub**, a “.pyi” file that ships within the package.
* If the package contains a “py.typed” file as described in [PEP 561](https://www.python.org/dev/peps/pep-0561/), use inlined type annotations provided in “.py” files within the package.
* If the `python.analysis.useLibraryCodeForTypes` setting is set to true, try to resolve using the **library implementation** (“.py” file). Some “.py” files may contain partial or complete type annotations. Pyright will use type annotations that are provided and do its best to infer any missing type information.
4. Try to resolve using a **stdlib typeshed stub**. If the `typeshedPath` is configured, use this instead of the typeshed stubs that are packaged with Pyright. This allows for the use of a newer or a patched version of the typeshed stdlib stubs.
5. Try to resolve using a **third-party typeshed** stub. If the `typeshedPath` is configured, use this instead of the typeshed stubs that are packaged with Pyright. This allows for the use of a newer or a patched version of the typeshed third-party stubs.
6. For an absolute import, if all of the above attempts fail, attempt to import a module from the same directory as the importing file and parent directories that are also children of the root workspace. This accommodates cases where it is assumed that a Python script will be executed from one of these subdirectories rather than from the root directory.
### 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:
1. If a `venv` name is specified along with a `python.venvPath` setting (or a `--venvpath` command-line argument), it appends the venv name to the specified venv path. This mechanism is not recommended for most users because it is less robust than the next two options because it relies on pyrights internal logic to determine the import resolution paths based on the virtual environment directories and files. The other two mechanisms (2 and 3 below) use the configured python interpreter to determine the import resolution paths (the value of `sys.path`).
2. Use the `python.pythonPath` setting. This setting is defined by the VS Code Python extension and can be configured using the Python extensions environment picker interface. More recent versions of the Python extension no longer store the selected Python environment in the `python.pythonPath` setting and instead use a storage mechanism that is private to the extension. Pyright is able to access this through an API exposed by the Python extension.
3. As a fallback, use the default Python environment (i.e. the one that is invoked when typing `python` in the shell).
### Editable installs
If you want to use static analysis tools with an editable install, you should configure the editable install to use `.pth` files that contain file paths rather than executable lines (prefixed with `import`) that install import hooks.
Import hooks can provide an editable installation that is a more accurate representation of your real installation. However, because resolving module locations using an import hook requires executing Python code, they are not usable by Pyright and other static analysis tools. Therefore, if your editable install is configured to use import hooks, Pyright will be unable to find the corresponding source files.
Notably, setuptools uses import hooks by default. For setuptools-based editable installs to be usable with Pyright, setuptools needs to be configured to use path-based `.pth` files through the build frontend.
#### pip with setuptools
`pip` with `setuptools` supports two ways to avoid import hooks:
- [compat mode](https://setuptools.pypa.io/en/latest/userguide/development_mode.html#legacy-behavior)
- [strict mode](https://setuptools.pypa.io/en/latest/userguide/development_mode.html#strict-editable-installs)
#### uv with setuptools
When using uv with setuptools, uv can be [configured](https://docs.astral.sh/uv/reference/settings/#config-settings) to avoid import hooks:
```toml
[tool.uv]
config-settings = { editable_mode = "compat" }
```
The `uv_build` backend always uses path-based `.pth` files.
#### Hatch / Hatchling
[Hatchling](https://hatch.pypa.io/latest/config/build/#dev-mode) uses path-based `.pth` files by
default. It will only use import hooks if you set `dev-mode-exact` to `true`.
#### PDM
[PDM](https://pdm.fming.dev/latest/pyproject/build/#editable-build-backend) uses path-based `.pth`
files by default. It will only use import hooks if you set `editable-backend` to
`"editables"`.
### 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.

31
docs/import-statements.md Normal file
View File

@@ -0,0 +1,31 @@
## Import Statements
### Loader Side Effects
An import statement instructs the Python import loader to perform several operations. For example, the statement `from a.b import Foo as Bar` causes the following steps to be performed at runtime:
1. Load and execute module `a` if it hasnt previously been loaded. Cache a reference to `a`.
2. Load and execute submodule `b` if it hasnt previously been loaded.
3. Store a reference to submodule `b` to the variable `b` within module `a`s namespace.
4. Look up attribute `Foo` within module `b`.
5. Assign the value of attribute `Foo` to a local variable called `Bar`.
If another source file were to subsequently execute the statement `import a`, it would observe `b` in the namespace of `a` as a side effect of step 3 in the earlier import operation. Relying on such side effects leads to fragile code because a change in execution ordering or a modification to one module can break code in another module. Reliance on such side effects is therefore considered a bug by Pyright, which intentionally does not attempt to model such side effects.
### Implicit Module Loads
Pyright models two loader side effects that are considered safe and are commonly used in Python code.
1. If an import statement targets a multi-part module name and does not use an alias, all modules within the multi-part module name are assumed to be loaded. For example, the statement `import a.b.c` is treated as though it is three back-to-back import statements: `import a`, `import a.b` and `import a.b.c`. This allows for subsequent use of all symbols in `a`, `a.b`, and `a.b.c`. If an alias is used (e.g. `import a.b.c as abc`), this is assumed to load only module `c`. A subsequent `import a` would not provide access to `a.b` or `a.b.c`.
2. If an `__init__.py` file includes an import statement of the form `from . import a`, the local variable `a` is assigned a reference to submodule `a`. Similarly, if an `__init__.py` file includes an import statement of the form `from .a import b`, the local variable `a` is assigned a reference to submodule `a`. This statement form is treated as though it is two back-to-back import statements: `from . import a` followed by `from .a import b`.
### Unsupported Loader Side Effects
All other module loader side effects are intentionally _not_ modeled by Pyright and should not be relied upon in code. Examples include:
- If one module contains the statement `import a.b` and a second module includes `import a`, the second module should not rely on the fact that `a.b` is now accessible as a side effect of the first modules import.
- If a module contains the statement `import a.b` in the global scope and a function that includes the statement `import a` or `import a.c`, the function should not assume that it can access `a.b`. This assumption might or might not be safe depending on execution order.
- If a module contains the statements `import a.b as foo` and `import a`, code within that module should not assume that it can access `a.b`. Such an assumption might be safe depending on the relative order of the statements and the order in which they are executed, but it leads to fragile code.

46
docs/index.html Normal file
View File

@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Pyright</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css" />
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: '',
repo: '',
};
</script>
<script>
window.$docsify = {
name: 'Pyright',
nameLink: 'https://microsoft.github.io/pyright',
search: 'auto',
loadSidebar: true,
loadNavbar: true,
auto2top: true,
search: {
maxAge: 86400000, // Expiration time, the default one day
paths: 'auto',
placeholder: 'Type to search',
noData: 'No Results',
depth: 4, // Headline depth, 1 - 6
hideOtherSidebarContent: true,
},
};
</script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-python.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-yml.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-toml.min.js"></script>
</body>
</html>

49
docs/installation.md Normal file
View File

@@ -0,0 +1,49 @@
## Installation
### Language Server
#### VS Code
For most VS Code users, we recommend using the Pylance extension rather than Pyright. Pylance incorporates the Pyright type checker but features additional capabilities such as semantic token highlighting and symbol indexing. You can install the latest-published version of the Pylance VS Code extension directly from VS Code. Simply open the extensions panel and search for “Pylance”.
#### Vim
Vim/neovim users can install [coc-pyright](https://github.com/fannheyward/coc-pyright), the Pyright extension for coc.nvim.
Alternatively, [ALE](https://github.com/dense-analysis/ale) will automatically check your code with Pyright if added to the linters list.
#### Sublime Text
Sublime text users can install the [LSP-pyright](https://github.com/sublimelsp/LSP-pyright) plugin from [package control](https://packagecontrol.io/packages/LSP-pyright).
#### Emacs
Emacs users can install [eglot](https://github.com/joaotavora/eglot) or [lsp-mode](https://github.com/emacs-lsp/lsp-mode) with [lsp-pyright](https://github.com/emacs-lsp/lsp-pyright).
#### PyCharm
PyCharm users can enable native Pyright support in the settings.
For more information, refer to [PyCharm documentation](https://www.jetbrains.com/help/pycharm/lsp-tools.html#pyright).
### Command-line
#### Python Package
A [community-maintained](https://github.com/RobertCraigie/pyright-python) Python package by the name of “pyright” is available on pypi and conda-forge. This package will automatically install node (which Pyright requires) and keep Pyright up to date.
`pip install pyright`
or
`conda install pyright`
Once installed, you can run the tool from the command line as follows:
`pyright <options>`
#### NPM Package
Alternatively, you can install the command-line version of Pyright directly from npm, which is part of node. If you don't have a recent version of node on your system, install that first from [nodejs.org](https://nodejs.org).
To install pyright globally:
`npm install -g pyright`
On MacOS or Linux, sudo may be required to install globally:
`sudo npm install -g pyright`
To update to the latest version:
`sudo npm update -g pyright`

38
docs/internals.md Normal file
View File

@@ -0,0 +1,38 @@
# Pyright Internals
## Code Structure
* packages/vscode-pyright/src/extension.ts: Language Server Protocol (LSP) client entry point for VS Code extension.
* packages/pyright-internal/typeshed-fallback/: Recent copy of Typeshed type stub files for Python stdlib
* packages/pyright-internal/src/pyright.ts: Main entry point for command-line tool
* packages/pyright-internal/src/server.ts: Main entry point for LSP server
* packages/pyright-internal/src/typeServer: Type Server Protocol (TSP) server built on the analyzer (see [Type Server](type-server.md))
* packages/pyright-internal/src/analyzer: Modules that perform analysis passes over Python parse tree
* packages/pyright-internal/src/common: Modules that are common to the parser and analyzer
* packages/pyright-internal/src/parser: Modules that perform tokenization and parsing of Python source
* packages/pyright-internal/src/tests: Tests for the parser and analyzer
## Core Concepts
Pyright implements a [service](https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/analyzer/service.ts), a persistent in-memory object that controls the order of analysis and provides an interface for the language server. For multi-root workspaces, each workspace gets its own service instance.
The service owns an instance of a [program](https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/analyzer/program.ts), which tracks the configuration file and all of the source files that make up the source base that is to be analyzed. A source file can be added to a program if it is a) referenced by the config file, b) currently open in the editor, or c) imported directly or indirectly by another source file. The program object is responsible for setting up file system watchers and updating the program as files are added, deleted, or edited. The program is also responsible for prioritizing all phases of analysis for all files, favoring files that are open in the editor (and their import dependencies).
The program tracks multiple [sourceFile](https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/analyzer/sourceFile.ts) objects. Each source file represents the contents of one Python source file on disk. It tracks the status of analysis for the file, including any intermediate or final results of the analysis and the diagnostics (errors and warnings) that result.
The program makes use of an [importResolver](https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/analyzer/importResolver.ts) to resolve the imported modules referenced within each source file.
## Analysis Phases
Pyright performs the following analysis phases for each source file.
The [tokenizer](https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/parser/tokenizer.ts) is responsible for converting the files string contents into a stream of tokens. White space, comments, and some end-of-line characters are ignored, as they are not needed by the parser.
The [parser](https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/parser/parser.ts) is responsible for converting the token stream into a parse tree. A generalized [parseTreeWalker](https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/analyzer/parseTreeWalker.ts) provides a convenient way to traverse the parse tree. All subsequent analysis phases utilize the parseTreeWalker.
The [binder](https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/analyzer/binder.ts) is responsible for building scopes and populating the symbol table for each scope. It does not perform any type checking, but it detects and reports some semantic errors that will result in unintended runtime exceptions. It also detects and reports inconsistent name bindings (e.g. a variable that uses both a global and nonlocal binding in the same scope). The binder also builds a “reverse code flow graph” for each scope, allowing the type analyzer to determine a symbols type at any point in the code flow based on its antecedents.
The [checker](https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/analyzer/checker.ts) is responsible for checking all of the statements and expressions within a source file. It relies heavily on the [typeEvaluator](https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/analyzer/typeEvaluator.ts) module, which performs most of the heavy lifting. The checker doesnt run on all files, only those that require full diagnostic output. For example, if a source file is not part of the program but is imported by the program, the checker doesnt need to run on it.

435
docs/mypy-comparison.md Normal file
View File

@@ -0,0 +1,435 @@
## Differences Between Pyright and 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?
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 official [Python typing specification](https://typing.readthedocs.io/en/latest/spec/index.html), which incorporates and builds upon PEP 484 and other typing-related PEPs. The typing spec is accompanied by an ever-expanding suite of conformance tests. For the latest conformance test results for pyright, mypy and other type checkers, refer to [this page](https://htmlpreview.github.io/?https://github.com/python/typing/blob/main/conformance/results/results.html).
For behaviors that are not explicitly spelled out in the typing spec, 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
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.
Pyright was also designed to be used as the foundation for a Python [language server](https://microsoft.github.io/language-server-protocol/). Language servers provide interactive programming features such as completion suggestions, function signature help, type information on hover, semantic-aware search, semantic-aware renaming, semantic token coloring, refactoring tools, etc. For a good user experience, these features require highly responsive type evaluation performance during interactive code modification. They also require type evaluation to work on code that is incomplete and contains syntax errors.
To achieve these design goals, pyright is implemented as a “lazy” or “just-in-time” type evaluator. Rather than analyzing all code in a module from top to bottom, it is able to evaluate the type of an arbitrary identifier anywhere within a module. If the type of that identifier depends on the types of other expressions or symbols, pyright recursively evaluates those in turn until it has enough information to determine the type of the target identifier. By comparison, mypy uses a more traditional multi-pass architecture where semantic analysis is performed multiple times on a module from the top to the bottom until all types converge.
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. This also means that when you run mypy on an older version of Python, it cannot support newer language features that require grammar changes.
### 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
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
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).
```python
def func1(val: object):
if isinstance(val, str):
pass
elif isinstance(val, int):
pass
else:
return
reveal_type(val) # mypy: object, pyright: str | int
```
### 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.
Mypys behavior for variables depends on whether [`--allow-redefinition`](https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-allow-redefinition) is specified. If redefinitions are not allowed, then mypy typically treats the first assignment (the one with the smallest line number) as though it is an implicit type declaration.
```python
def func1(condition: bool):
if condition:
x = 3 # Mypy treats this as an implicit type declaration
else:
x = "" # Mypy treats this as an error because `x` is implicitly declared as `int`
def func2(condition: bool):
x = None # Mypy provides some exceptions; this is not considered an implicit type declaration
if condition:
x = "" # This is not considered an error
def func3(condition: bool):
x = [] # Mypy doesn't treat this as a declaration
if condition:
x = [1, 2, 3] # The type of `x` is declared as `list[int]`
```
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
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.
```python
class A:
def method1(self) -> None:
self.x = 1
def method2(self) -> None:
self.x = "" # Mypy treats this as an error because `x` is implicitly declared as `int`
a = A()
reveal_type(a.x) # pyright: int | str
a.x = "" # Pyright allows this because the type of `x` is `int | str`
a.x = 3.0 # Pyright treats this as an error because the type of `x` is `int | str`
```
### 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-advanced.md#class-and-instance-variables).
Mypy does not distinguish between class variables and instance variables in all cases. This is a [known issue](https://github.com/python/mypy/issues/240).
```python
class A:
x: int = 0 # Regular class variable
y: ClassVar[int] = 0 # Pure class variable
def __init__(self):
self.z = 0 # Pure instance variable
print(A.x)
print(A.y)
print(A.z) # pyright: error, mypy: no error
```
### 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).
```python
v1: Sequence[int]
v1 = [1, 2, 3]
reveal_type(v1) # mypy and pyright both reveal `list[int]`
v2: Sequence[int] = [1, 2, 3]
reveal_type(v2) # mypy reveals `Sequence[int]` rather than `list[int]`
```
### 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-advanced.md#type-guards).
The following expression forms are not currently supported by mypy as type guards:
* `x == L` and `x != L` (where L is an expression with a literal type)
* `x in y` or `x not in y` (where y is instance of list, set, frozenset, deque, tuple, dict, defaultdict, or OrderedDict)
* `bool(x)` (where x is any expression that is statically verifiable to be truthy or falsey in all cases)
### Aliased Conditional Expressions
Pyright supports the [aliasing of conditional expressions](type-concepts-advanced.md#aliased-conditional-expression) used for type guards. Mypy does not currently support this, but it is a frequently-requested feature.
### 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.
```python
b: list[Any]
b = [1, 2, 3]
reveal_type(b) # pyright: list[Any], mypy: list[Any]
c = [1, 2, 3]
b = c
reveal_type(b) # pyright: list[Any], mypy: list[int]
```
### 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.
```python
x = [1, 3.4, ""]
reveal_type(x) # mypy: list[object], pyright: list[Unknown] or list[int | float | str]
```
For these mutable container types, pyright does not retain literal types when inferring the container type. Mypy is inconsistent, sometimes retaining literal types and sometimes not.
```python
def func(one: Literal[1]):
reveal_type(one) # Literal[1]
reveal_type([one]) # pyright: list[int], mypy: list[Literal[1]]
reveal_type(1) # Literal[1]
reveal_type([1]) # pyright: list[int], mypy: list[int]
```
### 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.
```python
x = (1, "stop")
reveal_type(x[1]) # pyright: Literal["stop"], mypy: str
y: Literal["stop", "go"] = x[1] # mypy: type error
```
### 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.
```python
x: str | None
x = 'a'
reveal_type(x) # pyright: Literal['a'], mypy: str
```
Pyright also supports “literal math” for simple operations involving literals.
```python
def func1(a: Literal[1, 2], b: Literal[2, 3]):
c = a + b
reveal_type(c) # Literal[3, 4, 5]
def func2():
c = "hi" + " there"
reveal_type(c) # Literal['hi there']
```
### 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
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).
### Constructor Calls
When pyright evaluates a call to a constructor, it attempts to follow the runtime behavior as closely as possible. At runtime, when a constructor is called, it invokes the `__call__` method of the metaclass. Most classes use `type` as their metaclass. (Even when a different metaclasses is used, it typically does not override `type.__call__`.) The `type.__call__` method calls the `__new__` method for the class and passes all of the arguments (both positional and keyword) that were passed to the constructor call. If the `__new__` method returns an instance of the class (or a child class), `type.__call__` then calls the `__init__` method on the class. Pyright follows this same flow for evaluating the type of a constructor call. If a custom metaclass is present, pyright evaluates its `__call__` method to determine whether it returns an instance of the class. If not, it assumes that the metaclass has custom behavior that overrides `type.__call__`. Likewise, if a class provides a `__new__` method that returns a type other than the class being constructed (or a child class thereof), it assumes that `__init__` will not be called.
By comparison, mypy first evaluates the `__init__` method if present, and it ignores the annotated return type of the `__new__` method.
### `None` Return Type
If the return type of a function is declared as `None`, an attempt to call that function and consume the returned value is flagged as an error by mypy. The justification is that this is a common source of bugs.
Pyright does not special-case `None` in this manner because there are legitimate use cases, and in our experience, this class of bug is rare.
### 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.
Many aspects of constraint solving are unspecified in PEP 484. This includes behaviors around literals, whether to use unions or joins to widen types, and how to handle cases where multiple types could satisfy all type constraints.
#### Constraint Solver: Literals
Pyrights constraint solver retains literal types only when they are required to satisfy constraints. In other cases, it widens the type to a non-literal type. Mypy is inconsistent in its handling of literal types.
```python
T = TypeVar("T")
def identity(x: T) -> T:
return x
def func(one: Literal[1]):
reveal_type(one) # Literal[1]
v1 = identity(one)
reveal_type(v1) # pyright: int, mypy: Literal[1]
reveal_type(1) # Literal[1]
v2 = identity(1)
reveal_type(v2) # pyright: int, mypy: int
```
#### Constraint Solver: Type Widening
As mentioned previously, pyright always uses unions rather than joins. Mypy typically uses joins. This applies to type widening during the constraint solving process.
```python
T = TypeVar("T")
def func(val1: T, val2: T) -> T:
...
reveal_type(func("", 1)) # mypy: object, pyright: str | int
```
#### Constraint Solver: Ambiguous Solution Scoring
In cases where more than one solution is possible for a type variable, both pyright and mypy employ various heuristics to pick the “best” solution. These heuristics are complex and difficult to document in their fullness. Pyrights general strategy is to return the “simplest” type that meets the constraints.
Consider the expression `make_list(x)` in the example below. The type constraints for `T` could be satisfied with either `int` or `list[int]`, but its much more likely that the developer intended the former (simpler) solution. Pyright calculates all possible solutions and “scores” them according to complexity, then picks the type with the best score. In rare cases, there can be two results with the same score, in which chase pyright arbitrarily picks one as the winner.
Mypy produces errors with this sample.
```python
T = TypeVar("T")
def make_list(x: T | Iterable[T]) -> list[T]:
return list(x) if isinstance(x, Iterable) else [x]
def func2(x: list[int], y: list[str] | int):
v1 = make_list(x)
reveal_type(v1) # pyright: "list[int]" ("list[list[T]]" is also a valid answer)
v2 = make_list(y)
reveal_type(v2) # pyright: "list[int | str]" ("list[list[str] | int]" is also a valid answer)
```
### Value-Constrained Type Variables
When mypy analyzes a class or function that has in-scope value-constrained TypeVars, it analyzes the class or function multiple times, once for each constraint. This can produce multiple errors.
```python
T = TypeVar("T", list[Any], set[Any])
def func(a: AnyStr, b: T):
reveal_type(a) # Mypy reveals 2 different types ("str" and "bytes"), pyright reveals "AnyStr"
return a + b # Mypy reports 4 errors
```
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-advanced.md#conditional-types-and-type-variables). This approach allows pyright to handle some value-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
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.
Mypy does not track the difference between explicit and implicit `Any` types, but it supports various checks that report the use of values whose type is `Any`: `--warn-return-any` and `--disallow-any-*`. For details, refer to [this documentation](https://mypy.readthedocs.io/en/stable/command_line.html#disallow-dynamic-typing).
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 rules are under-specified in PEP 484. Pyright and mypy apply similar rules, but there are inevitably cases where different results will be produced. For full documentation of pyrights overload behaviors, refer to [this documentation](type-concepts-advanced.md#overloads).
One known difference is in the handling of ambiguous overloads due to `Any` argument types where one return type is the supertype of all other return types. In this case, pyright evaluates the resulting return type as the supertype, but mypy evaluates the return type as `Any`. Pyrights behavior here tries to preserve as much type information as possible, which is important for completion suggestions.
```python
@overload
def func1(x: int) -> int: ...
@overload
def func1(x: str) -> float: ...
def func2(val: Any):
reveal_type(func1(val)) # mypy: Any, pyright: float
```
### 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).
Mypy models side effects of the import loader that are potentially unsafe.
```python
import http
def func():
import http.cookies
# The next line raises an exception at runtime
x = http.cookies # mypy allows, pyright flags as error
```
### 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
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.
1. A class declaration that references a metaclass whose declaration depends on the class.
```python
T = TypeVar("T")
class MetaA(type, Generic[T]): ...
class A(metaclass=MetaA["A"]): ...
```
2. A class declaration that uses a TypeVar whose bound or constraint depends on the class.
```python
T = TypeVar("T", bound="A")
class A(Generic[T]): ...
```
3. A class that is decorated with a class decorator that uses the class in the decorators own signature.
```python
def my_decorator(x: Callable[..., "A"]) -> Callable[..., "A"]:
return x
@my_decorator
class A: ...
```
### 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
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.
Mypy has full support for type comments. Pyright supports type comments only in locations where there is a way to provide an annotation using modern syntax. Pyright was written to assume Python 3.5 and newer, so support for older versions was not a priority.
```python
# The following type comment is supported by
# mypy but is rejected by pyright.
x, y = (3, 4) # type: (float, float)
# Using Python syntax from Python 3.6, this
# would be annotated as follows:
x: float
y: float
x, y = (3, 4)
```
### Plugins
Mypy supports a plug-in mechanism, whereas pyright does not. Mypy plugins allow developers to extend mypys capabilities to accommodate libraries that rely on behaviors that cannot be described using the standard type checking mechanisms.
Pyright maintainers have made the decision not to support plug-ins because of their many downsides: discoverability, maintainability, cost of development for the plug-in author, cost of maintenance for the plug-in object model and API, security, performance (especially latency — which is critical for language servers), and robustness. Instead, we have taken the approach of working with the typing community and library authors to extend the type system so it can accommodate more use cases. An example of this is [PEP 681](https://peps.python.org/pep-0681/), which introduced `dataclass_transform`.

44
docs/settings.md Normal file
View File

@@ -0,0 +1,44 @@
## Pyright Settings
The Pyright language server honors the following settings.
**pyright.disableLanguageServices** [boolean]: Disables all language services. This includes hover text, type completion, signature completion, find definition, find references, etc. This option is useful if you want to use pyright only as a type checker but want to run another Python language server for language service features.
**pyright.disableOrganizeImports** [boolean]: Disables the “Organize Imports” command. This is useful if you are using another extension that provides similar functionality and you dont want the two extensions to fight each other.
**pyright.disableTaggedHints** [boolean]: Disables the use of hint diagnostics with special tags to tell the client to display text ranges in a "grayed out" manner (to indicate unreachable code or unreferenced symbols) or in a "strike through" manner (to indicate use of a deprecated feature).
**pyright.openFilesOnly** [boolean]: Determines whether pyright analyzes (and reports errors for) all files in the workspace, as indicated by the config file. If this option is set to true, pyright analyzes only open files. This setting is deprecated in favor of python.analysis.diagnosticMode. It will be removed at a future time.
**pyright.useLibraryCodeForTypes** [boolean]: This setting is deprecated in favor of python.analysis.useLibraryCodeForTypes. It will be removed at a future time.
**python.analysis.autoImportCompletions** [boolean]: Determines whether pyright offers auto-import completions.
**python.analysis.autoSearchPaths** [boolean]: Determines whether pyright automatically adds common search paths like "src" if there are no execution environments defined in the config file.
**python.analysis.diagnosticMode** ["openFilesOnly", "workspace"]: Determines whether pyright analyzes (and reports errors for) all files in the workspace, as indicated by the config file. If this option is set to "openFilesOnly", pyright analyzes only open files.
**python.analysis.diagnosticSeverityOverrides** [map]: Allows a user to override the severity levels for individual diagnostic rules. "reportXXX" rules in the type check diagnostics settings in [configuration](configuration.md#type-check-diagnostics-settings) are supported. Use the rule name as a key and one of "error," "warning," "information," "true," "false," or "none" as value.
**python.analysis.exclude** [array of paths]: Paths of directories or files that should not be included. This can be overridden in the configuration file.
**python.analysis.extraPaths** [array of paths]: Paths to add to the default execution environment extra paths if there are no execution environments defined in the config file.
**python.analysis.ignore** [array of paths]: Paths of directories or files whose diagnostic output (errors and warnings) should be suppressed. This can be overridden in the configuration file.
**python.analysis.include** [array of paths]: Paths of directories or files that should be included. This can be overridden in the configuration file.
**python.analysis.logLevel** ["Error", "Warning", "Information", or "Trace"]: Level of logging for Output panel. The default value for this option is "Information".
**python.analysis.stubPath** [path]: Path to directory containing custom type stub files.
**python.analysis.typeCheckingMode** ["off", "basic", "standard", "strict"]: Determines the default type-checking level used by pyright. This can be overridden in the configuration file. (Note: This setting used to be called "pyright.typeCheckingMode". The old name is deprecated but is still currently honored.)
**python.analysis.typeshedPaths** [array of paths]: Paths to look for typeshed modules. Pyright currently honors only the first path in the array.
**python.analysis.useLibraryCodeForTypes** [boolean]: Determines whether pyright reads, parses and analyzes library code to extract type information in the absence of type stub files. Type information will typically be incomplete. We recommend using type stubs where possible. The default value for this option is true.
**python.pythonPath** [path]: Path to Python interpreter. This setting is being deprecated by the VS Code Python extension in favor of a setting that is stored in the Python extensions internal configuration store. Pyright supports both mechanisms but prefers the new one if both settings are present.
**python.venvPath** [path]: Path to folder with subdirectories that contain virtual environments. The `python.pythonPath` setting is recommended over this mechanism for most users. For more details, refer to the [import resolution](import-resolution.md#configuring-your-python-environment) documentation.

View File

@@ -0,0 +1,660 @@
## Static Typing: Advanced Topics
### Type Narrowing
Pyright uses a technique called “type narrowing” to track the type of an expression based on code flow. Consider the following code:
```python
val_str: str = "hi"
val_int: int = 3
def func(val: float | str | complex, test: bool):
reveal_type(val) # int | str | complex
val = val_int # Type is narrowed to int
reveal_type(val) # int
if test:
val = val_str # Type is narrowed to str
reveal_type(val) # str
reveal_type(val) # int | str
if isinstance(val, int):
reveal_type(val) # int
print(val)
else:
reveal_type(val) # str
print(val)
```
At the start of this function, the type checker knows nothing about `val` other than that its declared type is `float | str | complex`. Then it is assigned a value that has a known type of `int`. This is a legal assignment because `int` is considered a subclass of `float`. At the point in the code immediately after the assignment, the type checker knows that the type of `val` is an `int`. This is a “narrower” (more specific) type than `float | str | complex`. Type narrowing is applied whenever a symbol is assigned a new value.
Another assignment occurs several lines further down, this time within a conditional block. The symbol `val` is assigned a value known to be of type `str`, so the narrowed type of `val` is now `str`. Once the code flow of the conditional block merges with the main body of the function, the narrowed type of `val` becomes `int | str` because the type checker cannot statically predict whether the conditional block will be executed at runtime.
Another way that types can be narrowed is through the use of conditional code flow statements like `if`, `while`, and `assert`. Type narrowing applies to the block of code that is “guarded” by that condition, so type narrowing in this context is sometimes referred to as a “type guard”. For example, if you see the conditional statement `if x is None:`, the code within that `if` statement can assume that `x` contains `None`. Within the code sample above, we see an example of a type guard involving a call to `isinstance`. The type checker knows that `isinstance(val, int)` will return True only in the case where `val` contains a value of type `int`, not type `str`. So the code within the `if` block can assume that `val` contains a value of type `int`, and the code within the `else` block can assume that `val` contains a value of type `str`. This demonstrates how a type (in this case `int | str`) can be narrowed in both a positive (`if`) and negative (`else`) test.
The following expression forms support type narrowing:
* `<ident>` (where `<ident>` is an identifier)
* `<expr>.<member>` (member access expression where `<expr>` is a supported expression form)
* `<ident> := <expr>` (assignment expression where `<expr>` is a supported expression form)
* `<expr>[<int>]` (subscript expression where `<int>` is a non-negative integer)
* `<expr>[<str>]` (subscript expression where `<str>` is a string literal)
Examples of expressions that support type narrowing:
* `my_var`
* `employee.name`
* `a.foo.next`
* `args[3]`
* `kwargs["bar"]`
* `a.b.c[3]["x"].d`
### Type Guards
In addition to assignment-based type narrowing, Pyright supports the following type guards.
* `x is None` and `x is not None`
* `x == None` and `x != None`
* `x is ...` and `x is not ...` (where `...` is an ellipsis token)
* `x == ...` and `x != ...` (where `...` is an ellipsis token)
* `x is S` and `x is not S` (where S is a Sentinel)
* `type(x) is T` and `type(x) is not T`
* `type(x) == T` and `type(x) != T`
* `x is L` and `x is not L` (where L is an expression that evaluates to a literal type)
* `x is C` and `x is not C` (where C is a class)
* `x == L` and `x != L` (where L is an expression that evaluates to a literal type)
* `x.y is None` and `x.y is not None` (where x is a type that is distinguished by a field with a None)
* `x.y is E` and `x.y is not E` (where E is a literal enum or bool and x is a type that is distinguished by a field with a literal type)
* `x.y == LN` and `x.y != LN` (where LN is a literal expression or `None` and x is a type that is distinguished by a field or property with a literal type)
* `x[K] == V`, `x[K] != V`, `x[K] is V`, and `x[K] is not V` (where K and V are literal expressions and x is a type that is distinguished by a TypedDict field with a literal type)
* `x[I] == V` and `x[I] != V` (where I and V are literal expressions and x is a known-length tuple that is distinguished by the index indicated by I)
* `x[I] is B` and `x[I] is not B` (where I is a literal expression, B is a `bool` or enum literal, and x is a known-length tuple that is distinguished by the index indicated by I)
* `x[I] is None` and `x[I] is not None` (where I is a literal expression and x is a known-length tuple that is distinguished by the index indicated by I)
* `len(x) == L`, `len(x) != L`, `len(x) < L`, etc. (where x is tuple and L is an expression that evaluates to an int literal type)
* `x in y` or `x not in y` (where y is instance of list, set, frozenset, deque, tuple, dict, defaultdict, or OrderedDict)
* `S in D` and `S not in D` (where S is a string literal and D is a TypedDict)
* `isinstance(x, T)` (where T is a type or a tuple of types)
* `issubclass(x, T)` (where T is a type or a tuple of types)
* `f(x)` (where f is a user-defined type guard as defined in [PEP 647](https://www.python.org/dev/peps/pep-0647/) or [PEP 742](https://www.python.org/dev/peps/pep-0742))
* `bool(x)` (where x is any expression that is statically verifiable to be truthy or falsey in all cases)
* `x` (where x is any expression that is statically verifiable to be truthy or falsey in all cases)
Expressions supported for type guards include simple names, member access chains (e.g. `a.b.c.d`), the unary `not` operator, the binary `and` and `or` operators, subscripts that are integer literals (e.g. `a[2]` or `a[-1]`), and call expressions. Other operators (such as arithmetic operators or other subscripts) are not supported.
Some type guards are able to narrow in both the positive and negative cases. Positive cases are used in `if` statements, and negative cases are used in `else` statements. (Positive and negative cases are flipped if the type guard expression is preceded by a `not` operator.) In some cases, the type can be narrowed only in the positive or negative case but not both. Consider the following examples:
```python
class Foo: pass
class Bar: pass
def func1(val: Foo | Bar):
if isinstance(val, Bar):
reveal_type(val) # Bar
else:
reveal_type(val) # Foo
def func2(val: float | None):
if val:
reveal_type(val) # float
else:
reveal_type(val) # float | None
```
In the example of `func1`, the type was narrowed in both the positive and negative cases. In the example of `func2`, the type was narrowed only the positive case because the type of `val` might be either `float` (specifically, a value of 0.0) or `None` in the negative case.
### Aliased Conditional Expression
Pyright also supports a type guard expression `c`, where `c` is an identifier that refers to a local variable that is assigned one of the above supported type guard expression forms. These are called “aliased conditional expressions”. Examples include `c = a is not None` and `c = isinstance(a, str)`. When “c” is used within a conditional check, it can be used to narrow the type of expression `a`.
This pattern is supported only in cases where `c` is a local variable within a module or function scope and is assigned a value only once. It is also limited to cases where expression `a` is a simple identifier (as opposed to a member access expression or subscript expression), is local to the function or module scope, and is assigned only once within the scope. Unary `not` operators are allowed for expression `a`, but binary `and` and `or` are not.
```python
def func1(x: str | None):
is_str = x is not None
if is_str:
reveal_type(x) # str
else:
reveal_type(x) # None
```
```python
def func2(val: str | bytes):
is_str = not isinstance(val, bytes)
if not is_str:
reveal_type(val) # bytes
else:
reveal_type(val) # str
```
```python
def func3(x: list[str | None]) -> str:
is_str = x[0] is not None
if is_str:
# This technique doesn't work for subscript expressions,
# so x[0] is not narrowed in this case.
reveal_type(x[0]) # str | None
```
```python
def func4(x: str | None):
is_str = x is not None
if is_str:
# This technique doesn't work in cases where the target
# expression is assigned elsewhere. Here `x` is assigned
# elsewhere in the function, so its type is not narrowed
# in this case.
reveal_type(x) # str | None
x = ""
```
### Narrowing for Implied Else
When an “if” or “elif” clause is used without a corresponding “else”, Pyright will generally assume that the code can “fall through” without executing the “if” or “elif” block. However, there are cases where the analyzer can determine that a fall-through is not possible because the “if” or “elif” is guaranteed to be executed based on type analysis.
```python
def func1(x: int):
if x == 1 or x == 2:
y = True
print(y) # Error: "y" is possibly unbound
def func2(x: Literal[1, 2]):
if x == 1 or x == 2:
y = True
print(y) # No error
```
This can be especially useful when exhausting all members in an enum or types in a union.
```python
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"
def func4(value: str | int) -> str:
if isinstance(value, str):
return "received a str"
elif isinstance(value, int):
return "received an int"
```
If you later added another color to the `Color` enumeration above (e.g. `YELLOW = 4`), Pyright would detect that `func3` no longer exhausts all members of the enumeration and possibly returns `None`, which violates the declared return type. Likewise, if you modify the type of the `value` parameter in `func4` to expand the union, a similar error will be produced.
This “narrowing for implied else” technique works for all narrowing expressions listed above with the exception of simple falsey/truthy statements and type guards. It is also limited to simple names and doesnt work with member access or index expressions, and it requires that the name has a declared type (an explicit type annotation). These limitations are imposed because this functionality would otherwise have significant impact on analysis performance.
### Narrowing Any
In general, the type `Any` is not narrowed. The only exceptions to this rule are the built-in `isinstance` and `issubclass` type guards, class pattern matching in “match” statements, and user-defined type guards. In all other cases, `Any` is left as is, even for assignments.
```python
a: Any = 3
reveal_type(a) # Any
a = "hi"
reveal_type(a) # Any
```
The same applies to `Any` when it is used as a type argument.
```python
b: Iterable[Any] = [1, 2, 3]
reveal_type(b) # list[Any]
c: Iterable[str] = [""]
b = c
reveal_type(b) # list[Any]
```
### Narrowing for Captured Variables
If a variables type is narrowed in an outer scope and the variable is subsequently captured by an inner-scoped function or lambda, Pyright retains the narrowed type if it can determine that the value of the captured variable is not modified on any code path after the inner-scope function or lambda is defined and is not modified in another scope via a `nonlocal` or `global` binding.
```python
def func(val: int | None):
if val is not None:
def inner_1() -> None:
reveal_type(val) # int
print(val + 1)
inner_2 = lambda: reveal_type(val) + 1 # int
inner_1()
inner_2()
```
### Value-Constrained Type Variables
When a TypeVar is defined, it can be constrained to two or more types (values).
```python
# Example of unconstrained type variable
_T = TypeVar("_T")
# Example of value-constrained type variables
_StrOrFloat = TypeVar("_StrOrFloat", str, float)
```
When a value-constrained TypeVar appears more than once within a function signature, the type provided for all instances of the TypeVar must be consistent.
```python
def add(a: _StrOrFloat, b: _StrOrFloat) -> _StrOrFloat:
return a + b
# The arguments for `a` and `b` are both `str`
v1 = add("hi", "there")
reveal_type(v1) # str
# The arguments for `a` and `b` are both `float`
v2 = add(1.3, 2.4)
reveal_type(v2) # float
# The arguments for `a` and `b` are inconsistent types
v3 = add(1.3, "hi") # Error
```
### Conditional Types and Type Variables
When checking the implementation of a function that uses type variables in its signature, the type checker must verify that type consistency is guaranteed. Consider the following example, where the input parameter and return type are both annotated with a type variable. The type checker must verify that if a caller passes an argument of type `str`, then all code paths must return a `str`. Likewise, if a caller passes an argument of type `float`, all code paths must return a `float`.
```python
def add_one(value: _StrOrFloat) -> _StrOrFloat:
if isinstance(value, str):
sum = value + "1"
else:
sum = value + 1
reveal_type(sum) # str* | float*
return sum
```
The type of variable `sum` is reported with a star (`*`). This indicates that internally the type checker is tracking the type as a “conditional” type. In this particular example, it indicates that `sum` is a `str` type if the parameter `value` is a `str` but is a `float` if `value` is a `float`. By tracking these conditional types, the type checker can verify that the return type is consistent with the return type `_StrOrFloat`. Conditional types are a form of _intersection_ type, and they are considered subtypes of both the concrete type and the type variable.
### Inferred Type of “self” and “cls” Parameters
When a type annotation for a methods `self` or `cls` parameter is omitted, pyright will infer its type based on the class that contains the method. The inferred type is internally represented as a type variable that is bound to the class.
The type of `self` is represented as `Self@ClassName` where `ClassName` is the class that contains the method. Likewise, the `cls` parameter in a class method will have the type `Type[Self@ClassName]`.
```python
class Parent:
def method1(self):
reveal_type(self) # Self@Parent
return self
@classmethod
def method2(cls):
reveal_type(cls) # Type[Self@Parent]
return cls
class Child(Parent):
...
reveal_type(Child().method1()) # Child
reveal_type(Child.method2()) # Type[Child]
```
### Overloads
Some functions or methods can return one of several different types. In cases where the return type depends on the types of the input arguments, it is useful to specify this using a series of `@overload` signatures. When Pyright evaluates a call expression, it determines which overload signature best matches the supplied arguments.
[PEP 484](https://www.python.org/dev/peps/pep-0484/#function-method-overloading) introduced the `@overload` decorator and described how it can be used, but the PEP did not specify precisely how a type checker should choose the “best” overload. Pyright uses the following rules.
1. Pyright first filters the list of overloads based on simple “arity” (number of arguments) and keyword argument matching. For example, if one overload requires two positional arguments but only one positional argument is supplied by the caller, that overload is eliminated from consideration. Likewise, if the call includes a keyword argument but no corresponding parameter is included in the overload, it is eliminated from consideration.
2. Pyright next considers the types of the arguments and compares them to the declared types of the corresponding parameters. If the types do not match for a given overload, that overload is eliminated from consideration. Bidirectional type inference is used to determine the types of the argument expressions.
3. If only one overload remains, it is the “winner”.
4. If more than one overload remains, the “winner” is chosen based on the order in which the overloads are declared. In general, the first remaining overload is the “winner”. There are two exceptions to this rule.
Exception 1: When an `*args` (unpacked) argument matches a `*args` parameter in one of the overload signatures, this overrides the normal order-based rule.
Exception 2: When two or more overloads match because an argument evaluates to `Any` or `Unknown`, the matching overload is ambiguous. In this case, pyright examines the return types of the remaining overloads and eliminates types that are duplicates or are subsumed by (i.e. proper subtypes of) other types in the list. If only one type remains after this coalescing step, that type is used. If more than one type remains after this coalescing step, the type of the call expression evaluates to `Unknown`. For example, if two overloads are matched due to an argument that evaluates to `Any`, and those two overloads have return types of `str` and `LiteralString`, pyright will coalesce this to just `str` because `LiteralString` is a proper subtype of `str`. If the two overloads have return types of `str` and `bytes`, the call expression will evaluate to `Unknown` because `str` and `bytes` have no overlap.
5. If no overloads remain, Pyright considers whether any of the arguments are union types. If so, these union types are expanded into their constituent subtypes, and the entire process of overload matching is repeated with the expanded argument types. If two or more overloads match, the union of their respective return types form the final return type for the call expression. This "union expansion" can result in a combinatoric explosion if many arguments evaluate to union types. For example, if four arguments are present, and they all evaluate to unions that expand to ten subtypes, this could result in 10^4 combinations. Pyright expands unions for arguments left to right and halts expansion when the number of signatures exceeds 64.
6. If no overloads remain and all unions have been expanded, a diagnostic is generated indicating that the supplied arguments are incompatible with all overload signatures.
### Class and Instance Variables
Most object-oriented languages clearly differentiate between class variables and instance variables. Python is a bit looser in that it allows an object to overwrite a class variable with an instance variable of the same name.
```python
class A:
my_var = 0
def my_method(self):
self.my_var = "hi!"
a = A()
print(A.my_var) # Class variable value of 0
print(a.my_var) # Class variable value of 0
A.my_var = 1
print(A.my_var) # Updated class variable value of 1
print(a.my_var) # Updated class variable value of 1
a.my_method() # Writes to the instance variable my_var
print(A.my_var) # Class variable value of 1
print(a.my_var) # Instance variable value of "hi!"
A.my_var = 2
print(A.my_var) # Updated class variable value of 2
print(a.my_var) # Instance variable value of "hi!"
```
Pyright differentiates between three types of variables: pure class variables, regular class variables, and pure instance variables.
#### Pure Class Variables
If a class variable is declared with a `ClassVar` annotation as described in [PEP 526](https://peps.python.org/pep-0526/#class-and-instance-variable-annotations), it is considered a “pure class variable” and cannot be overwritten by an instance variable of the same name.
```python
from typing import ClassVar
class A:
x: ClassVar[int] = 0
def instance_method(self):
self.x = 1 # Type error: Cannot overwrite class variable
@classmethod
def class_method(cls):
cls.x = 1
a = A()
print(A.x)
print(a.x)
A.x = 1
a.x = 2 # Type error: Cannot overwrite class variable
```
#### Regular Class Variables
If a class variable is declared without a `ClassVar` annotation, it can be overwritten by an instance variable of the same name. The declared type of the instance variable is assumed to be the same as the declared type of the class variable.
Regular class variables can also be declared within a class method using a `cls` member access expression, but declaring regular class variables within the class body is more common and generally preferred for readability.
```python
class A:
x: int = 0
y: int
def instance_method(self):
self.x = 1
self.y = 2
@classmethod
def class_method(cls):
cls.z: int = 3
A.y = 0
A.z = 0
print(f"{A.x}, {A.y}, {A.z}") # 0, 0, 0
A.class_method()
print(f"{A.x}, {A.y}, {A.z}") # 0, 0, 3
a = A()
print(f"{a.x}, {a.y}, {a.z}") # 0, 0, 3
a.instance_method()
print(f"{a.x}, {a.y}, {a.z}") # 1, 2, 3
a.x = "hi!" # Error: Incompatible type
```
#### Pure Instance Variables
If a variable is not declared within the class body but is instead declared within a class method using a `self` member access expression, it is considered a “pure instance variable”. Such variables cannot be accessed through a class reference.
```python
class A:
def __init__(self):
self.x: int = 0
self.y: int
print(A.x) # Error: 'x' is not a class variable
a = A()
print(a.x)
a.x = 1
a.y = 2
print(f"{a.x}, {a.y}") # 1, 2
print(a.z) # Error: 'z' is not an known member
```
#### Inheritance of Class and Instance Variables
Class and instance variables are inherited from parent classes. If a parent class declares the type of a class or instance variable, a derived class must honor that type when assigning to it.
```python
class Parent:
x: int | str | None
y: int
class Child(Parent):
x = "hi!"
y = None # Error: Incompatible type
```
The derived class can redeclare the type of a class or instance variable. If `reportIncompatibleVariableOverride` is enabled, the redeclared type must be the same as the type declared by the parent class. If the variable is immutable (as in a frozen `dataclass`), it is considered covariant, and it can be redeclared as a subtype of the type declared by the parent class.
```python
class Parent:
x: int | str | None
y: int
class Child(Parent):
x: int # Type error: 'x' cannot be redeclared with subtype because variable is mutable and therefore invariant
y: str # Type error: 'y' cannot be redeclared with an incompatible type
```
If a parent class declares the type of a class or instance variable and a derived class does not redeclare it but does assign a value to it, the declared type is retained from the parent class. It is not overridden by the inferred type of the assignment in the derived class.
```python
class Parent:
x: object
class Child(Parent):
x = 3
reveal_type(Parent.x) # object
reveal_type(Child.x) # object
```
If neither the parent nor the derived class declare the type of a class or instance variable, the type is inferred within each class.
```python
class Parent:
x = object()
class Child(Parent):
x = 3
reveal_type(Parent.x) # object
reveal_type(Child.x) # int
```
#### Type Variable Scoping
A type variable must be bound to a valid scope (a class, function, or type alias) before it can be used within that scope.
Pyright displays the bound scope for a type variable using an `@` symbol. For example, `T@func` means that type variable `T` is bound to function `func`.
```python
S = TypeVar("S")
T = TypeVar("T")
def func(a: T) -> T:
b: T = a # T refers to T@func
reveal_type(b) # T@func
c: S # Error: S has no bound scope in this context
return b
```
When a TypeVar or ParamSpec appears within parameter or return type annotations for a function and it is not already bound to an outer scope, it is normally bound to the function. As an exception to this rule, if the TypeVar or ParamSpec appears only within the return type annotation of the function and only within a single Callable in the return type, it is bound to that Callable rather than the function. This allows a function to return a generic Callable.
```python
# T is bound to func1 because it appears in a parameter type annotation.
def func1(a: T) -> Callable[[T], T]:
a: T # OK because T is bound to func1
# T is bound to the return callable rather than func2 because it appears
# only within a return Callable.
def func2() -> Callable[[T], T]:
a: T # Error because T has no bound scope in this context
# T is bound to func3 because it appears outside of a Callable.
def func3() -> Callable[[T], T] | T:
...
# This scoping logic applies also to type aliases used within a return
# type annotation. T is bound to the return Callable rather than func4.
Transform = Callable[[S], S]
def func4() -> Transform[T]:
...
```
### Type Annotation Comments
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
offsets = [] # type: list[int]
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.
### Literal Math Inference
When inferring the type of some unary and binary operations that involve operands with literal types, pyright computes the result of operations on the literal values, producing a new literal type in the process. For example:
```python
def func(x: Literal[1, 3], y: Literal[4, 7]):
z = x + y
reveal_type(z) # Literal[5, 8, 7, 10]
z = x * y
reveal_type(z) # Literal[4, 7, 12, 21]
z = (x | y) ^ 1
reveal_type(z) # Literal[4, 6]
z = x ** y
reveal_type(z) # Literal[1, 81, 2187]
```
Literal math also works on `str` literals.
```python
reveal_type("a" + "b") # Literal["ab"]
```
The result of a literal math operation can result in large unions. Pyright limits the number of subtypes in the resulting union to 64. If the union grows beyond that, the corresponding non-literal type is inferred.
```python
def func(x: Literal[1, 2, 3, 4, 5]):
y = x * x
reveal_type(y) # Literal[1, 2, 3, 4, 5, 6, 8, 10, 9, 12, 15, 16, 20, 25]
z = y * x
reveal_type(z) # int
```
Literal math inference is disabled within loops and lambda expressions.
### Static Conditional Evaluation
Pyright performs static evaluation of several conditional expression forms. This includes several forms that are mandated by the [Python typing spec](https://typing.readthedocs.io/en/latest/spec/directives.html#version-and-platform-checking).
* `sys.version_info <comparison> <tuple>`
* `sys.version_info[0] >= <number>`
* `sys.platform == <string literal>`
* `os.name == <string literal>`
* `typing.TYPE_CHECKING` or `typing_extensions.TYPE_CHECKING`
* `True` or `False`
* An identifier defined with the "defineConstant" configuration option
* A `not` unary operator with any of the above forms
* An `and` or `or` binary operator with any of the above forms
If one of these conditional expressions evaluates statically to false, pyright does not analyze any of the code within it other than checking for and reporting syntax errors.
### Reachability
Pyright performs “reachability analysis” to determine whether statements will be executed at runtime and whether it should analyze and report errors in code.
Reachability analysis is based on both non-type and type information. Non-type information includes statements that affect code structure such as `continue`, `raise` and `return`. It also includes conditional statements (`if`, `elif`, or `while`) where the conditional expression is one of these [supported expression forms](type-concepts-advanced#static-conditional-evaluation). Type analysis is not performed on code determined to be unreachable using non-type information. Therefore, language server features like completion suggestions are not available for this code.
Here are some examples of code determined to be unreachable using non-type information.
```python
from typing import TYPE_CHECKING
import sys
if False:
print('unreachable')
if not TYPE_CHECKING:
print('unreachable')
if sys.version_info < (3, 0):
print('unreachable')
if sys.platform == 'ENIAC':
print('unreachable')
def func1():
return
print('unreachable')
def func2():
raise NotImplemented
print('unreachable')
```
Pyright can also detect code that is unreachable based on static type analysis. This analysis is based on the assumption that any provided type annotations are accurate.
Here are some examples of code determined to be unreachable using type analysis.
```python
from typing import Literal, NoReturn
def always_raise() -> NoReturn:
raise ValueError
def func1():
always_raise()
print('unreachable')
def func2(x: str):
if not isinstance(x, str):
print('unreachable')
def func3(x: Literal[1, 2]):
if x == 1 or x == 2:
return
print("unreachable")
```
Code that is determined to be unreachable is reported through the use of “tagged hints”. These are special diagnostics that tell a language client to display the code in a visually distinctive manner, typically with a grayed-out appearance. Code determined to be unreachable using non-type information is always reported through this mechanism. Code determined to be unreachable using type analysis is reported only if “enableReachabilityAnalysis” is enabled in the configuration.

104
docs/type-concepts.md Normal file
View File

@@ -0,0 +1,104 @@
## Static Typing: The Basics
Getting started with static type checking in Python is easy, but its important to understand a few simple concepts. In addition to the documentation below, you may also find the community-maintained [Static Typing Documentation](https://typing.readthedocs.io/en/latest/) to be of use. That site also includes the official [Specification for the Python Type System](https://typing.readthedocs.io/en/latest/spec/index.html).
### Type Declarations
When you add a type annotation to a variable or a parameter in Python, you are _declaring_ that the symbol will be assigned values that are compatible with that type. You can think of type annotations as a powerful way to comment your code. Unlike text-based comments, these comments are readable by both humans and enforceable by type checkers.
If a variable or parameter has no type annotation, Pyright will assume that any value can be assigned to it.
### Type Assignability
When your code assigns a value to a symbol (in an assignment expression) or a parameter (in a call expression), the type checker first determines the type of the value being assigned. It then determines whether the target has a declared type. If so, it verifies that the type of the value is _assignable_ to the declared type.
Lets look at a few simple examples. In this first example, the declared type of `a` is `float`, and it is assigned a value that is an `int`. This is permitted because `int` is assignable to `float`.
```python
a: float = 3
```
In this example, the declared type of `b` is `int`, and it is assigned a value that is a `float`. This is flagged as an error because `float` is not assignable to `int`.
```python
b: int = 3.4 # Error
```
This example introduces the notion of a _Union type_, which specifies that a value can be one of several distinct types. A union type can be expressed using the `|` operator to combine individual types.
```python
c: int | float = 3.4
c = 5
c = a
c = b
c = None # Error
c = "" # Error
```
This example introduces the _Optional_ type, which is the same as a union with `None`.
```python
d: Optional[int] = 4
d = b
d = None
d = "" # Error
```
Those examples are straightforward. Lets look at one that is less intuitive. In this example, the declared type of `f` is `list[int | None]`. A value of type `list[int]` is being assigned to `f`. As we saw above, `int` is assignable to `int | None`. You might therefore assume that `list[int]` is assignable to `list[int | None]`, but this is an incorrect assumption. To understand why, we need to understand generic types and type arguments.
```python
e: list[int] = [3, 4]
f: list[int | None] = e # Error
```
### Generic Types
A _generic type_ is a class that is able to handle different types of inputs. For example, the `list` class is generic because it is able to operate on different types of elements. The type `list` by itself does not specify what is contained within the list. Its element type must be specified as a _type argument_ using the indexing (square bracket) syntax in Python. For example, `list[int]` denotes a list that contains only `int` elements whereas `list[int | float]` denotes a list that contains a mixture of int and float elements.
We noted above that `list[int]` is not assignable to `list[int | None]`. Why is this the case? Consider the following example.
```python
my_list_1: list[int] = [1, 2, 3]
my_list_2: list[int | None] = my_list_1 # Error
my_list_2.append(None)
for elem in my_list_1:
print(elem + 1) # Runtime exception
```
The code is appending the value `None` to the list `my_list_2`, but `my_list_2` refers to the same object as `my_list_1`, which has a declared type of `list[int]`. The code has violated the type of `my_list_1` because it no longer contains only `int` elements. This broken assumption results in a runtime exception. The type checker detects this broken assumption when the code attempts to assign `my_list_1` to `my_list_2`.
`list` is an example of a _mutable container type_. It is mutable in that code is allowed to modify its contents — for example, add or remove items. The type parameters for mutable container types are typically marked as _invariant_, which means that an exact type match is enforced. This is why the type checker reports an error when attempting to assign a `list[int]` to a variable of type `list[int | None]`.
Most mutable container types also have immutable counterparts.
| Mutable Type | Immutable Type |
| ----------------- | -------------- |
| list | Sequence |
| dict | Mapping |
| set | Container |
| n/a | tuple |
Switching from a mutable container type to a corresponding immutable container type is often an effective way to resolve type errors relating to assignability. Lets modify the example above by changing the type annotation for `my_list_2`.
```python
my_list_1: list[int] = [1, 2, 3]
my_list_2: Sequence[int | None] = my_list_1 # No longer an error
```
The type error on the second line has now gone away.
For more details about generic types, type parameters, and invariance, refer to [PEP 483 — The Theory of Type Hints](https://www.python.org/dev/peps/pep-0483/).
### Debugging Types
When you want to know the type that the type checker has evaluated for an expression, you can use the special `reveal_type()` function:
```python
x = 1
reveal_type(x) # Type of "x" is "Literal[1]"
```
This function is always available and does not need to be imported. When you use Pyright within an IDE, you can also simply hover over an identifier to see its evaluated type.

373
docs/type-inference.md Normal file
View File

@@ -0,0 +1,373 @@
## Understanding Type Inference
### 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.
Symbols are defined within _scopes_. A scope is associated with a block of code and defines which symbols are visible to that code block. Scopes can be “nested” allowing code to see symbols within its immediate scope and all “outer” scopes.
The following constructs within Python define a scope:
1. The “builtins” scope is always present and is always the outermost scope. It is pre-populated by the Python interpreter with symbols like “int” and “list”.
2. The module scope (sometimes called the “global” scope) is defined by the current source code file.
3. Each class defines its own scope. Symbols that represent methods, class variables, or instance variables appear within a class 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
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.
When a parameter or variable is annotated with a type, the type checker verifies that all values assigned to that parameter or variable conform to that type.
Consider the following example:
```python
def func1(p1: float, p2: str, p3, **p4) -> None:
var1: int = p1 # This is a type violation
var2: str = p2 # This is allowed because the types match
var2: int # This is an error because it redeclares var2
var3 = p1 # var3 does not have a declared type
return var1 # This is a type violation
```
Symbol | Symbol Category | Scope | Declared Type
----------|-----------------|-----------|----------------------------------------------------
func1 | Function | Module | (float, str, Any, dict[str, Any]) -> None
p1 | Parameter | func1 | float
p2 | Parameter | func1 | str
p3 | Parameter | func1 | <none>
p4 | Parameter | func1 | <none>
var1 | Variable | func1 | int
var2 | Variable | func1 | str
var3 | Variable | func1 | <none>
Note that once a symbols type is declared, it cannot be redeclared to a different type.
### 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
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
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:
```python
var1 = 3 # Inferred type is int
var2 = "hi" # Inferred type is str
var3 = list() # Inferred type is list[Unknown]
var4 = [3, 4] # Inferred type is list[int]
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
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.
```python
# In this example, symbol var1 has an inferred type of `str | int`.
class Foo:
def __init__(self):
self.var1 = ""
def do_something(self, val: int):
self.var1 = val
# In this example, symbol var2 has an inferred type of `Foo | None`.
if __debug__:
var2 = None
else:
var2 = Foo()
```
#### 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)
One powerful technique Pyright uses to eliminate type inference ambiguities is _bidirectional inference_. This technique makes use of an “expected type”.
As we saw above, the type of the expression `[]` is ambiguous, but if this expression is passed as an argument to a function, and the corresponding parameter is annotated with the type `list[int]`, Pyright can now assume that the type of `[]` in this context must be `list[int]`. Ambiguity eliminated!
This technique is called “bidirectional inference” because type inference for an assignment normally proceeds by first determining the type of the right-hand side (RHS) of the assignment, which then informs the type of the left-hand side (LHS) of the assignment. With bidirectional inference, if the LHS of an assignment has a declared type, it can influence the inferred type of the RHS.
Lets look at a few examples:
```python
var1 = [] # Type of RHS is ambiguous
var2: list[int] = [] # Type of LHS now makes type of RHS unambiguous
var3 = [4] # Type is assumed to be list[int]
var4: list[float] = [4] # Type of RHS is now list[float]
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
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”.
```python
if some_condition:
my_list = []
else:
my_list = ["a", "b"]
reveal_type(my_list) # list[str]
```
#### 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.
```python
# This function has two explicit return statements and one implicit
# return (at the end). It does not have a declared return type,
# so Pyright infers its return type based on the return expressions.
# In this case, the inferred return type is `str | bool | None`.
def func1(val: int):
if val > 3:
return ""
elif val < 1:
return True
```
#### 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` statement that raises an exception of type `NotImplementedError`.
```python
class Foo:
# The inferred return type is NoReturn.
def method1(self):
raise Exception()
# The inferred return type is Unknown.
@abstractmethod
def method2(self):
raise NotImplementedError()
```
#### 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
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:
```python
# The return type of this function cannot be fully inferred based
# on the information provided because the types of parameters
# a and b are unknown. In this case, the inferred return
# type is `Unknown | None`.
def func1(a, b, c):
if c:
return a
elif c > 3:
return b
else:
return None
```
In cases where all parameters are unannotated, Pyright uses a technique called _call-site return type inference_. It performs type inference using the the types of arguments passed to the function in a call expression. If the unannotated function calls other functions, call-site return type inference can be used recursively. Pyright limits this recursion to a small number for practical performance reasons.
```python
def func2(p_int: int, p_str: str, p_flt: float):
# The type of var1 is inferred to be `int | None` based
# on call-site return type inference.
var1 = func1(p_int, p_int, p_int)
# The type of var2 is inferred to be `str | float | None`.
var2 = func1(p_str, p_flt, p_int)
```
#### 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.
For instance methods, the first parameter (named `self` by convention) is inferred to be type `Self`.
For class methods, the first parameter (named `cls` by convention) is inferred to be type `type[Self]`.
For other unannotated parameters within a method, Pyright looks for a method of the same name implemented in a base class. If the corresponding method in the base class has the same signature (the same number of parameters with the same names), no overloads, and annotated parameter types, the type annotation from this method is “inherited” for the corresponding parameter in the child class method.
```python
class Parent:
def method1(self, a: int, b: str) -> float:
...
class Child(Parent):
def method1(self, a, b):
return a
reveal_type(Child.method1) # (self: Child, a: int, b: str) -> int
```
When parameter types are inherited from a base class method, the return type is not inherited. Instead, normal return type inference techniques are used.
If the type of an unannotated parameter cannot be inferred using any of the above techniques and the parameter has a default argument expression associated with it, the parameter type is inferred from the default argument type. If the default argument is `None`, the inferred type is `Unknown | None`.
```python
def func(a, b=0, c=None):
pass
reveal_type(func) # (a: Unknown, b: int, c: Unknown | None) -> None
```
This inference technique also applies to lambdas whose input parameters include default arguments.
```python
cb = lambda x = "": x
reveal_type(cb) # (x: str = "" -> str)
```
#### 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.
```python
# This function is allowed to return only values 1, 2 or 3.
def func1() -> Literal[1, 2, 3]:
...
# This function must be passed one of three specific string values.
def func2(mode: Literal["r", "w", "rw"]) -> None:
...
```
When Pyright is performing type inference, it generally does not infer literal types. Consider the following example:
```python
# If Pyright inferred the type of var1 to be list[Literal[4]],
# any attempt to append a value other than 4 to this list would
# generate an error. Pyright therefore infers the broader
# type list[int].
var1 = [4]
```
#### 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.
```python
# The inferred type is tuple[Literal[1], Literal["a"], Literal[True]].
var1 = (1, "a", True)
def func1(a: int):
# The inferred type is tuple[int, int].
var2 = (a, a)
# If you want the type to be tuple[int, ...]
# (i.e. a homogeneous tuple of indeterminate length),
# use a type annotation.
var3: tuple[int, ...] = (a, a)
```
Because tuples are typed as specifically as possible, literal types are normally retained. However, as an exception to this inference rule, if the tuple expression is nested within another tuple, set, list or dictionary expression, literal types are not retained. This is done to avoid the inference of complex types (e.g. unions with many subtypes) when evaluating tuple statements with many entries.
```python
# The inferred type is list[tuple[int, str, bool]].
var4 = [(1, "a", True), (2, "b", False), (3, "c", False)]
```
#### List Expressions
When inferring the type of a list expression (in the absence of bidirectional inference hints), Pyright uses the following heuristics:
1. If the list is empty (`[]`), assume `list[Unknown]` (unless a known list type is assigned to the same variable along another code path).
2. If the list contains at least one element and all elements are the same type T, infer the type `list[T]`.
3. If the list contains multiple elements that are of different types, the behavior depends on the `strictListInference` configuration setting. By default this setting is off.
* If `strictListInference` is off, infer `list[Unknown]`.
* Otherwise use the union of all element types and infer `list[Union[(elements)]]`.
These heuristics can be overridden through the use of bidirectional inference hints (e.g. by providing a declared type for the target of the assignment expression).
```python
var1 = [] # Infer list[Unknown]
var2 = [1, 2] # Infer list[int]
# Type depends on strictListInference config setting
var3 = [1, 3.4] # Infer list[Unknown] (off)
var3 = [1, 3.4] # Infer list[int | float] (on)
var4: list[float] = [1, 3.4] # Infer list[float]
```
#### Set Expressions
When inferring the type of a set expression (in the absence of bidirectional inference hints), Pyright uses the following heuristics:
1. If the set contains at least one element and all elements are the same type T, infer the type `set[T]`.
2. If the set contains multiple elements that are of different types, the behavior depends on the `strictSetInference` configuration setting. By default this setting is off.
* If `strictSetInference` is off, infer `set[Unknown]`.
* Otherwise use the union of all element types and infer `set[Union[(elements)]]`.
These heuristics can be overridden through the use of bidirectional inference hints (e.g. by providing a declared type for the target of the assignment expression).
```python
var1 = {1, 2} # Infer set[int]
# Type depends on strictSetInference config setting
var2 = {1, 3.4} # Infer set[Unknown] (off)
var2 = {1, 3.4} # Infer set[int | float] (on)
var3: set[float] = {1, 3.4} # Infer set[float]
```
#### Dictionary Expressions
When inferring the type of a dictionary expression (in the absence of bidirectional inference hints), Pyright uses the following heuristics:
1. If the dict is empty (`{}`), assume `dict[Unknown, Unknown]`.
2. If the dict contains at least one element and all keys are the same type K and all values are the same type V, infer the type `dict[K, V]`.
3. If the dict contains multiple elements where the keys or values differ in type, the behavior depends on the `strictDictionaryInference` configuration setting. By default this setting is off.
* If `strictDictionaryInference` is off, infer `dict[Unknown, Unknown]`.
* Otherwise use the union of all key and value types `dict[Union[(keys)], Union[(values)]]`.
```python
var1 = {} # Infer dict[Unknown, Unknown]
var2 = {1: ""} # Infer dict[int, str]
# Type depends on strictDictionaryInference config setting
var3 = {"a": 3, "b": 3.4} # Infer dict[str, Unknown] (off)
var3 = {"a": 3, "b": 3.4} # Infer dict[str, int | float] (on)
var4: dict[str, float] = {"a": 3, "b": 3.4}
```
#### 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.
```python
# The type of var1 is (a: Unknown, b: Unknown) -> Unknown.
var1 = lambda a, b: a + b
# This function takes a comparison function callback.
def float_sort(list: list[float], comp: Callable[[float, float], bool]): ...
# In this example, the types of the lambdas input parameters
# a and b can be inferred to be float because the float_sort
# function expects a callback that accepts two floats as
# inputs.
float_sort([2, 1.3], lambda a, b: False if a < b else True)
```

49
docs/type-server.md Normal file
View File

@@ -0,0 +1,49 @@
# Pyright Type Server
In addition to the [command-line tool](command-line.md) and the [language server](settings.md), Pyright ships a **type server** that speaks the Type Server Protocol (TSP). It is distributed as a separate npm package, `pyright-typeserver`, and is exposed through the `pyright-typeserver` executable.
## What is the Type Server Protocol?
The Language Server Protocol (LSP) is designed around editor features (completions, hover, go-to-definition, and so on). Some tools instead need direct access to a Python type checker's *type information* — the inferred type of an expression, the declared type of a symbol, resolved imports, and search paths — without going through editor-oriented requests.
The Type Server Protocol is a JSON-RPC protocol, layered on top of the same transport as LSP, that exposes this type information directly. A client can open Python documents in the usual LSP way (`textDocument/didOpen`, `textDocument/didChange`, …) and then ask the type server questions such as:
* `typeServer/getComputedType` — the inferred type at a parse node.
* `typeServer/getDeclaredType` — the declared type of a declaration.
* `typeServer/getExpectedType` — the expected (contextual) type at a node.
* `typeServer/resolveImport` — resolve an import to a file on disk.
* `typeServer/getPythonSearchPaths` — the search paths used for import resolution.
* `typeServer/getSnapshot` — the current analysis snapshot version, used to keep type queries consistent with the document state.
## Running the type server
The type server communicates over stdio, just like the language server:
```bash
pyright-typeserver --stdio
```
It is not intended to be run interactively; it is started by a client (for example, an editor extension or a code-generation tool) that drives it over the protocol.
## Notebook support
The type server understands Jupyter notebooks. When a client sends `notebookDocument/didOpen`, `notebookDocument/didChange`, and `notebookDocument/didClose`, the server models the notebook as a linear chain of chained cell source files so that names defined in earlier cells are visible in later cells, matching notebook execution semantics.
## Virtual file redirection
The type server supports redirecting the contents of a file on disk to a virtual document supplied by the client. This is used, for example, by stub generators that synthesize a merged view of a module and want the type server to analyze the synthesized contents in place of the file on disk. Clients drive this through the `pyright/setVirtualFileRedirect` and `pyright/removeVirtualFileRedirect` notifications.
## Relationship to Pyright
The type server is built on the same analyzer, binder, and type evaluator as the Pyright command-line tool and language server. It reuses Pyright's `Service` / `Program` / `SourceFile` infrastructure, so type results are identical to what Pyright's other front ends produce.
### Code layout: two packages
The type server is split across two packages, following the same convention Pyright uses for its command-line tool and language server:
| Package | Role |
| --- | --- |
| `packages/pyright-internal/src/typeServer/` | **All of the actual code.** The server, protocol definitions, notebook support, virtual-file redirection, the file system layer, and the tests all live here. This is the only package with real implementation. |
| `packages/pyright-typeserver/` | **The distributable wrapper.** A thin bundling shim (rspack config, `package.json`, and a bin entry point) that packages the code above into the publishable `pyright-typeserver` npm package. It contains no logic — its `nodeMain.ts` simply calls `main()` from `pyright-internal`. |
This mirrors how `pyright-internal` holds all of the parser/checker/evaluator logic while the `pyright` package is just the thin CLI and language-server bundle. Keeping the code in `pyright-internal` means the type server shares the exact same `vscode-languageserver` copy and analyzer internals as the rest of Pyright, rather than pulling in a duplicate or mismatched set of dependencies.

57
docs/type-stubs.md Normal file
View File

@@ -0,0 +1,57 @@
## 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
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 as having type “Unknown”, and wildcard imports (of the form `from foo import *`) will not populate the modules namespace with specific symbol names.
Why does Pyright not attempt (by default) to determine types from imported python sources? There are several reasons.
1. Imported libraries can be quite large, so analyzing them can require significant time and computation.
2. Some libraries are thin shims on top of native (C++) libraries. Little or no type information would be inferable in these cases.
3. Some libraries override Pythons default loader logic. Static analysis is not possible in these cases.
4. Type information inferred from source files is often of low value because many types cannot be inferred correctly. Even if concrete types can be inferred, generic type definitions cannot.
5. Type analysis would expose all symbols from an imported module, even those that are not meant to be exposed by the author. Unlike many other languages, Python offers no way of differentiating between a symbol that is meant to be exported and one that isnt.
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
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
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)
Click on the menu item to create the type stub. Depending on the size of the library, it may take pyright tens of seconds to analyze the library and generate type stub files. Once complete, you should see a message in VS Code indicating success or failure.
![Pyright](img/CreateTypeStub2.png)
#### 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
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:
1. When generating a “.pyi” file, pyright removes any imports that are not referenced. Sometimes libraries import symbols that are meant to be simply re-exported from a module even though they are not referenced internally to that module. In such cases, you will need to manually add back these imports. Pyright does not perform this import culling in `__init__.pyi` files because this re-export technique is especially common in such files.
2. Some libraries attempt to import modules within a try statement. These constructs dont work well in type stub files because they cannot be evaluated statically. Pyright omits any try statements when creating “.pyi” files, so you may need to add back in these import statements.
3. Decorator functions are especially problematic for static type analyzers. Unless properly typed, they completely hide the signature of any class or function they are applied to. For this reason, it is highly recommended that you enable the “reportUntypedFunctionDecorator” and “reportUntypedClassDecorator” switches in pyrightconfig.json. Most decorators simply return the same function they are passed. Those can easily be annotated with a TypeVar like this:
```python
from typing import Any, Callable, TypeVar
_FuncT = TypeVar('_FuncT', bound=Callable[..., Any])
def my_decorator(*args, **kw) -> Callable[[_FuncT], _FuncT]: ...
```

430
docs/typed-libraries.md Normal file
View File

@@ -0,0 +1,430 @@
## 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.
These recommendations are intended to provide the following benefits:
1. Consumers of libraries should have a great coding experience with fast and accurate completion suggestions, class and function documentation, signature help (including parameter default values), hover text, and auto-imports. This should happen by default without needing to download extra packages and without any special configuration. These features should be consistent across the Python ecosystem regardless of a developers choice of editor, IDE, notebook environment, etc.
2. Consumers of libraries should be able to rely on complete and accurate type information so static type checkers can detect and report type inconsistencies and other violations of the interface contract.
3. Library authors should be able to specify a well-defined interface contract that is enforced by tools. This allows a library implementation to evolve and improve without breaking consumers of the library.
4. Library authors should have the benefits of static type checking to produce high-quality, bug-free implementations.
### 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.
Inlined type annotations should be included directly within the source code that ships with the package. Of the options listed in PEP 561, inlined type annotations offer the most benefits. They typically require the least effort to add and maintain, they are always consistent with the implementation, and docstrings and default parameter values are readily available, allowing language servers to enhance the development experience.
There are cases where inlined type annotations are not possible — most notably when a librarys exposed functionality is implemented in a language other than Python. Libraries that expose symbols implemented in languages other than Python should include stub (“.pyi”) files that describe the types for those symbols. These stubs should also contain docstrings and default parameter values.
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
[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:
* foo._bar (_bar is private)
* foo._bar.baz (_bar and baz are private)
* foo._bar.baz.bop (_bar, baz, and bop are private)
Each module exposes a set of symbols. Some of these symbols are considered “private” — implementation details that are not part of the librarys interface. Type checkers like pyright use the following rules to determine which symbols are visible outside of the package.
* Symbols whose names begin with an underscore (but are not dunder names) are considered private.
* Imported symbols are considered private by default. If they use the “import A as A” (a redundant module alias), “from X import A as A” (a redundant symbol alias), or “from . import A” forms, symbol “A” is not private unless the name begins with an underscore. If a file `__init__.py` uses the form “from .A import X”, symbol “A” is not private unless the name begins with an underscore (but “X” is still private). If a wildcard import (of the form “from X import *”) is used, all symbols referenced by the wildcard are not private.
* A module can expose an `__all__` symbol at the module level that provides a list of names that are considered part of the interface. The `__all__` symbol indicates which symbols are included in a wildcard import. All symbols included in the `__all__` list are considered public even if the other rules above would otherwise indicate that they were private. For example, this allows symbols whose names begin with an underscore to be included in the interface.
* Local variables within a function (including nested functions) are always considered private.
The following idioms are supported for defining the values contained within `__all__`. These restrictions allow type checkers to statically determine the value of `__all__`.
* `__all__ = ('a', 'b')`
* `__all__ = ['a', 'b']`
* `__all__ += ['a', 'b']`
* `__all__ += submodule.__all__`
* `__all__.extend(['a', 'b'])`
* `__all__.extend(submodule.__all__)`
* `__all__.append('a')`
* `__all__.remove('a')`
### 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:
Classes:
* All class variables, instance variables, and methods that are “visible” (not overridden) are annotated and refer to known types
* If a class is a subclass of a generic class, type arguments are provided for each generic type parameter, and these type arguments are known types
Functions and Methods:
* All input parameters have type annotations that refer to known types
* The return parameter is annotated and refers to a known type
* The result of applying one or more decorators results in a known type
Type Aliases:
* All of the types referenced by the type alias are known
Variables:
* All variables have type annotations that refer to known types
Type annotations can be omitted in a few specific cases where the type is obvious from the context:
* Constants that are assigned simple literal values (e.g. `RED = '#F00'` or `MAX_TIMEOUT = 50` or `room_temperature: Final = 20`). A constant is a symbol that is assigned only once and is either annotated with `Final` or is named in all-caps. A constant that is not assigned a simple literal value requires explicit annotations, preferably with a `Final` annotation (e.g. `WOODWINDS: Final[list[str]] = ['Oboe', 'Bassoon']`).
* Enum values within an Enum class do not require annotations because they take on the type of the Enum class.
* Type aliases do not require annotations. A type alias is a symbol that is defined at a module level with a single assignment where the assigned value is an instantiable type, as opposed to a class instance (e.g. `Foo = Callable[[Literal["a", "b"]], int | str]` or `Bar = MyGenericClass[int] | None`).
* The “self” parameter in an instance method and the “cls” parameter in a class method do not require an explicit annotation.
* The return type for an `__init__` method does not need to be specified, since it is always `None`.
* The following module-level symbols do not require type annotations: `__all__`,`__author__`, `__copyright__`, `__email__`, `__license__`, `__title__`, `__uri__`, `__version__`.
* The following class-level symbols do not require type annotations: `__class__`, `__dict__`, `__doc__`, `__module__`, `__slots__`.
* 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
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
```python
# Variable with known type (unambiguous because it uses a literal assignment)
a = 3
# Variable with ambiguous type
a = [3, 4, 5]
# Variable with known (declared) type
a: list[int] = [3, 4, 5]
# Type alias with partially unknown type (because type
# arguments are missing for list and dict)
DictOrList = list | dict
# Type alias with known type
DictOrList = list[Any] | dict[str, Any]
# Generic type alias with known type
_T = TypeVar("_T")
DictOrList = list[_T] | dict[str, _T]
# Function with known type
def func(a: int | None, b: dict[str, float] = {}) -> None:
pass
# Function with partially unknown type (because type annotations
# are missing for input parameters and return type)
def func(a, b):
pass
# Function with partially unknown type (because of missing
# type args on dict)
def func(a: int, b: dict) -> None:
pass
# Function with partially unknown type (because return type
# annotation is missing)
def func(a: int, b: dict[str, float]):
pass
# Decorator with partially unknown type (because type annotations
# are missing for input parameters and return type)
def my_decorator(func):
return func
# Function with partially unknown type (because type is obscured
# by untyped decorator)
@my_decorator
def func(a: int) -> str:
pass
# Class with known type
class MyClass:
height: float = 2.0
def __init__(self, name: str, age: int):
self.age: int = age
@property
def name(self) -> str:
...
# Class with partially unknown type
class MyClass:
# Missing type annotation for class variable
height = None
# Missing input parameter annotations
def __init__(self, name, age):
# Missing type annotation for instance variable
self.age = age
# Missing return type annotation
@property
def name(self):
...
# Class with partially unknown type
class BaseClass1:
# Missing type annotation
height: = 2.0
# Missing type annotation
def get_stuff(self):
...
# Class with known type (because it overrides all symbols
# exposed by BaseClass that have incomplete types)
class DerivedClass1(BaseClass1):
height: float
def get_stuff(self) -> str:
...
# Class with known type
class BaseClass2:
height: float = 2.0
# Class with ambiguous type
class DerivedClass2(BaseClass2):
# Missing type annotation, could be inferred as float or int
height = 1
# Class with partially unknown type because base class
# (dict) is generic, and type arguments are not specified.
class DictSubclass(dict):
pass
```
### 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>`
Pyright will analyze the library, identify all symbols that comprise the interface to the library and emit errors for any symbols whose types are ambiguous or unknown. It also produces a “type completeness score” which is the percentage of symbols with known types.
To see additional details (including a full list of symbols in the library), append the `--verbose` option.
The `--verifytypes` option can be combined with `--outputjson` to emit the results in a form that can be consumed by other tools.
The `--verifytypes` feature can be integrated into a continuous integration (CI) system to verify that a library remains “type complete”.
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
Here are some tips for increasing the type completeness score for your library:
* If your package includes tests or sample code, consider removing them from the distribution. If there is good reason to include them, consider placing them in a directory that begins with an underscore so they are not considered part of your librarys interface.
* If your package includes submodules that are meant to be implementation details, rename those files to begin with an underscore.
* If a symbol is not intended to be part of the librarys interface and is considered an implementation detail, rename it such that it begins with an underscore. It will then be considered private and excluded from the type completeness check.
* 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
#### 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
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
If a function or method is intended to take parameters that are specified only by name, use the keyword-only separator ("*").
```python
def create_user(age: int, *, dob: date | None = None):
...
```
#### 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
def compare_values(value1: T, value2: T, /) -> bool:
...
def compare_values(__value1: T, __value2: T) -> bool:
...
```
### Annotating Decorators
Decorators modify the behavior of a class or a function. Providing annotations for decorators is straightforward if the decorator retains the original signature of the decorated function.
```python
_F = TypeVar("_F", bound=Callable[..., Any])
def simple_decorator(_func: _F) -> _F:
"""
Simple decorators are invoked without parentheses like this:
@simple_decorator
def my_function(): ...
"""
...
def complex_decorator(*, mode: str) -> Callable[[_F], _F]:
"""
Complex decorators are invoked with arguments like this:
@complex_decorator(mode="easy")
def my_function(): ...
"""
...
```
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
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 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.
```python
# Simple type alias
FamilyPet = Cat | Dog | GoldFish
# Generic type alias
ListOrTuple = list[_T] | tuple[_T, ...]
# Recursive type alias
TreeNode = LeafNode | list["TreeNode"]
# Explicit type alias using PEP 613 syntax
StrOrInt: TypeAlias = str | int
```
#### 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 or subclass thereof.
```python
from abc import ABC, abstractmethod
class Hashable(ABC):
@property
@abstractmethod
def hash_value(self) -> int:
"""Subclasses must override"""
raise NotImplementedError()
@abstractmethod
def print(self) -> str:
"""Subclasses must override"""
raise NotImplementedError()
```
#### 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
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
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.
In both cases, it is OK to omit the declared type of a constant if it is assigned a literal str, int, float, bool or None value. In such cases, the type inference rules are clear and unambiguous, and adding a literal type annotation would be redundant.
```python
# All-caps constant with inferred type
COLOR_FORMAT_RGB = "rgb"
# All-caps constant with explicit type
COLOR_FORMAT_RGB: Literal["rgb"] = "rgb"
LATEST_VERSION: tuple[int, int] = (4, 5)
# Final variable with inferred type
ColorFormatRgb: Final = "rgb"
# Final variable with explicit type
ColorFormatRgb: Final[Literal["rgb"]] = "rgb"
LATEST_VERSION: Final[tuple[int, int]] = (4, 5)
```
#### 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.
Data classes (described in [PEP 557](https://www.python.org/dev/peps/pep-0557/)) are preferred over untyped dictionaries.
TypedDict (described in [PEP 589](https://www.python.org/dev/peps/pep-0589/)) is preferred over untyped dictionaries.
### 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
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
# Older versions of Python do not support subscripting
# for the OrderedDict type, so the annotation must be
# enclosed in quotes.
def get_config(self) -> "OrderedDict[str, str]":
return self._config
```
### Type Comment Annotations
Python 3.0 introduced syntax for parameter and return type annotations, as specified in [PEP 484](https://www.python.org/dev/peps/pep-0484/). Python 3.6 introduced support for variable type annotations, as specified in [PEP 526](https://www.python.org/dev/peps/pep-0526/).
If you need to support older versions of Python, type annotations can still be provided as “type comments”. These comments take the form `# type: <annotation>`.
```python
class Foo:
# Variable type comments go at the end of the line
# where the variable is assigned.
timeout = None # type: int | None
# Function type comments can be specified on the
# line after the function signature.
def send_message(self, name, length):
# type: (str, int) -> None
...
# Function type comments can also specify the type
# of each parameter on its own line.
def receive_message(
self,
name, # type: str
length # type: int
):
# type: () -> Message
...
```
#### 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
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
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
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.