You’ve written type hints throughout this course. The hints by themselves don’t do anything — Python ignores them at runtime. pyright is a separate program that reads your code, follows the hints, and tells you about mismatches.
Most editors (VS Code’s Pylance, Cursor, modern Vim/Neovim setups) run pyright in the background as you type. This lesson shows how to run it from the command line and how to turn on strict mode.
Install pyright
The fastest way:
uv tool install pyright
That installs it as a global command-line tool. Check:
pyright --version
pyright 1.1.400
Run it
From inside your project folder:
pyright
Pyright scans every .py file and reports any type errors. With a clean codebase, you’ll see:
0 errors, 0 warnings, 0 informations
If there’s a mismatch, you’ll see something like:
main.py:5:13 - error: Argument of type "Literal['hi']" cannot be assigned to parameter "n" of type "int"
The format is file:line:column - severity: message.
Strict mode
By default pyright is fairly lenient. To get the most out of type checking, turn on strict mode. Create or edit pyproject.toml in your project root:
[tool.pyright]
typeCheckingMode = "strict"
Or run with the flag:
pyright --strict
In strict mode, pyright complains about:
- Functions without type hints
- Variables whose type can’t be inferred
- Implicit
Anytypes - Unsafe operations (using a possibly-
Nonevalue without checking) - Unused variables and imports
It’s noisy at first. But once your code passes strict mode, a whole category of bugs disappears.
A small example
def double(n):
return n * 2
print(double("hello"))
In default mode, pyright might not warn — Python happily multiplies a string. In strict mode:
main.py:1:12 - error: Function declaration missing return type annotation
main.py:1:12 - error: Type of parameter "n" is unknown
Add the types:
def double(n: int) -> int:
return n * 2
print(double("hello"))
Now pyright catches the actual problem:
main.py:5:14 - error: Argument of type "Literal['hello']" cannot be assigned to parameter "n" of type "int"
That’s the goal — surface the real bug at the call site before you ever run the code.
Type errors that pyright catches
A taste of what strict mode covers:
- Wrong argument type —
int_function("hi") - Wrong return type — claims to return
int, actually returnsNone - Possibly-None access —
result.upper()whereresultcould beNone - Dictionary key not present —
config["host"]whenhostmight not exist - Calling something that isn’t callable —
value()whenvalueisn’t a function - Attribute that doesn’t exist —
user.emial(typo) - Unused variables and imports — flagged for cleanup
Each comes with the file, line, and a clear message.
When pyright is wrong
Sometimes pyright doesn’t have enough information and complains about something you know is fine. Two escape hatches:
# tell pyright the type of a value explicitly
config: dict[str, str] = json.load(f) # type: ignore is rarely needed if you annotate
# silence a specific line
result = third_party_func() # pyright: ignore[reportUnknownMemberType]
# pyright: ignore should be the last resort — usually adding a type hint or fixing the underlying issue is better.
Editor integration
Pyright runs as a background language server (you saw this in Section 1 with Pylance). Every red underline in your editor is pyright (or its sibling Pylance) at work.
Running pyright from the command line is most useful for:
- CI checks — adding a
pyrightstep to your build pipeline so type errors fail the build - Whole-project scans — sometimes the editor only checks files you have open
- Reproducing what CI sees — make sure your environment matches
A practical workflow
# while developing, the editor gives instant feedback
# before committing, run a full check:
pyright
# fix any errors, then commit
For a real-world Python codebase moving toward strict typing, this two-step rhythm pays off enormously. The earlier you catch a bug, the cheaper it is.
What’s next
Now that pyright is set up, we can write meaningful types. Next — generic types and how to describe collections precisely.