Open-Source Wikis

/

Ruff

/

Features

/

Type checker (ty)

astral-sh/ruff

Type checker (ty)

End-to-end view of ty check.

What the user sees

ty check src/

ty reports type errors, e.g.:

src/foo.py:5:1: invalid-assignment Cannot assign value of type `str` to variable of type `int`
src/foo.py:9:1: unsupported-operator `int` does not support operator `+` with `str`

ty also serves as a language server (ty server), powering hover, go-to-definition, completion, and renames.

Pipeline

graph TD
    CLI[ty CLI<br/>crates/ty]
    Project[ty_project::Project<br/>load ty.toml / pyproject.toml]
    Db[ProjectDatabase<br/>Salsa-tracked]
    Files[Python files]
    Parser[ruff_python_parser]
    AST[Mod AST]
    Index[ruff_python_index]
    Sem[Semantic model]
    Resolver[ty_module_resolver<br/>typeshed + site-packages + project]
    Vendored[ty_vendored<br/>typeshed stubs]
    Site[ty_site_packages<br/>installed packages]
    Inference[ty_python_semantic::types<br/>infer_definition_types,<br/>infer_expression_type]
    Lints[lint.rs<br/>diagnostic registry]
    Diag[Diagnostics]
    Output[stdout / LSP]

    CLI --> Project
    Project --> Db
    Db --> Files
    Files --> Parser
    Parser --> AST
    AST --> Index
    Index --> Sem
    Db --> Resolver
    Resolver --> Vendored
    Resolver --> Site
    Sem --> Inference
    Resolver --> Inference
    Inference --> Lints
    Lints --> Diag
    Diag --> Output

Salsa-driven recompute

ty is built on Salsa. Every analysis step is a tracked query: parse a file, build a scope graph, infer the type of a definition, infer the type of an expression, check a statement against its annotation, etc. Salsa caches results keyed by their inputs and recomputes only what's needed when an input changes.

This is what enables the LSP to feel responsive on large projects: editing one file invalidates only the queries that depend on its content.

What ty knows about

The type system supports (in various states of completeness):

  • Classes and instances, with PEP 695 generics
  • Protocols (structural subtyping) including recursive protocols
  • Unions, intersections, literal types, tuple types, callable types
  • TypeVar, ParamSpec, TypeVarTuple — variance, defaults, constraints
  • Type narrowing (isinstance, is, ==, match/case)
  • TypeGuard / TypeIs
  • Decorators that affect type (@dataclass, @final, @overload, @functools.partial)
  • TypedDict, NamedTuple, Enum
  • PEP 695 type aliases
  • Gradual typing (Any, Never, Unknown for incomplete inference)

The mdtest corpus (crates/ty_python_semantic/resources/mdtest/) is the de facto specification: each file is a Markdown document mixing examples and expected diagnostics.

Module resolution

ty_module_resolver decides what import x and from x.y import z mean:

  1. Project source (the workspace files).
  2. ty_site_packages (installed packages discovered through the configured Python interpreter).
  3. ty_vendored (bundled typeshed stubs).

Stubs (.pyi) are preferred over runtime sources where present. The resolver mirrors Pyright's algorithm closely (the Ruff acknowledgements credit Pyright for the import-resolution design).

Diagnostics

Each diagnostic has:

  • A short stable id (invalid-assignment, protocol-violation, …)
  • A message tuned for narrow terminals
  • Optional subdiagnostics with extra context (e.g. "argument 1 was expected to be int, got str")

Diagnostics are documented and rendered consistently across CLI and LSP.

Suppressions

# type: ignore and ty-specific # type: ignore[<id>] comments suppress diagnostics. Implementation: crates/ty_python_semantic/src/suppression*.

CLI vs server

The CLI (crates/ty) calls into the same database the LSP (crates/ty_server) uses. The CLI builds a database, walks files, and prints diagnostics. The LSP keeps the database alive across edits and handles incremental requests.

Where to start when extending

  • New type-system feature: see ty_python_semantic. Add an mdtest fixture early — it doubles as a spec.
  • New diagnostic: extend the LintMetadata registry, implement the check, add an mdtest case.
  • LSP feature: combine a tracked query in ty_ide (or ty_python_semantic) with a handler in ty_server.
  • Module resolution change: ty_module_resolver plus tests for typeshed + site-packages combinations.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Type checker (ty) – Ruff wiki | Factory