mrkeyoor.com_
Thu 06 Aug 23:53 UTC
PyPIUtilsupdated 06 Aug 2026

pyright

The pyright package on PyPI is not Microsoft's type checker itself. Microsoft's pyright is a TypeScript program distributed on npm, and this package is Robert Craigie's community wrapper around it; the README opens by stating it is not affiliated with Microsoft in any way. What pip installs is a launcher: when you run the pyright command it looks for node on your PATH, downloads a Node.js runtime via nodeenv if there is none (or via prebuilt wheels if you installed the nodejs extra), npm-installs the pinned pyright package into a cache directory under ~/.cache, and then forwards your arguments to the real checker. From there the flags, config files, and output are exactly upstream pyright, the same engine that powers Pylance in VS Code. Each wrapper release pins one upstream version, and a set of PYRIGHT_PYTHON_* environment variables controls which version actually runs, where node comes from, and where the cache lives.

Verdict

The path of least resistance for getting pyright into a pip-only workflow, provided you accept a Node.js runtime appearing in ~/.cache and a community wrapper that trails upstream on its own schedule. If node is already in your stack, install the npm package directly and skip the layer.

API stability3/5The wrapper itself is a stable pass-through plus env vars, but what you actually run is upstream pyright, which ships behavior changes in perpetual 1.1.x patch releases: a routine bump routinely surfaces new diagnostics, and PYRIGHT_PYTHON_FORCE_VERSION=latest makes CI results drift with no lockfile change.
Docs3/5One README documents install, the nodejs extra, pre-commit setup with the virtualenv fix, and every PYRIGHT_PYTHON_* env var, then defers everything else to Microsoft's pyright docs, which are thorough. There is no docs site for the wrapper and no troubleshooting section for the runtime download failing.
Maintenance3/5Effectively a single-maintainer project (Robert Craigie, also of Prisma Client Python). Releases are automated against upstream but show multi-month gaps: v1.1.408 on 2026-01-08, v1.1.409 on 2026-04-23, v1.1.411 on 2026-06-25, which was also the last push. 21 open issues counting PRs.
Ecosystem4/5About 9.8M weekly downloads and it is the de facto way Python tooling invokes pyright: the standard pre-commit hook lives here, and the engine underneath is the same one behind Pylance. The repo itself is small (273 stars) because the ecosystem weight belongs to upstream.

Use it if

  • Your team is pip-only and you want pyright in requirements.txt, tox, or CI without asking every Python developer to install Node.js and npm first
  • You want CI to run the same checker as Pylance in VS Code, so the squiggles developers see locally match what the pipeline rejects
  • You use pre-commit: this repo is the maintained pre-commit hook for pyright, with a documented fix for the virtualenv isolation problem
  • You need version control over the checker: the pip version pins one upstream release, and PYRIGHT_PYTHON_FORCE_VERSION lets you pin or float independently of your lockfile
Skip it if

Setup reality

pip install pyright succeeds instantly because you only installed the launcher; the real work happens on first invocation. If node is not on PATH, nodeenv downloads a full Node.js distribution, then npm installs the actual checker into ~/.cache (override with PYRIGHT_PYTHON_CACHE_DIR or XDG_CACHE_HOME). The README itself recommends pip install pyright[nodejs] instead, which pulls node from nodejs-wheel-binaries wheels because the nodeenv path is less reliable. In CI this means the first pyright call costs a download every run unless you cache that directory. Version behavior surprises people: the pip version pins one npm version, PYRIGHT_PYTHON_FORCE_VERSION overrides it, and setting it to latest means results can change with no change to your lockfile. Under pre-commit, the hook runs in its own virtualenv and cannot see your dependencies until you add additional_dependencies or point venvPath at your real venv.

Patterns

Install and run the checkerinstall-and-run

pip install "pyright[nodejs]"

pyright src/            # check a directory
python -m pyright src/  # same thing, module form
pyright --version       # triggers the first-run download

The nodejs extra pulls Node.js from prebuilt wheels (nodejs-wheel-binaries), which the README recommends over the default nodeenv download because it is more reliable. Either way, the first invocation still npm-installs the actual checker into ~/.cache.

Pin or float the upstream pyright versionpin-pyright-version

# run an exact upstream version, regardless of what pip installed
export PYRIGHT_PYTHON_FORCE_VERSION=1.1.411

# or always track Microsoft's newest release
export PYRIGHT_PYTHON_FORCE_VERSION=latest

pyright src/

Each pip release pins one npm version, and the wrapper's releases can trail upstream by weeks. latest closes that gap but means new diagnostics appear in CI with no change on your side; pin an exact version for reproducible builds. PYRIGHT_PYTHON_PYLANCE_VERSION instead matches your VS Code Pylance build, but FORCE_VERSION wins if both are set.

