This repository contains both Ruff (a Python linter and formatter) and ty (a Python type checker). The crates follow a naming convention: ruff_* for Ruff-specific code and ty_* for ty-specific code. ty reuses several Ruff crates, including the Python parser (ruff_python_parser) and AST definitions (ruff_python_ast).
Run all tests (setting CARGO_PROFILE_DEV_OPT_LEVEL=1 CARGO_PROFILE_DEV_DEBUG="line-tables-only" to enable optimizations while retaining some debug info, and setting INSTA_FORCE_PASS=1 INSTA_UPDATE=always MDTEST_UPDATE_SNAPSHOTS=1 to ensure all snapshots are updated):
CARGO_PROFILE_DEV_OPT_LEVEL=1 INSTA_FORCE_PASS=1 INSTA_UPDATE=always CARGO_PROFILE_DEV_DEBUG="line-tables-only" MDTEST_UPDATE_SNAPSHOTS=1 cargo test
Run tests for a specific crate:
CARGO_PROFILE_DEV_OPT_LEVEL=1 INSTA_FORCE_PASS=1 INSTA_UPDATE=always CARGO_PROFILE_DEV_DEBUG="line-tables-only" MDTEST_UPDATE_SNAPSHOTS=1 cargo test -p ty_python_semantic
Run a single mdtest file. The path to the mdtest file should be relative to the crates/ty_python_semantic/resources/mdtest folder:
CARGO_PROFILE_DEV_OPT_LEVEL=1 INSTA_FORCE_PASS=1 INSTA_UPDATE=always CARGO_PROFILE_DEV_DEBUG="line-tables-only" MDTEST_UPDATE_SNAPSHOTS=1 cargo test -p ty_python_semantic --test mdtest -- <path/to/mdtest_file.md>
To run a specific mdtest within a file, use a substring of the Markdown header text as MDTEST_TEST_FILTER. Only use this if it's necessary to isolate a single test case:
MDTEST_TEST_FILTER="<filter>" CARGO_PROFILE_DEV_OPT_LEVEL=1 INSTA_FORCE_PASS=1 INSTA_UPDATE=always CARGO_PROFILE_DEV_DEBUG="line-tables-only" MDTEST_UPDATE_SNAPSHOTS=1 cargo test -p ty_python_semantic --test mdtest -- <path/to/mdtest_file.md>
After running the tests, always review the contents of any snapshots that have been added or updated.
When running tests with INSTA_FORCE_PASS=1, check for .pending-snap files if any affected tests use inline snapshots.
cargo clippy --workspace --all-targets --all-features -- -D warnings
Use debug builds (not --release) when developing, as release builds lack debug assertions and have slower compile times.
Run Ruff:
cargo run --bin ruff -- check path/to/file.py
Run ty:
cargo run --bin ty -- check path/to/file.py
When working on ty, first read .agents/skills/working-on-ty/SKILL.md, then also read and follow any more specific ty skills it points to.
pub: make an item public when another workspace crate needs it and that produces the cleaner implementation.uvx prek at the end of a task if you changed files in the repo. This includes changes such as rebases or addressing review comments. Use uvx prek run --files <path1> <path2> and pass every file you changed. This keeps the hook run independent of staged state and avoids sweeping unrelated changes. Use uvx prek run -a when a full-repository hook sweep is specifically needed.panic!, unreachable!, or .unwrap(). Instead, try to encode those constraints in the type system. Don‘t be afraid to write code that’s more verbose or requires largeish refactors if it enables you to avoid these unsafe calls.if let combined with &&) over nested if let statements to reduce indentation and improve readability. At the end of a task, always check your work to see if you missed opportunities to use let chains.#[expect()] over [allow()], where possible. But if a lint is complaining about unused/dead code, it's usually best to just delete the unused code.cargo dev generate-all after changing configuration options, CLI arguments, lint rules, or environment variable definitions, as these changes require regeneration of schemas, docs, and CLI references.test_.impl blocks unless the impl is deliberately placed in a separate file, as for large structs.uv run for any scripts from the repository root unless you use --no-project, --script or similar. Using uv run from the Ruff repo root without these flags will build Ruff from source, which is very slow and usually unnecessary.