Configure with pyrightconfig.jsonpyrightconfig-basics

{
  "include": ["src"],
  "exclude": ["**/__pycache__", ".venv"],
  "typeCheckingMode": "standard",
  "pythonVersion": "3.11",
  "venvPath": ".",
  "venv": ".venv"
}

venvPath plus venv is how pyright finds your installed packages when it is not launched from inside the venv; without them you get reportMissingImports on every third-party import. pythonVersion controls which syntax and stdlib stubs are assumed, independent of the interpreter running the wrapper.

Configure in pyproject.toml insteadpyproject-tool-pyright

[tool.pyright]
include = ["src"]
typeCheckingMode = "strict"
pythonVersion = "3.11"
venvPath = "."
venv = ".venv"

Same keys as pyrightconfig.json, TOML syntax. If both files exist, pyrightconfig.json wins and the pyproject section is ignored entirely, which is a classic source of edits that mysteriously do nothing.

Run pyright in GitHub Actionsrun-in-ci

jobs:
  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install -r requirements.txt "pyright[nodejs]"
      - run: pyright

Install your project's dependencies into the same environment or every import gets flagged. The exit code is nonzero when errors exist, so the step fails the build on its own. The first pyright call downloads the npm package every run; cache ~/.cache (or set PYRIGHT_PYTHON_CACHE_DIR to a cached path) to avoid paying that each time.

Get JSON output for toolingmachine-readable-output

pyright --outputjson src/ > report.json

python -c "
import json
r = json.load(open('report.json'))
print(r['summary']['errorCount'], 'errors')
for d in r['generalDiagnostics'][:5]:
    print(d['file'], d['range']['start']['line'] + 1, d.get('rule'), d['message'].splitlines()[0])
"

generalDiagnostics carries file, severity, rule, and a range; line numbers in ranges are 0-based, so add 1 before showing them to humans. summary has errorCount, warningCount, and timeInSec, handy for tracking a ratchet number in CI.

Re-check on every savewatch-mode

pyright --watch src/

Keeps the node process alive and re-analyzes only what changed, so iterations are much faster than fresh runs, which rebuild the whole program state. The tradeoff is a resident node process holding the analysis in memory.

Suppress a diagnostic on one lineignore-per-line

value = legacy_call()  # pyright: ignore[reportUnknownVariableType]

data = parse(raw)  # type: ignore

Prefer the pyright: ignore form with a rule name: it suppresses only that rule, only for pyright, so mypy still checks the line. A bare type: ignore silences every rule for every checker and quietly keeps hiding new problems introduced later.

Relax rules or mode for a whole fileignore-per-file

# at the top of the file
# pyright: reportMissingTypeStubs=false, reportUnknownMemberType=false

# or override the checking mode for just this file
# pyright: basic

These comments must appear before any code. Useful for generated code or a legacy module inside a strict repo; the rest of the codebase keeps its configured mode. Rule names are the report* settings from the configuration docs.

Ratchet from basic to strict, directory by directorystrict-vs-basic-mode

{
  "typeCheckingMode": "basic",
  "strict": ["src/core"]
}

strict enables dozens of extra report* rules (unknown types, missing annotations, untyped decorators), and turning it on repo-wide in an existing codebase usually produces thousands of errors. The strict array lets you hold cleaned-up directories at strict while the rest stays basic. Note the default mode is standard, which sits between the two.

Run pyright and mypy side by sideuse-with-mypy

pip install mypy "pyright[nodejs]"

mypy src/ && pyright src/

They genuinely disagree: pyright infers types inside unannotated functions while mypy skips unannotated bodies by default, and their narrowing rules differ. Expect each to report things the other accepts. Use type: ignore for both checkers and pyright: ignore[rule] when only pyright should stand down.

Run as a pre-commit hookpre-commit-hook

repos:
  - repo: https://github.com/RobertCraigie/pyright-python
    rev: v1.1.411
    hooks:
      - id: pyright
        additional_dependencies: ["pytest", "pydantic"]

pre-commit installs the hook in its own isolated virtualenv, so pyright cannot see your project's packages and floods you with import errors. The README's two fixes: list your dependencies under additional_dependencies, or set venvPath and venv in your pyright config to point at the real project venv.

Alternatives

PackageRegistryPick it when
mypyPyPIYou want the reference Python type checker as a plain Python package, with zero Node.js involvement and the plugin ecosystem (Django, SQLAlchemy stubs)
basedpyrightPyPIYou want pyright's engine but properly packaged for PyPI: the checker ships inside the wheel, nothing is downloaded at runtime, plus extra rules and a real LSP story outside VS Code
tyPyPIYou want a very fast Rust-based checker from the ruff team and can accept something much newer than pyright or mypy