Repository: astral-sh/ty Branch: main Commit: 876233049afc Files: 68 Total size: 616.9 KB Directory structure: gitextract_pe4vyql8/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── 1_bug_report.yaml │ │ ├── 2_question.yaml │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── actionlint.yaml │ ├── renovate.json5 │ ├── workflows/ │ │ ├── build-binaries.yml │ │ ├── build-docker.yml │ │ ├── ci.yaml │ │ ├── daily_property_tests.yml │ │ ├── publish-docs.yml │ │ ├── publish-mirror.yml │ │ ├── publish-pypi.yml │ │ └── release.yml │ └── zizmor.yml ├── .gitignore ├── .gitmodules ├── .markdownlint.yaml ├── .pre-commit-config.yaml ├── .python-version ├── BENCHMARKS.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── SECURITY.md ├── _typos.toml ├── assets/ │ └── badge/ │ └── v0.json ├── dist-workspace.toml ├── docs/ │ ├── .overrides/ │ │ ├── main.html │ │ └── partials/ │ │ └── integrations/ │ │ └── analytics/ │ │ └── fathom.html │ ├── configuration.md │ ├── editors.md │ ├── exclusions.md │ ├── features/ │ │ ├── diagnostics.md │ │ ├── language-server.md │ │ └── type-system.md │ ├── index.md │ ├── installation.md │ ├── js/ │ │ └── extra.js │ ├── modules.md │ ├── python-version.md │ ├── reference/ │ │ ├── cli.md │ │ ├── configuration.md │ │ ├── editor-settings.md │ │ ├── environment.md │ │ ├── exit-codes.md │ │ ├── rules.md │ │ └── typing-faq.md │ ├── requirements.in │ ├── requirements.txt │ ├── rules.md │ ├── stylesheets/ │ │ └── extra.css │ ├── suppression.md │ └── type-checking.md ├── mkdocs.yml ├── pyproject.toml ├── python/ │ └── ty/ │ ├── __init__.py │ ├── __main__.py │ ├── _find_ty.py │ └── py.typed └── scripts/ ├── autogenerate_files.sh ├── release.sh ├── transform_readme.py └── update_schemastore.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # Check http://editorconfig.org for more information # This is the main config file for this project: root = true [*] charset = utf-8 trim_trailing_whitespace = true end_of_line = lf indent_style = space insert_final_newline = true indent_size = 2 [*.{rs,py,pyi,toml}] indent_size = 4 [*.md] max_line_length = 100 ================================================ FILE: .gitattributes ================================================ * text=auto eol=lf docs/reference/cli.md linguist-generated=true docs/reference/configuration.md linguist-generated=true docs/reference/rules.md linguist-generated=true ================================================ FILE: .github/ISSUE_TEMPLATE/1_bug_report.yaml ================================================ name: Bug report description: Report an error or unexpected behavior body: - type: markdown attributes: value: | Thank you for taking the time to report an issue! We're glad to have you involved with ty. **Before reporting, please make sure to search through [existing issues](https://github.com/astral-sh/ty/issues?q=is:issue+is:open+label:bug) (including [closed](https://github.com/astral-sh/ty/issues?q=is:issue%20state:closed%20label:bug)).**
**In particular, check out the list of [frequently encountered problems](https://github.com/astral-sh/ty/issues/445).** - type: textarea attributes: label: Summary description: | A clear and concise description of the bug, including a minimal reproducible example. Be sure to include the command you invoked (e.g., `ty check`) and the current ty settings (e.g., relevant sections from your `pyproject.toml`). If possible, try to include the [playground](https://play.ty.dev) link that reproduces this issue. validations: required: true - type: input attributes: label: Version description: What version of ty are you using? (see `ty version`) placeholder: e.g., ty 0.0.1 (ff9000864 2025-05-06) validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/2_question.yaml ================================================ name: Question description: Ask a question about ty labels: ["question"] body: - type: textarea attributes: label: Question description: Describe your question in detail. validations: required: true - type: input attributes: label: Version description: What version of ty are you using? (see `ty version`) placeholder: e.g., ty 0.0.1 (ff9000864 2025-05-06) validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: true contact_links: - name: Documentation url: https://github.com/astral-sh/ty/blob/main/README.md about: Please consult the documentation before creating an issue. - name: Community url: https://discord.com/invite/astral-sh about: Join our Discord community to ask questions and collaborate. ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ## Summary ## Test Plan ================================================ FILE: .github/actionlint.yaml ================================================ # Configuration for the actionlint tool, which we run via prek # to verify the correctness of the syntax in our GitHub Actions workflows. self-hosted-runner: # Various runners we use that aren't recognized out-of-the-box by actionlint: labels: - depot-ubuntu-latest-8 - depot-ubuntu-22.04-16 - depot-ubuntu-22.04-32 - github-windows-2025-x86_64-8 - github-windows-2025-x86_64-16 ================================================ FILE: .github/renovate.json5 ================================================ { $schema: "https://docs.renovatebot.com/renovate-schema.json", dependencyDashboard: true, suppressNotifications: ["prEditedNotification"], extends: ["github>astral-sh/renovate-config"], labels: ["internal"], schedule: ["before 4am on Monday"], semanticCommits: "disabled", separateMajorMinor: false, prHourlyLimit: 10, enabledManagers: ["github-actions", "pre-commit"], "pre-commit": { enabled: true, }, packageRules: [ // Pin GitHub Actions to immutable SHAs. { matchDepTypes: ["action"], pinDigests: true, }, // Annotate GitHub Actions SHAs with a SemVer version. { extends: ["helpers:pinGitHubActionDigests"], extractVersion: "^(?v?\\d+\\.\\d+\\.\\d+)$", versioning: "regex:^v?(?\\d+)(\\.(?\\d+)\\.(?\\d+))?$", }, { // Group upload/download artifact updates, the versions are dependent groupName: "Artifact GitHub Actions dependencies", matchManagers: ["github-actions"], matchDatasources: ["gitea-tags", "github-tags"], matchPackageNames: ["actions/upload-artifact", "actions/download-artifact"], description: "Weekly update of artifact-related GitHub Actions dependencies", }, { // This package rule disables updates for GitHub runners: // we'd only pin them to a specific version // if there was a deliberate reason to do so groupName: "GitHub runners", matchManagers: ["github-actions"], matchDatasources: ["github-runners"], description: "Disable PRs updating GitHub runners (e.g. 'runs-on: macos-14')", enabled: false, }, { groupName: "prek dependencies", matchManagers: ["pre-commit"], description: "Weekly update of prek dependencies", } ], vulnerabilityAlerts: { commitMessageSuffix: "", labels: ["internal", "security"], }, } ================================================ FILE: .github/workflows/build-binaries.yml ================================================ # Build ty on all platforms. # # Generates both wheels (for PyPI) and archived binaries (for GitHub releases). # # Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a local # artifacts job within `cargo-dist`. name: "Build binaries" on: workflow_call: inputs: plan: required: true type: string pull_request: paths: # When we change pyproject.toml, we want to ensure that the maturin builds still work. - pyproject.toml # And when we change this workflow itself... - .github/workflows/build-binaries.yml concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: read env: PACKAGE_NAME: ty MODULE_NAME: ty PYTHON_VERSION: "3.13" CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 jobs: sdist: if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: "Prep README.md" run: python scripts/transform_readme.py - name: "Build sdist" uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1 with: maturin-version: v1.11.5 command: sdist args: --out dist - name: "Test sdist" run: | pip install dist/"${PACKAGE_NAME}"-*.tar.gz --force-reinstall "${MODULE_NAME}" version "${MODULE_NAME}" --help python -m "${MODULE_NAME}" --help - name: "Upload sdist" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: wheels-sdist path: dist macos-x86_64: if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }} runs-on: macos-14 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: x64 - name: "Prep README.md" run: python scripts/transform_readme.py - name: "Build wheels - x86_64" uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1 with: maturin-version: v1.11.5 target: x86_64 args: --release --locked --out dist --compatibility pypi - name: "Upload wheels" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: wheels-macos-x86_64 path: dist - name: "Archive binary" run: | TARGET=x86_64-apple-darwin ARCHIVE_NAME=ty-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp ruff/target/$TARGET/release/ty $ARCHIVE_NAME/ty tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 - name: "Upload binary" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: artifacts-macos-x86_64 path: | *.tar.gz *.sha256 macos-aarch64: if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }} runs-on: macos-14 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: arm64 - name: "Prep README.md" run: python scripts/transform_readme.py - name: "Build wheels - aarch64" uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1 with: maturin-version: v1.11.5 target: aarch64 args: --release --locked --out dist --compatibility pypi - name: "Test wheel - aarch64" run: | pip install dist/"${PACKAGE_NAME}"-*.whl --force-reinstall ty --help python -m ty --help - name: "Upload wheels" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: wheels-aarch64-apple-darwin path: dist - name: "Archive binary" run: | TARGET=aarch64-apple-darwin ARCHIVE_NAME=ty-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp ruff/target/$TARGET/release/ty $ARCHIVE_NAME/ty tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 - name: "Upload binary" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: artifacts-aarch64-apple-darwin path: | *.tar.gz *.sha256 windows: if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }} runs-on: windows-latest strategy: matrix: platform: - target: x86_64-pc-windows-msvc arch: x64 - target: i686-pc-windows-msvc arch: x86 - target: aarch64-pc-windows-msvc arch: x64 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: ${{ matrix.platform.arch }} - name: "Prep README.md" run: python scripts/transform_readme.py - name: "Build wheels" uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1 with: maturin-version: v1.11.5 target: ${{ matrix.platform.target }} args: --release --locked --out dist --compatibility pypi env: # aarch64 build fails, see https://github.com/PyO3/maturin/issues/2110 XWIN_VERSION: 16 - name: "Test wheel" if: ${{ !startsWith(matrix.platform.target, 'aarch64') }} shell: bash run: | python -m pip install dist/"${PACKAGE_NAME}"-*.whl --force-reinstall "${MODULE_NAME}" version "${MODULE_NAME}" --help python -m "${MODULE_NAME}" --help - name: "Upload wheels" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: wheels-${{ matrix.platform.target }} path: dist - name: "Archive binary" shell: bash run: | ARCHIVE_FILE=ty-${{ matrix.platform.target }}.zip 7z a $ARCHIVE_FILE ./ruff/target/${{ matrix.platform.target }}/release/ty.exe sha256sum $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 - name: "Upload binary" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: artifacts-${{ matrix.platform.target }} path: | *.zip *.sha256 linux: if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }} runs-on: ubuntu-latest strategy: matrix: target: - x86_64-unknown-linux-gnu - i686-unknown-linux-gnu steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: x64 - name: "Prep README.md" run: python scripts/transform_readme.py - name: "Build wheels" uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1 with: maturin-version: v1.11.5 target: ${{ matrix.target }} manylinux: 2_17 args: --release --locked --out dist --compatibility pypi - name: "Test wheel" if: ${{ startsWith(matrix.target, 'x86_64') }} run: | pip install dist/"${PACKAGE_NAME}"-*.whl --force-reinstall "${MODULE_NAME}" version "${MODULE_NAME}" --help python -m "${MODULE_NAME}" --help - name: "Upload wheels" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: wheels-${{ matrix.target }} path: dist - name: "Archive binary" shell: bash run: | set -euo pipefail TARGET=${{ matrix.target }} ARCHIVE_NAME=ty-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp ruff/target/$TARGET/release/ty $ARCHIVE_NAME/ty tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 - name: "Upload binary" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: artifacts-${{ matrix.target }} path: | *.tar.gz *.sha256 linux-cross: if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }} runs-on: ubuntu-latest strategy: matrix: platform: - target: aarch64-unknown-linux-gnu arch: aarch64 manylinux: 2_17 # see https://github.com/astral-sh/ruff/issues/3791 # and https://github.com/gnzlbg/jemallocator/issues/170#issuecomment-1503228963 maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16 - target: armv7-unknown-linux-gnueabihf arch: armv7 manylinux: 2_17 - target: s390x-unknown-linux-gnu arch: s390x manylinux: 2_17 - target: powerpc64le-unknown-linux-gnu arch: ppc64le manylinux: 2_17 # see https://github.com/astral-sh/ruff/issues/10073 maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16 - target: arm-unknown-linux-musleabihf # Use the cross container, but tag as `linux_armv6l` manylinux: auto arch: arm steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: "Prep README.md" run: python scripts/transform_readme.py - name: "Build wheels" uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1 with: maturin-version: v1.11.5 target: ${{ matrix.platform.target }} manylinux: ${{ matrix.platform.manylinux }} docker-options: ${{ matrix.platform.maturin_docker_options }} args: --release --locked --out dist --compatibility pypi - uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1 if: ${{ matrix.platform.arch != 'ppc64le'}} name: Test wheel with: arch: ${{ matrix.platform.arch == 'arm' && 'armv6' || matrix.platform.arch }} distro: ${{ matrix.platform.arch == 'arm' && 'bullseye' || 'ubuntu20.04' }} githubToken: ${{ github.token }} install: | apt-get update apt-get install -y --no-install-recommends python3 python3-pip pip3 install -U pip run: | pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall ty --help - name: "Upload wheels" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: wheels-${{ matrix.platform.target }} path: dist - name: "Archive binary" shell: bash run: | set -euo pipefail TARGET=${{ matrix.platform.target }} ARCHIVE_NAME=ty-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp ruff/target/$TARGET/release/ty $ARCHIVE_NAME/ty tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 - name: "Upload binary" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: artifacts-${{ matrix.platform.target }} path: | *.tar.gz *.sha256 musllinux: if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }} runs-on: ubuntu-latest strategy: matrix: target: - x86_64-unknown-linux-musl - i686-unknown-linux-musl steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: x64 - name: "Prep README.md" run: python scripts/transform_readme.py - name: "Build wheels" uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1 with: maturin-version: v1.11.5 target: ${{ matrix.target }} manylinux: musllinux_1_2 args: --release --locked --out dist --compatibility pypi - name: "Test wheel" if: matrix.target == 'x86_64-unknown-linux-musl' run: | docker run --rm -v ${{ github.workspace }}:/io -w /io --env MODULE_NAME --env PACKAGE_NAME alpine:latest sh -c " apk add python3; python -m venv .venv; .venv/bin/pip3 install ${PACKAGE_NAME} --no-index --find-links dist/ --force-reinstall; .venv/bin/${MODULE_NAME} --help; " - name: "Upload wheels" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: wheels-${{ matrix.target }} path: dist - name: "Archive binary" shell: bash run: | set -euo pipefail TARGET=${{ matrix.target }} ARCHIVE_NAME=ty-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp ruff/target/$TARGET/release/ty $ARCHIVE_NAME/ty tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 - name: "Upload binary" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: artifacts-${{ matrix.target }} path: | *.tar.gz *.sha256 musllinux-cross: if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }} runs-on: ubuntu-latest strategy: matrix: platform: - target: aarch64-unknown-linux-musl arch: aarch64 maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16 - target: armv7-unknown-linux-musleabihf arch: armv7 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive persist-credentials: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: "Prep README.md" run: python scripts/transform_readme.py - name: "Build wheels" uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1 with: maturin-version: v1.11.5 target: ${{ matrix.platform.target }} manylinux: musllinux_1_2 args: --release --locked --out dist --compatibility pypi docker-options: ${{ matrix.platform.maturin_docker_options }} - uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1 name: Test wheel with: arch: ${{ matrix.platform.arch }} distro: alpine_latest githubToken: ${{ github.token }} install: | apk add python3 run: | python -m venv .venv .venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall .venv/bin/${{ env.MODULE_NAME }} --help - name: "Upload wheels" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: wheels-${{ matrix.platform.target }} path: dist - name: "Archive binary" shell: bash run: | set -euo pipefail TARGET=${{ matrix.platform.target }} ARCHIVE_NAME=ty-$TARGET ARCHIVE_FILE=$ARCHIVE_NAME.tar.gz mkdir -p $ARCHIVE_NAME cp ruff/target/$TARGET/release/ty $ARCHIVE_NAME/ty tar czvf $ARCHIVE_FILE $ARCHIVE_NAME shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256 - name: "Upload binary" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: artifacts-${{ matrix.platform.target }} path: | *.tar.gz *.sha256 ================================================ FILE: .github/workflows/build-docker.yml ================================================ # Build and publish a Docker image. # # Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a local # artifacts job within `cargo-dist`. # # TODO(charlie): Ideally, the publish step would happen as a publish job within `cargo-dist`, but # sharing the built image as an artifact between jobs is challenging. name: "Build Docker image" on: workflow_call: inputs: plan: required: true type: string pull_request: paths: - .github/workflows/build-docker.yml env: TY_BASE_IMG: ghcr.io/${{ github.repository_owner }}/ty permissions: contents: read # TODO(zanieb): Ideally, this would be `read` on dry-run but that will require # significant changes to the workflow. packages: write # zizmor: ignore[excessive-permissions] jobs: docker-build: name: Build Docker image (ghcr.io/astral-sh/ty) for ${{ matrix.platform }} runs-on: ubuntu-latest environment: name: release strategy: fail-fast: false matrix: platform: - linux/amd64 - linux/arm64 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive persist-credentials: false - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Check tag consistency if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} env: TAG: ${{ inputs.plan != '' && fromJson(inputs.plan).announcement_tag || 'dry-run' }} run: | version=$(grep -m 1 "^version = " pyproject.toml | sed -e 's/version = "\(.*\)"/\1/g') if [ "${TAG}" != "${version}" ]; then echo "The input tag does not match the version from pyproject.toml:" >&2 echo "${TAG}" >&2 echo "${version}" >&2 exit 1 else echo "Releasing ${version}" fi - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 with: images: ${{ env.TY_BASE_IMG }} # Defining this makes sure the org.opencontainers.image.version OCI label becomes the actual release version and not the branch name tags: | type=raw,value=dry-run,enable=${{ inputs.plan == '' || fromJson(inputs.plan).announcement_tag_is_implicit }} type=pep440,pattern={{ version }},value=${{ inputs.plan != '' && fromJson(inputs.plan).announcement_tag || 'dry-run' }},enable=${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} - name: Normalize Platform Pair (replace / with -) run: | platform=${{ matrix.platform }} echo "PLATFORM_TUPLE=${platform//\//-}" >> "$GITHUB_ENV" # Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/ - name: Build and push by digest id: build uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: . platforms: ${{ matrix.platform }} cache-from: type=gha,scope=ty-${{ env.PLATFORM_TUPLE }} cache-to: type=gha,mode=min,scope=ty-${{ env.PLATFORM_TUPLE }} labels: ${{ steps.meta.outputs.labels }} outputs: type=image,name=${{ env.TY_BASE_IMG }},push-by-digest=true,name-canonical=true,push=${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} - name: Export digests env: digest: ${{ steps.build.outputs.digest }} run: | mkdir -p /tmp/digests touch "/tmp/digests/${digest#sha256:}" - name: Upload digests uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: digests-${{ env.PLATFORM_TUPLE }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 docker-publish: name: Publish Docker image (ghcr.io/astral-sh/ty) runs-on: ubuntu-latest environment: name: release needs: - docker-build if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} permissions: attestations: write id-token: write packages: write steps: - name: Download digests uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: path: /tmp/digests pattern: digests-* merge-multiple: true - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 with: images: ${{ env.TY_BASE_IMG }} # Order is on purpose such that the label org.opencontainers.image.version has the first pattern with the full version tags: | type=pep440,pattern={{ version }},value=${{ fromJson(inputs.plan).announcement_tag }} type=pep440,pattern={{ major }}.{{ minor }},value=${{ fromJson(inputs.plan).announcement_tag }} - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} # Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/ - name: Create manifest list and push working-directory: /tmp/digests # The jq command expands the docker/metadata json "tags" array entry to `-t tag1 -t tag2 ...` for each tag in the array # The printf will expand the base image with the `@sha256: ...` for each sha256 in the directory # The final command becomes `docker buildx imagetools create -t tag1 -t tag2 ... @sha256: @sha256: ...` run: | # shellcheck disable=SC2046 docker buildx imagetools create \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf "${TY_BASE_IMG}@sha256:%s " *) - name: Export manifest digest id: manifest-digest env: IMAGE: ${{ env.TY_BASE_IMG }} VERSION: ${{ steps.meta.outputs.version }} run: | digest="$( docker buildx imagetools inspect \ "${IMAGE}:${VERSION}" \ --format '{{json .Manifest}}' \ | jq -r '.digest' )" echo "digest=${digest}" >> "$GITHUB_OUTPUT" - name: Generate artifact attestation uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-name: ${{ env.TY_BASE_IMG }} subject-digest: ${{ steps.manifest-digest.outputs.digest }} docker-publish-extra: name: Publish additional Docker image based on ${{ matrix.image-mapping }} runs-on: ubuntu-latest environment: name: release needs: - docker-publish if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} permissions: attestations: write id-token: write packages: write strategy: fail-fast: false matrix: # Mapping of base image followed by a comma followed by one or more base tags (comma separated) # Note, org.opencontainers.image.version label will use the first base tag (use the most specific tag first) image-mapping: - alpine:3.23,alpine3.23,alpine - debian:trixie-slim,trixie-slim,debian-slim - buildpack-deps:trixie,trixie,debian steps: - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Generate Dynamic Dockerfile Tags shell: bash env: TAG_VALUE: ${{ fromJson(inputs.plan).announcement_tag }} run: | set -euo pipefail # Extract the image and tags from the matrix variable IFS=',' read -r BASE_IMAGE BASE_TAGS <<< "${{ matrix.image-mapping }}" # Generate Dockerfile content cat < Dockerfile FROM ${BASE_IMAGE} COPY --from=${TY_BASE_IMG}:latest /ty /usr/local/bin/ty ENTRYPOINT [] CMD ["/usr/local/bin/ty"] EOF # Initialize a variable to store all tag docker metadata patterns TAG_PATTERNS="" # Loop through all base tags and append its docker metadata pattern to the list # Order is on purpose such that the label org.opencontainers.image.version has the first pattern with the full version IFS=','; for TAG in ${BASE_TAGS}; do TAG_PATTERNS="${TAG_PATTERNS}type=pep440,pattern={{ version }},suffix=-${TAG},value=${TAG_VALUE}\n" TAG_PATTERNS="${TAG_PATTERNS}type=pep440,pattern={{ major }}.{{ minor }},suffix=-${TAG},value=${TAG_VALUE}\n" TAG_PATTERNS="${TAG_PATTERNS}type=raw,value=${TAG}\n" done # Remove the trailing newline from the pattern list TAG_PATTERNS="${TAG_PATTERNS%\\n}" # Export image cache name echo "IMAGE_REF=${BASE_IMAGE//:/-}" >> "$GITHUB_ENV" # Export tag patterns using the multiline env var syntax { echo "TAG_PATTERNS<> "$GITHUB_ENV" - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 # ghcr.io prefers index level annotations env: DOCKER_METADATA_ANNOTATIONS_LEVELS: index with: images: ${{ env.TY_BASE_IMG }} flavor: | latest=false tags: | ${{ env.TAG_PATTERNS }} - name: Build and push id: build-and-push uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: . platforms: linux/amd64,linux/arm64 # We do not really need to cache here as the Dockerfile is tiny #cache-from: type=gha,scope=ty-${{ env.IMAGE_REF }} #cache-to: type=gha,mode=min,scope=ty-${{ env.IMAGE_REF }} push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} annotations: ${{ steps.meta.outputs.annotations }} - name: Generate artifact attestation uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-name: ${{ env.TY_BASE_IMG }} subject-digest: ${{ steps.build-and-push.outputs.digest }} # This is effectively a duplicate of `docker-publish` to make https://github.com/astral-sh/ty/pkgs/container/ty # show the ty base image first since GitHub always shows the last updated image digests # This works by annotating the original digests (previously non-annotated) which triggers an update to ghcr.io docker-republish: name: Annotate Docker image (ghcr.io/astral-sh/ty) runs-on: ubuntu-latest environment: name: release needs: - docker-publish-extra if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} permissions: attestations: write id-token: write packages: write steps: - name: Download digests uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: path: /tmp/digests pattern: digests-* merge-multiple: true - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 env: DOCKER_METADATA_ANNOTATIONS_LEVELS: index with: images: ${{ env.TY_BASE_IMG }} # Order is on purpose such that the label org.opencontainers.image.version has the first pattern with the full version tags: | type=pep440,pattern={{ version }},value=${{ fromJson(inputs.plan).announcement_tag }} type=pep440,pattern={{ major }}.{{ minor }},value=${{ fromJson(inputs.plan).announcement_tag }} - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} # Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/ - name: Create manifest list and push working-directory: /tmp/digests # The readarray part is used to make sure the quoting and special characters are preserved on expansion (e.g. spaces) # The jq command expands the docker/metadata json "tags" array entry to `-t tag1 -t tag2 ...` for each tag in the array # The printf will expand the base image with the `@sha256: ...` for each sha256 in the directory # The final command becomes `docker buildx imagetools create -t tag1 -t tag2 ... @sha256: @sha256: ...` run: | readarray -t lines <<< "$DOCKER_METADATA_OUTPUT_ANNOTATIONS"; annotations=(); for line in "${lines[@]}"; do annotations+=(--annotation "$line"); done # shellcheck disable=SC2046 docker buildx imagetools create \ "${annotations[@]}" \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf "${TY_BASE_IMG}@sha256:%s " *) - name: Export manifest digest id: manifest-digest env: IMAGE: ${{ env.TY_BASE_IMG }} VERSION: ${{ steps.meta.outputs.version }} run: | digest="$( docker buildx imagetools inspect \ "${IMAGE}:${VERSION}" \ --format '{{json .Manifest}}' \ | jq -r '.digest' )" echo "digest=${digest}" >> "$GITHUB_OUTPUT" - name: Generate artifact attestation uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-name: ${{ env.TY_BASE_IMG }} subject-digest: ${{ steps.manifest-digest.outputs.digest }} ================================================ FILE: .github/workflows/ci.yaml ================================================ name: CI permissions: contents: read on: push: branches: [main] pull_request: workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }} cancel-in-progress: true env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 PACKAGE_NAME: ty jobs: python-package: name: "python package" runs-on: ubuntu-latest timeout-minutes: 20 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false submodules: recursive - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ env.PYTHON_VERSION }} architecture: x64 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - name: "Build wheels" uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1.50.1 with: args: --out dist - name: "Test wheel" run: | pip install --force-reinstall --find-links dist "${PACKAGE_NAME}" --pre ty --help python -m ty --help - name: "Remove wheels from cache" run: rm -rf target/wheels prek: name: "prek" runs-on: depot-ubuntu-22.04-16 timeout-minutes: 10 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 - name: "Cache prek" uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: ~/.cache/prek key: prek-${{ hashFiles('.pre-commit-config.yaml') }} - name: "Run prek" run: | echo '```console' > "$GITHUB_STEP_SUMMARY" # Enable color output for prek and remove it for the summary # Use --hook-stage=manual to enable slower hooks that are skipped by default SKIP=cargo-fmt,clippy,dev-generate-all uvx prek run --all-files --show-diff-on-failure --color always --hook-stage manual | \ tee >(sed -E 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mGK]//g' >> "$GITHUB_STEP_SUMMARY") >&1 exit_code="${PIPESTATUS[0]}" echo '```' >> "$GITHUB_STEP_SUMMARY" exit "$exit_code" generated-check: name: "Check generated files unedited" runs-on: ubuntu-latest timeout-minutes: 5 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false submodules: recursive - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 - name: "Run auto generation scripts" run: | ./scripts/autogenerate_files.sh - name: "Check for uncommitted changes" run: | if [[ -n "$(git status --porcelain)" ]]; then echo "Error: Auto-generated files were manually edited." echo "Files with changes:" git status --porcelain exit 1 fi docs: timeout-minutes: 10 name: "mkdocs" runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - name: "Build docs" run: uvx --with-requirements docs/requirements.txt mkdocs build --strict -f mkdocs.yml ================================================ FILE: .github/workflows/daily_property_tests.yml ================================================ name: Daily property test run on: workflow_dispatch: schedule: - cron: "0 12 * * *" pull_request: paths: - ".github/workflows/daily_property_tests.yml" permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true env: CARGO_INCREMENTAL: 0 CARGO_NET_RETRY: 10 CARGO_TERM_COLOR: always RUSTUP_MAX_RETRIES: 10 FORCE_COLOR: 1 jobs: property_tests: name: Property tests runs-on: ubuntu-latest timeout-minutes: 20 # Don't run the cron job on forks: if: ${{ github.repository == 'astral-sh/ty' || github.event_name != 'schedule' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false repository: astral-sh/ruff - name: "Install Rust toolchain" run: rustup show - name: "Install mold" uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1 - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - name: Build ty # A release build takes longer (2 min vs 1 min), but the property tests run much faster in release # mode (1.5 min vs 14 min), so the overall time is shorter with a release build. run: cargo build --locked --release --package ty_python_semantic --tests - name: Run property tests shell: bash run: | export QUICKCHECK_TESTS=100000 for _ in {1..5}; do cargo test --locked --release --package ty_python_semantic -- --ignored list::property_tests cargo test --locked --release --package ty_python_semantic -- --ignored types::property_tests::stable done create-issue-on-failure: name: Create an issue if the daily property test run surfaced any bugs runs-on: ubuntu-latest needs: property_tests if: ${{ github.repository == 'astral-sh/ty' && always() && github.event_name == 'schedule' && needs.property_tests.result == 'failure' }} permissions: issues: write steps: - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | await github.rest.issues.create({ owner: "astral-sh", repo: "ty", title: `Daily property test run failed on ${new Date().toDateString()}`, body: "Run listed here: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}", labels: ["bug", "type properties"], }) ================================================ FILE: .github/workflows/publish-docs.yml ================================================ # Publish the ty documentation. # # Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a post-announce # job within `cargo-dist`. name: mkdocs on: workflow_dispatch: inputs: ref: description: "The commit SHA, tag, or branch to publish. Uses the default branch if not specified." default: "" type: string workflow_call: inputs: plan: required: true type: string permissions: contents: read jobs: mkdocs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ inputs.ref }} persist-credentials: true - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.14 - name: "Set docs version" env: version: ${{ (inputs.plan != '' && fromJson(inputs.plan).announcement_tag) || inputs.ref }} run: | # if version is missing, use 'latest' if [ -z "$version" ]; then echo "Using 'latest' as version" version="latest" fi # Use version as display name for now display_name="$version" echo "version=$version" >> "$GITHUB_ENV" echo "display_name=$display_name" >> "$GITHUB_ENV" - name: "Set branch name" run: | timestamp="$(date +%s)" # create branch_display_name from display_name by replacing all # characters disallowed in git branch names with hyphens branch_display_name="$(echo "${display_name}" | tr -c '[:alnum:]._' '-' | tr -s '-')" echo "branch_name=update-docs-$branch_display_name-$timestamp" >> "$GITHUB_ENV" echo "timestamp=$timestamp" >> "$GITHUB_ENV" - name: "Install dependencies" run: pip install -r docs/requirements.txt - name: "Build docs" run: mkdocs build --strict -f mkdocs.yml - name: "Clone docs repo" run: git clone https://${{ secrets.ASTRAL_DOCS_PAT }}@github.com/astral-sh/docs.git astral-docs - name: "Copy docs" run: rm -rf astral-docs/site/ty && mkdir -p astral-docs/site && cp -r site/ty astral-docs/site/ - name: "Commit docs" working-directory: astral-docs run: | git config user.name "astral-docs-bot" git config user.email "176161322+astral-docs-bot@users.noreply.github.com" git checkout -b "${branch_name}" git add site/ty git commit -m "Update ty documentation for $version" - name: "Create Pull Request" working-directory: astral-docs env: GITHUB_TOKEN: ${{ secrets.ASTRAL_DOCS_PAT }} run: | # set the PR title pull_request_title="Update ty documentation for ${display_name}" # Delete any existing pull requests that are open for this version # by checking against pull_request_title because the new PR will # supersede the old one. gh pr list --state open --json title --jq '.[] | select(.title == "$pull_request_title") | .number' | \ xargs -I {} gh pr close {} # push the branch to GitHub git push origin "${branch_name}" # create the PR gh pr create \ --base=main \ --head="${branch_name}" \ --title="${pull_request_title}" \ --body="Automated documentation update for ${display_name}" \ --label="documentation" - name: "Merge Pull Request" if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }} working-directory: astral-docs env: GITHUB_TOKEN: ${{ secrets.ASTRAL_DOCS_PAT }} run: | # auto-merge the PR if the build was triggered by a release. Manual builds should be reviewed by a human. # give the PR a few seconds to be created before trying to auto-merge it sleep 10 gh pr merge --squash "${branch_name}" ================================================ FILE: .github/workflows/publish-mirror.yml ================================================ # Publish ty releases to a mirror # # Assumed to run as a subworkflow of .github/workflows/release.yml as a custom publish job name: publish-mirror on: workflow_call: inputs: plan: required: true type: string permissions: {} jobs: publish-mirror: runs-on: ubuntu-latest environment: name: release env: VERSION: ${{ fromJson(inputs.plan).announcement_tag }} steps: - name: "Download GitHub Artifacts" uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: "Upload to R2" env: AWS_ACCESS_KEY_ID: ${{ secrets.MIRROR_R2_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.MIRROR_R2_SECRET_ACCESS_KEY }} AWS_ENDPOINT_URL: https://${{ secrets.MIRROR_R2_CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com AWS_DEFAULT_REGION: auto R2_BUCKET: ${{ secrets.MIRROR_R2_BUCKET_NAME }} PROJECT: ty run: | aws s3 cp --recursive --output table --color on \ --exclude '*' \ --include '*.zip' --include '*.zip.sha256' \ --include '*.tar.gz' --include '*.tar.gz.sha256' \ --include sha256.sum --include '*.ps1' --include '*.sh' \ --cache-control "public, max-age=31536000, immutable" \ artifacts/ \ "s3://${R2_BUCKET}/github/${PROJECT}/releases/download/${VERSION}/" ================================================ FILE: .github/workflows/publish-pypi.yml ================================================ # Publish a release to PyPI. # # Assumed to run as a subworkflow of .github/workflows/release.yml; specifically, as a publish job # within `cargo-dist`. name: "Publish to PyPI" on: workflow_call: inputs: plan: required: true type: string jobs: pypi-publish: name: Upload to PyPI runs-on: ubuntu-latest environment: name: release permissions: # For PyPI's trusted publishing. id-token: write steps: - name: "Install uv" uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 with: pattern: wheels-* path: wheels merge-multiple: true - name: Publish to PyPi run: uv publish -v wheels/* ================================================ FILE: .github/workflows/release.yml ================================================ # This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist # # Copyright 2022-2024, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release # * builds artifacts with dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # # Note that the GitHub Release will be created with a generated # title/body based on your changelogs. name: Release permissions: "contents": "write" # This task will run whenever you workflow_dispatch with a tag that looks like a version # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that # package (erroring out if it doesn't have the given version or isn't dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all # (dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will # spin up, creating an independent announcement for each one. However, GitHub # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # # If there's a prerelease-style suffix to the version, then the release(s) # will be marked as a prerelease. on: pull_request: workflow_dispatch: inputs: tag: description: Release Tag required: true default: dry-run type: string jobs: # Run 'dist plan' (or host) to determine what tasks we need to do plan: runs-on: "depot-ubuntu-latest-4" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ (inputs.tag != 'dry-run' && inputs.tag) || '' }} tag-flag: ${{ inputs.tag && inputs.tag != 'dry-run' && format('--tag={0}', inputs.tag) || '' }} publishing: ${{ inputs.tag && inputs.tag != 'dry-run' }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 with: persist-credentials: false submodules: recursive - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.31.0/cargo-dist-installer.sh | sh" - name: Cache dist uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f with: name: cargo-dist-cache path: ~/.cargo/bin/dist # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | dist ${{ (inputs.tag && inputs.tag != 'dry-run' && format('host --steps=create --tag={0}', inputs.tag)) || 'plan' }} --output-format=json > plan-dist-manifest.json echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f with: name: artifacts-plan-dist-manifest path: plan-dist-manifest.json custom-build-binaries: needs: - plan if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }} uses: ./.github/workflows/build-binaries.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit custom-build-docker: needs: - plan if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }} uses: ./.github/workflows/build-docker.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit permissions: "attestations": "write" "contents": "read" "id-token": "write" "packages": "write" # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - custom-build-binaries - custom-build-docker runs-on: "depot-ubuntu-latest-4" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 with: persist-credentials: false submodules: recursive - name: Install cached dist uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true - id: cargo-dist shell: bash run: | dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<> "$GITHUB_OUTPUT" jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f with: name: artifacts-build-global path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Determines if we should publish/announce host: needs: - plan - custom-build-binaries - custom-build-docker - build-global-artifacts # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.custom-build-binaries.result == 'skipped' || needs.custom-build-binaries.result == 'success') && (needs.custom-build-docker.result == 'skipped' || needs.custom-build-docker.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "depot-ubuntu-latest-4" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 with: persist-credentials: false submodules: recursive - name: Install cached dist uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 with: name: cargo-dist-cache path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 with: pattern: artifacts-* path: target/distrib/ merge-multiple: true # This is a harmless no-op for GitHub Releases, hosting for that happens in "announce" - id: host shell: bash run: | dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f with: # Overwrite the previous copy name: artifacts-dist-manifest path: dist-manifest.json custom-publish-pypi: needs: - plan - host if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} uses: ./.github/workflows/publish-pypi.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit # publish jobs get escalated permissions permissions: "id-token": "write" "packages": "write" # Create a GitHub Release while uploading all files to it announce: needs: - plan - host - custom-publish-pypi # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' && (needs.custom-publish-pypi.result == 'skipped' || needs.custom-publish-pypi.result == 'success') }} runs-on: "depot-ubuntu-latest-4" permissions: "attestations": "write" "contents": "write" "id-token": "write" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 with: persist-credentials: false submodules: recursive # Create a GitHub Release while uploading all files to it - name: "Download GitHub Artifacts" uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 with: pattern: artifacts-* path: artifacts merge-multiple: true - name: Cleanup run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - name: Attest uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 with: subject-path: | artifacts/*.json artifacts/*.sh artifacts/*.ps1 artifacts/*.zip artifacts/*.tar.gz - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(needs.host.outputs.val).announcement_is_prerelease && '--prerelease' || '' }}" ANNOUNCEMENT_TITLE: "${{ fromJson(needs.host.outputs.val).announcement_title }}" ANNOUNCEMENT_BODY: "${{ fromJson(needs.host.outputs.val).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* custom-publish-docs: needs: - plan - announce uses: ./.github/workflows/publish-docs.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit custom-publish-mirror: needs: - plan - announce uses: ./.github/workflows/publish-mirror.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit permissions: "contents": "read" ================================================ FILE: .github/zizmor.yml ================================================ rules: secrets-outside-env: ignore: # TODO: move the ASTRAL_DOCS_PAT secret to the release environment - publish-docs.yml ================================================ FILE: .gitignore ================================================ # Python-generated files __pycache__/ *.py[oc] build/ dist/ wheels/ *.egg-info # Virtual environments .venv ================================================ FILE: .gitmodules ================================================ [submodule "ruff"] path = ruff url = https://github.com/astral-sh/ruff ================================================ FILE: .markdownlint.yaml ================================================ # default to true for all rules default: true # MD007/unordered-list-indent MD007: indent: 4 # MD033/no-inline-html MD033: false # MD041/first-line-h1 MD041: false # MD013/line-length MD013: false # MD014/commands-show-output MD014: false # MD024/no-duplicate-heading MD024: # Allow when nested under different parents e.g. CHANGELOG.md siblings_only: true # MD046/code-block-style # # Ignore this because it conflicts with the code block style used in content # tabs of mkdocs-material which is to add a blank line after the content title. # # Ref: https://github.com/astral-sh/ruff/pull/15011#issuecomment-2544790854 MD046: false # Link text should be descriptive # Disallows link text like *here* which is annoying. MD059: false ================================================ FILE: .pre-commit-config.yaml ================================================ fail_fast: false exclude: | (?x)^( .github/workflows/release.yml| ruff/.*| docs/reference/(cli|configuration|rules|environment).md )$ repos: # Priority 0: Read-only hooks; hooks that modify disjoint file types. - repo: https://github.com/astral-sh/uv-pre-commit rev: 0.10.9 hooks: - id: uv-lock priority: 0 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: check-merge-conflict priority: 0 - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.5 hooks: - id: ruff-format priority: 0 - id: ruff args: [--fix, --exit-non-zero-on-fix] types_or: [python, pyi] require_serial: true priority: 1 - repo: https://github.com/abravalheri/validate-pyproject rev: v0.25 hooks: - id: validate-pyproject priority: 0 - repo: https://github.com/executablebooks/mdformat rev: 1.0.0 hooks: - id: mdformat language: python # means renovate will also update `additional_dependencies` additional_dependencies: - mdformat-mkdocs==5.1.4 - mdformat-footnote==0.1.3 priority: 0 - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.48.0 hooks: - id: markdownlint-fix priority: 1 - repo: https://github.com/crate-ci/typos rev: v1.44.0 hooks: - id: typos priority: 0 # Prettier - repo: https://github.com/rbubley/mirrors-prettier rev: v3.8.1 hooks: - id: prettier types: [yaml] priority: 0 # zizmor detects security vulnerabilities in GitHub Actions workflows. # Additional configuration for the tool is found in `.github/zizmor.yml` - repo: https://github.com/zizmorcore/zizmor-pre-commit rev: v1.23.1 hooks: - id: zizmor priority: 0 - repo: https://github.com/shellcheck-py/shellcheck-py rev: v0.11.0.1 hooks: - id: shellcheck priority: 0 - repo: https://github.com/python-jsonschema/check-jsonschema rev: 0.37.0 hooks: - id: check-github-workflows priority: 0 # `actionlint` hook, for verifying correct syntax in GitHub Actions workflows. # Some additional configuration for `actionlint` can be found in `.github/actionlint.yaml`. - repo: https://github.com/rhysd/actionlint rev: v1.7.11 hooks: - id: actionlint stages: # This hook is disabled by default, since it's quite slow. # To run all hooks *including* this hook, use `uvx pre-commit run -a --hook-stage=manual`. # To run *just* this hook, use `uvx pre-commit run -a actionlint --hook-stage=manual`. - manual args: - "-ignore=SC2129" # ignorable stylistic lint from shellcheck - "-ignore=SC2016" # another shellcheck lint: seems to have false positives? language: golang # means renovate will also update `additional_dependencies` additional_dependencies: # actionlint has a shellcheck integration which extracts shell scripts in `run:` steps from GitHub Actions # and checks these with shellcheck. This is arguably its most useful feature, # but the integration only works if shellcheck is installed - "github.com/wasilibs/go-shellcheck/cmd/shellcheck@v0.11.1" priority: 0 ================================================ FILE: .python-version ================================================ 3.13 ================================================ FILE: BENCHMARKS.md ================================================ # Benchmarks All benchmarks were computed on macOS (Apple M4 Pro 14, 48 GB) with the following tool versions: - [Pyrefly](https://pypi.org/project/pyrefly/) 0.45.2 - [Pyright](https://www.npmjs.com/package/pyright) 1.1.407 - [mypy](https://pypi.org/project/mypy/) \<=1.19.0 - [ty](https://pypi.org/project/ty/) 0.0.2 Benchmark performance may vary across operating systems, and from project to project. This document includes benchmarks from a variety of projects to provide a representative example of real-world usage: [Black](https://github.com/psf/black), [discord.py](https://github.com/Rapptz/discord.py), [Home Assistant](https://github.com/home-assistant/core), [isort](https://github.com/pycqa/isort), [Jinja](https://github.com/pallets/jinja), [pandas](https://github.com/pandas-dev/pandas), [pandas-stubs](https://github.com/pandas-dev/pandas-stubs), [Prefect](https://github.com/PrefectHQ/prefect), and [PyTorch](https://github.com/pytorch/pytorch). For instructions on running the benchmarks, see [`ty_benchmark/README.md`](https://github.com/astral-sh/ruff/blob/7f7485d608d2da19a0632a1238f2d4be551f612f/scripts/ty_benchmark/README.md). ## CLI ```text black ----- Benchmark 1: ty Time (mean ± σ): 53.8 ms ± 1.6 ms [User: 344.9 ms, System: 36.3 ms] Range (min … max): 51.4 ms … 57.9 ms 49 runs Warning: Ignoring non-zero exit code. Benchmark 2: Pyrefly Time (mean ± σ): 167.4 ms ± 5.2 ms [User: 648.8 ms, System: 157.8 ms] Range (min … max): 159.3 ms … 177.0 ms 18 runs Warning: Ignoring non-zero exit code. Benchmark 3: mypy Time (mean ± σ): 1.197 s ± 0.008 s [User: 1.144 s, System: 0.051 s] Range (min … max): 1.186 s … 1.212 s 10 runs Benchmark 4: Pyright Time (mean ± σ): 1.193 s ± 0.020 s [User: 13.264 s, System: 0.808 s] Range (min … max): 1.173 s … 1.240 s 10 runs Warning: Ignoring non-zero exit code. Summary ty ran 3.11 ± 0.13 times faster than Pyrefly 22.16 ± 0.74 times faster than Pyright 22.25 ± 0.66 times faster than mypy ------------------------------------------------------------------------------- discord.py ---------- Benchmark 1: ty Time (mean ± σ): 272.3 ms ± 1.4 ms [User: 1436.3 ms, System: 99.3 ms] Range (min … max): 270.5 ms … 275.6 ms 10 runs Warning: Ignoring non-zero exit code. Benchmark 2: Pyrefly Time (mean ± σ): 312.7 ms ± 23.9 ms [User: 2585.5 ms, System: 244.1 ms] Range (min … max): 283.4 ms … 367.9 ms 10 runs Warning: Ignoring non-zero exit code. Benchmark 3: mypy Time (mean ± σ): 6.617 s ± 0.212 s [User: 6.521 s, System: 0.091 s] Range (min … max): 6.233 s … 6.852 s 10 runs Warning: Ignoring non-zero exit code. Benchmark 4: Pyright Time (mean ± σ): 2.874 s ± 0.078 s [User: 35.269 s, System: 1.710 s] Range (min … max): 2.753 s … 2.964 s 10 runs Warning: Ignoring non-zero exit code. Summary ty ran 1.15 ± 0.09 times faster than Pyrefly 10.55 ± 0.29 times faster than Pyright 24.30 ± 0.79 times faster than mypy ------------------------------------------------------------------------------- homeassistant ------------- Benchmark 1: ty Time (mean ± σ): 2.186 s ± 0.083 s [User: 23.139 s, System: 2.920 s] Range (min … max): 2.067 s … 2.355 s 10 runs Warning: Ignoring non-zero exit code. Benchmark 2: Pyrefly Time (mean ± σ): 5.320 s ± 0.020 s [User: 25.054 s, System: 33.545 s] Range (min … max): 5.294 s … 5.365 s 10 runs Warning: Ignoring non-zero exit code. Benchmark 3: mypy Time (mean ± σ): 45.662 s ± 0.224 s [User: 43.882 s, System: 1.767 s] Range (min … max): 45.328 s … 46.009 s 10 runs Warning: Ignoring non-zero exit code. Benchmark 4: Pyright Time (mean ± σ): 19.623 s ± 0.425 s [User: 227.040 s, System: 21.942 s] Range (min … max): 19.255 s … 20.748 s 10 runs Warning: Ignoring non-zero exit code. Summary ty ran 2.43 ± 0.09 times faster than Pyrefly 8.98 ± 0.39 times faster than Pyright 20.89 ± 0.80 times faster than mypy ------------------------------------------------------------------------------- isort ----- Benchmark 1: ty Time (mean ± σ): 39.0 ms ± 1.1 ms [User: 161.0 ms, System: 21.0 ms] Range (min … max): 36.2 ms … 41.2 ms 67 runs Warning: Ignoring non-zero exit code. Benchmark 2: Pyrefly Time (mean ± σ): 138.5 ms ± 2.8 ms [User: 462.0 ms, System: 82.2 ms] Range (min … max): 132.2 ms … 143.8 ms 21 runs Warning: Ignoring non-zero exit code. Benchmark 3: mypy Time (mean ± σ): 581.2 ms ± 2.2 ms [User: 547.8 ms, System: 31.2 ms] Range (min … max): 578.4 ms … 584.9 ms 10 runs Benchmark 4: Pyright Time (mean ± σ): 2.453 s ± 0.032 s [User: 13.608 s, System: 0.743 s] Range (min … max): 2.402 s … 2.504 s 10 runs Warning: Ignoring non-zero exit code. Summary ty ran 3.55 ± 0.13 times faster than Pyrefly 14.91 ± 0.44 times faster than mypy 62.90 ± 2.02 times faster than Pyright ------------------------------------------------------------------------------- jinja ----- Benchmark 1: ty Time (mean ± σ): 110.2 ms ± 3.3 ms [User: 326.8 ms, System: 27.6 ms] Range (min … max): 107.0 ms … 119.0 ms 26 runs Warning: Ignoring non-zero exit code. Benchmark 2: Pyrefly Time (mean ± σ): 134.5 ms ± 1.7 ms [User: 444.8 ms, System: 87.5 ms] Range (min … max): 131.5 ms … 137.8 ms 21 runs Warning: Ignoring non-zero exit code. Benchmark 3: mypy Time (mean ± σ): 700.9 ms ± 13.2 ms [User: 665.5 ms, System: 33.0 ms] Range (min … max): 693.7 ms … 737.7 ms 10 runs Warning: Ignoring non-zero exit code. Benchmark 4: Pyright Time (mean ± σ): 1.099 s ± 0.014 s [User: 12.235 s, System: 0.736 s] Range (min … max): 1.081 s … 1.127 s 10 runs Warning: Ignoring non-zero exit code. Summary ty ran 1.22 ± 0.04 times faster than Pyrefly 6.36 ± 0.23 times faster than mypy 9.97 ± 0.33 times faster than Pyright ------------------------------------------------------------------------------- pandas ------ Benchmark 1: ty Time (mean ± σ): 551.5 ms ± 56.7 ms [User: 4906.6 ms, System: 222.6 ms] Range (min … max): 467.6 ms … 614.8 ms 10 runs Warning: Ignoring non-zero exit code. Benchmark 2: Pyrefly Time (mean ± σ): 1.174 s ± 0.012 s [User: 11.917 s, System: 1.035 s] Range (min … max): 1.149 s … 1.186 s 10 runs Warning: Ignoring non-zero exit code. Benchmark 3: mypy Time (mean ± σ): 21.366 s ± 0.083 s [User: 21.112 s, System: 0.247 s] Range (min … max): 21.234 s … 21.539 s 10 runs Benchmark 4: Pyright Time (mean ± σ): 6.878 s ± 0.082 s [User: 80.281 s, System: 3.504 s] Range (min … max): 6.749 s … 7.001 s 10 runs Warning: Ignoring non-zero exit code. Summary ty ran 2.13 ± 0.22 times faster than Pyrefly 12.47 ± 1.29 times faster than Pyright 38.74 ± 3.99 times faster than mypy ------------------------------------------------------------------------------- pandas-stubs ------------ Benchmark 1: ty Time (mean ± σ): 83.2 ms ± 2.8 ms [User: 377.9 ms, System: 50.1 ms] Range (min … max): 75.3 ms … 87.1 ms 36 runs Warning: Ignoring non-zero exit code. Benchmark 2: Pyrefly Time (mean ± σ): 253.6 ms ± 6.0 ms [User: 853.5 ms, System: 322.0 ms] Range (min … max): 245.2 ms … 264.2 ms 11 runs Warning: Ignoring non-zero exit code. Benchmark 3: mypy Time (mean ± σ): 5.290 s ± 0.028 s [User: 5.150 s, System: 0.135 s] Range (min … max): 5.248 s … 5.332 s 10 runs Benchmark 4: Pyright Time (mean ± σ): 1.792 s ± 0.030 s [User: 17.300 s, System: 1.199 s] Range (min … max): 1.760 s … 1.843 s 10 runs Summary ty ran 3.05 ± 0.12 times faster than Pyrefly 21.55 ± 0.80 times faster than Pyright 63.61 ± 2.15 times faster than mypy ------------------------------------------------------------------------------- prefect ------- Benchmark 1: ty Time (mean ± σ): 92.4 ms ± 1.1 ms [User: 524.8 ms, System: 70.1 ms] Range (min … max): 90.8 ms … 95.1 ms 30 runs Warning: Ignoring non-zero exit code. Benchmark 2: Pyrefly Time (mean ± σ): 322.3 ms ± 7.9 ms [User: 1061.7 ms, System: 691.8 ms] Range (min … max): 305.0 ms … 330.5 ms 10 runs Warning: Ignoring non-zero exit code. Benchmark 3: mypy Time (mean ± σ): 742.5 ms ± 3.4 ms [User: 702.0 ms, System: 38.1 ms] Range (min … max): 737.6 ms … 749.0 ms 10 runs Warning: Ignoring non-zero exit code. Benchmark 4: Pyright Time (mean ± σ): 3.545 s ± 0.042 s [User: 41.849 s, System: 2.252 s] Range (min … max): 3.507 s … 3.641 s 10 runs Warning: Ignoring non-zero exit code. Summary ty ran 3.49 ± 0.10 times faster than Pyrefly 8.03 ± 0.10 times faster than mypy 38.35 ± 0.64 times faster than Pyright ------------------------------------------------------------------------------- pytorch ------- Benchmark 1: ty Time (mean ± σ): 1.160 s ± 0.115 s [User: 11.200 s, System: 1.344 s] Range (min … max): 1.034 s … 1.314 s 10 runs Warning: Ignoring non-zero exit code. Benchmark 2: Pyrefly Time (mean ± σ): 2.084 s ± 0.029 s [User: 19.660 s, System: 4.377 s] Range (min … max): 2.051 s … 2.143 s 10 runs Warning: Ignoring non-zero exit code. Benchmark 3: mypy Time (mean ± σ): 30.157 s ± 0.155 s [User: 29.776 s, System: 0.373 s] Range (min … max): 29.956 s … 30.454 s 10 runs Warning: Ignoring non-zero exit code. Benchmark 4: Pyright Time (mean ± σ): 12.770 s ± 0.294 s [User: 149.537 s, System: 8.990 s] Range (min … max): 12.357 s … 13.175 s 10 runs Warning: Ignoring non-zero exit code. Summary ty ran 1.80 ± 0.18 times faster than Pyrefly 11.01 ± 1.12 times faster than Pyright 26.01 ± 2.59 times faster than mypy ``` ## LSP ### Incremental edit ```shell ----------------------------------------------------------------------------------------- benchmark 'black': 3 tests ---------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_incremental_edit[black-ty] 8.8965 (1.0) 9.4442 (1.0) 9.1480 (1.0) 0.1953 (1.0) 9.1324 (1.0) 0.3284 (1.0) 4;0 109.3131 (1.0) 10 1 test_incremental_edit[black-pyrefly] 181.7555 (20.43) 192.5020 (20.38) 186.0771 (20.34) 4.5209 (23.15) 183.9968 (20.15) 8.1744 (24.89) 2;0 5.3741 (0.05) 10 1 test_incremental_edit[black-pyright] 418.8096 (47.08) 436.1230 (46.18) 430.3303 (47.04) 5.7802 (29.60) 432.2938 (47.34) 7.9072 (24.08) 3;0 2.3238 (0.02) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------- benchmark 'discord.py': 3 tests ----------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_incremental_edit[discord.py-ty] 11.0239 (1.0) 11.4890 (1.0) 11.2585 (1.0) 0.1475 (1.0) 11.3125 (1.0) 0.2244 (1.0) 4;0 88.8216 (1.0) 10 1 test_incremental_edit[discord.py-pyrefly] 404.7799 (36.72) 540.6511 (47.06) 480.9133 (42.72) 39.8237 (269.90) 486.0451 (42.97) 36.0575 (160.67) 3;1 2.0794 (0.02) 10 1 test_incremental_edit[discord.py-pyright] 438.0625 (39.74) 459.7234 (40.01) 454.7564 (40.39) 7.2835 (49.36) 458.2917 (40.51) 9.2910 (41.40) 2;0 2.1990 (0.02) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------- benchmark 'homeassistant': 3 tests --------------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ test_incremental_edit[homeassistant-ty] 26.2820 (1.0) 26.8146 (1.0) 26.5646 (1.0) 0.1786 (1.0) 26.5610 (1.0) 0.2392 (1.0) 4;0 37.6440 (1.0) 10 1 test_incremental_edit[homeassistant-pyright] 492.1233 (18.72) 513.8475 (19.16) 499.2279 (18.79) 6.6173 (37.05) 497.2621 (18.72) 6.0271 (25.20) 3;1 2.0031 (0.05) 10 1 test_incremental_edit[homeassistant-pyrefly] 1,858.6557 (70.72) 1,979.7762 (73.83) 1,923.3550 (72.40) 38.1201 (213.43) 1,917.0547 (72.18) 48.3089 (201.99) 4;0 0.5199 (0.01) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ---------------------------------------------------------------------------------------- benchmark 'isort': 3 tests ---------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ test_incremental_edit[isort-ty] 9.9467 (1.0) 10.3084 (1.0) 10.1385 (1.0) 0.1117 (1.0) 10.1680 (1.0) 0.1633 (1.0) 3;0 98.6339 (1.0) 10 1 test_incremental_edit[isort-pyrefly] 116.1576 (11.68) 128.3192 (12.45) 122.6576 (12.10) 3.7490 (33.56) 123.0442 (12.10) 5.8752 (35.97) 3;0 8.1528 (0.08) 10 1 test_incremental_edit[isort-pyright] 383.3340 (38.54) 400.4724 (38.85) 390.9956 (38.57) 5.0222 (44.95) 389.3690 (38.29) 6.7132 (41.10) 3;0 2.5576 (0.03) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ---------------------------------------------------------------------------------------- benchmark 'jinja': 3 tests ---------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ test_incremental_edit[jinja-ty] 43.7648 (1.0) 45.4928 (1.0) 44.3021 (1.0) 0.4960 (1.0) 44.2532 (1.0) 0.4335 (1.0) 2;1 22.5723 (1.0) 10 1 test_incremental_edit[jinja-pyrefly] 182.0870 (4.16) 194.4938 (4.28) 189.2674 (4.27) 4.0190 (8.10) 190.3916 (4.30) 2.8280 (6.52) 3;2 5.2835 (0.23) 10 1 test_incremental_edit[jinja-pyright] 424.4407 (9.70) 439.2709 (9.66) 432.3523 (9.76) 5.2093 (10.50) 432.5269 (9.77) 8.0710 (18.62) 4;0 2.3129 (0.10) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ---------------------------------------------------------------------------------------------- benchmark 'pandas': 3 tests ---------------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_incremental_edit[pandas-ty] 69.6266 (1.0) 93.7763 (1.0) 81.3957 (1.0) 7.0073 (1.0) 81.2143 (1.0) 6.5060 (1.0) 3;0 12.2857 (1.0) 10 1 test_incremental_edit[pandas-pyright] 434.3018 (6.24) 540.0721 (5.76) 467.1337 (5.74) 34.8674 (4.98) 457.4263 (5.63) 46.5595 (7.16) 1;0 2.1407 (0.17) 10 1 test_incremental_edit[pandas-pyrefly] 3,756.2226 (53.95) 4,757.2635 (50.73) 4,377.4530 (53.78) 367.8468 (52.49) 4,486.5002 (55.24) 714.0047 (109.74) 5;0 0.2284 (0.02) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------- benchmark 'prefect': 3 tests ---------------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_incremental_edit[prefect-ty] 4.7899 (1.0) 5.3654 (1.0) 5.0291 (1.0) 0.1827 (1.0) 4.9726 (1.0) 0.2836 (1.0) 3;0 198.8408 (1.0) 10 1 test_incremental_edit[prefect-pyright] 537.4210 (112.20) 555.0903 (103.46) 543.9572 (108.16) 5.3413 (29.24) 543.2359 (109.25) 7.0621 (24.90) 4;0 1.8384 (0.01) 10 1 test_incremental_edit[prefect-pyrefly] 2,486.7581 (519.17) 3,972.1852 (740.34) 3,280.6660 (652.33) 500.2204 (>1000.0) 3,231.6573 (649.90) 706.5563 (>1000.0) 3;0 0.3048 (0.00) 10 1 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------- benchmark 'pytorch': 3 tests ---------------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_incremental_edit[pytorch-ty] 4.3493 (1.0) 4.6291 (1.0) 4.4956 (1.0) 0.0940 (1.0) 4.4831 (1.0) 0.1323 (1.0) 4;0 222.4377 (1.0) 10 1 test_incremental_edit[pytorch-pyright] 367.6819 (84.54) 374.5961 (80.92) 370.4936 (82.41) 2.2878 (24.35) 370.5413 (82.65) 3.5865 (27.12) 2;0 2.6991 (0.01) 10 1 test_incremental_edit[pytorch-pyrefly] 2,333.3450 (536.48) 2,889.9276 (624.30) 2,604.7441 (579.39) 202.1562 (>1000.0) 2,582.8069 (576.12) 380.2684 (>1000.0) 4;0 0.3839 (0.00) 10 1 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ``` ### Fetch diagnostics ```shell ----------------------------------------------------------------------------------------- benchmark 'black': 3 tests ---------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_fetch_diagnostics[black-ty] 40.5680 (1.0) 45.9828 (1.0) 43.4315 (1.0) 1.9415 (3.59) 43.4317 (1.0) 3.6550 (5.37) 5;0 23.0247 (1.0) 10 1 test_fetch_diagnostics[black-pyrefly] 131.4388 (3.24) 133.1042 (2.89) 132.3125 (3.05) 0.5407 (1.0) 132.2801 (3.05) 0.6808 (1.0) 3;0 7.5579 (0.33) 10 1 test_fetch_diagnostics[black-pyright] 234.5532 (5.78) 261.0507 (5.68) 241.2080 (5.55) 7.5489 (13.96) 238.8710 (5.50) 3.9942 (5.87) 1;1 4.1458 (0.18) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------- benchmark 'discord.py': 3 tests ----------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ test_fetch_diagnostics[discord.py-ty] 88.1260 (1.0) 91.9354 (1.0) 89.9785 (1.0) 1.2617 (1.0) 89.8813 (1.0) 1.9953 (1.33) 3;0 11.1138 (1.0) 10 1 test_fetch_diagnostics[discord.py-pyrefly] 438.9406 (4.98) 462.2016 (5.03) 442.3538 (4.92) 7.0283 (5.57) 439.9846 (4.90) 1.4996 (1.0) 1;1 2.2606 (0.20) 10 1 test_fetch_diagnostics[discord.py-pyright] 488.1340 (5.54) 501.5540 (5.46) 493.6927 (5.49) 3.7678 (2.99) 493.7276 (5.49) 3.5742 (2.38) 3;1 2.0256 (0.18) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------- benchmark 'homeassistant': 3 tests ---------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_fetch_diagnostics[homeassistant-ty] 109.6321 (1.0) 112.8913 (1.0) 111.4661 (1.0) 1.1174 (1.0) 111.4080 (1.0) 1.7967 (1.0) 5;0 8.9713 (1.0) 10 1 test_fetch_diagnostics[homeassistant-pyrefly] 218.1328 (1.99) 238.8827 (2.12) 222.4006 (2.00) 6.7033 (6.00) 219.6265 (1.97) 4.9113 (2.73) 2;1 4.4964 (0.50) 10 1 test_fetch_diagnostics[homeassistant-pyright] 888.3600 (8.10) 938.0845 (8.31) 901.1896 (8.08) 18.2062 (16.29) 893.4438 (8.02) 5.7174 (3.18) 2;2 1.1096 (0.12) 10 1 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------- benchmark 'isort': 3 tests ---------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_fetch_diagnostics[isort-ty] 41.8193 (1.0) 47.1411 (1.0) 43.6068 (1.0) 1.7622 (1.0) 43.1409 (1.0) 2.2658 (1.0) 3;0 22.9322 (1.0) 10 1 test_fetch_diagnostics[isort-pyrefly] 103.6808 (2.48) 110.2636 (2.34) 105.9967 (2.43) 2.6978 (1.53) 104.5978 (2.42) 5.5226 (2.44) 3;0 9.4343 (0.41) 10 1 test_fetch_diagnostics[isort-pyright] 297.8888 (7.12) 327.5334 (6.95) 306.2138 (7.02) 8.7235 (4.95) 304.5766 (7.06) 6.0185 (2.66) 1;1 3.2657 (0.14) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------- benchmark 'jinja': 3 tests ---------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_fetch_diagnostics[jinja-ty] 114.6546 (1.0) 129.8110 (1.0) 120.6856 (1.0) 5.6731 (3.75) 119.8813 (1.0) 9.1737 (7.27) 3;0 8.2860 (1.0) 10 1 test_fetch_diagnostics[jinja-pyrefly] 134.1455 (1.17) 139.0428 (1.07) 136.2655 (1.13) 1.5126 (1.0) 135.7366 (1.13) 1.2612 (1.0) 3;1 7.3386 (0.89) 10 1 test_fetch_diagnostics[jinja-pyright] 280.1174 (2.44) 305.6963 (2.35) 287.5077 (2.38) 8.9505 (5.92) 283.8312 (2.37) 10.0400 (7.96) 2;0 3.4782 (0.42) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------- benchmark 'pandas': 3 tests ---------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_fetch_diagnostics[pandas-ty] 289.0480 (1.0) 294.6924 (1.0) 292.1341 (1.0) 1.6226 (1.0) 292.2783 (1.0) 2.4183 (2.01) 2;0 3.4231 (1.0) 10 1 test_fetch_diagnostics[pandas-pyrefly] 522.8750 (1.81) 529.1068 (1.80) 524.6167 (1.80) 1.9387 (1.19) 524.1335 (1.79) 1.2023 (1.0) 2;2 1.9062 (0.56) 10 1 test_fetch_diagnostics[pandas-pyright] 928.4370 (3.21) 949.3941 (3.22) 941.8812 (3.22) 5.6550 (3.49) 943.2703 (3.23) 4.2351 (3.52) 2;1 1.0617 (0.31) 10 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------- benchmark 'prefect': 3 tests ---------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_fetch_diagnostics[prefect-ty] 123.5941 (1.0) 127.0129 (1.0) 125.3534 (1.0) 1.0498 (1.02) 125.1861 (1.0) 1.4461 (1.10) 3;0 7.9774 (1.0) 10 1 test_fetch_diagnostics[prefect-pyrefly] 437.5048 (3.54) 441.0088 (3.47) 438.7041 (3.50) 1.0287 (1.0) 438.4152 (3.50) 1.3126 (1.0) 2;0 2.2794 (0.29) 10 1 test_fetch_diagnostics[prefect-pyright] 828.5065 (6.70) 884.9894 (6.97) 845.3657 (6.74) 14.9587 (14.54) 842.5912 (6.73) 8.1088 (6.18) 2;1 1.1829 (0.15) 10 1 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------- benchmark 'pytorch': 3 tests ----------------------------------------------------------------------------------------- Name (time in ms) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- test_fetch_diagnostics[pytorch-ty] 51.7146 (1.0) 59.2463 (1.0) 54.3685 (1.0) 2.2254 (2.06) 54.3844 (1.0) 3.0549 (2.12) 2;0 18.3930 (1.0) 10 1 test_fetch_diagnostics[pytorch-pyrefly] 174.7057 (3.38) 178.1138 (3.01) 175.7364 (3.23) 1.0824 (1.0) 175.5052 (3.23) 1.4398 (1.0) 1;0 5.6903 (0.31) 10 1 test_fetch_diagnostics[pytorch-pyright] 447.4101 (8.65) 474.9993 (8.02) 460.5399 (8.47) 9.8641 (9.11) 461.9636 (8.49) 18.7404 (13.02) 3;0 2.1714 (0.12) 10 1 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ``` ================================================ FILE: CHANGELOG.md ================================================ # Changelog ## 0.0.24 Released on 2026-03-19. ### Bug fixes - Ensure `TypedDict` subscripts for unknown keys return `Unknown` ([#23926](https://github.com/astral-sh/ruff/pull/23926)) - Fix overflow with recursive `TypeIs` ([#23784](https://github.com/astral-sh/ruff/pull/23784)) - Fix variance of frozen dataclass-transform models ([#23931](https://github.com/astral-sh/ruff/pull/23931)) ### LSP server - Improve [semantic token](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens) classification for attribute access on union types ([#23841](https://github.com/astral-sh/ruff/pull/23841)) ### Core type checking - Improve performance and correctness by avoiding inferring intersection types for call arguments as a result of bidirectional inference ([#23933](https://github.com/astral-sh/ruff/pull/23933)) - Narrow keyword arguments when unpacking dictionary instances ([#23436](https://github.com/astral-sh/ruff/pull/23436)) - Discover `/usr/local/lib` dist-packages on Debian/Ubuntu ([#23797](https://github.com/astral-sh/ruff/pull/23797)) - Sync vendored typeshed stubs ([#23963](https://github.com/astral-sh/ruff/pull/23963)). [Typeshed diff](https://github.com/python/typeshed/compare/fa659b1def704dea3dc8e25c7857b23eac69df4d...f8f0794d0fe249c06dc9f31a004d85be6cca6ced) ## Performance - Introduce fast path for protocol non-assignability ([#23952](https://github.com/astral-sh/ruff/pull/23952)) - Improved generic-solver performance in cases involving overload sets ([#23881](https://github.com/astral-sh/ruff/pull/23881)) ### Contributors - [@Geo5](https://github.com/Geo5) - [@ibraheemdev](https://github.com/ibraheemdev) - [@charliermarsh](https://github.com/charliermarsh) - [@dcreager](https://github.com/dcreager) - [@ollema](https://github.com/ollema) - [@sharkdp](https://github.com/sharkdp) ## 0.0.23 Released on 2026-03-13. ### Bug fixes - Fix false-positive diagnostics for PEP-604 union annotations on attribute targets on Python 3.9 when `from __future__ import annotations` is active ([#23915](https://github.com/astral-sh/ruff/pull/23915)) - `dataclass_transform`: Respect `kw_only` overwrites in dataclasses ([#23930](https://github.com/astral-sh/ruff/pull/23930)) - Fix too-many-cycle panics when inferring loop variables with `Literal` types ([#23875](https://github.com/astral-sh/ruff/pull/23875)) ### Server - Fix [folding range](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_foldingRange) classification of lines starting with `#` ([#23831](https://github.com/astral-sh/ruff/pull/23831)) - Fix [folding ranges](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_foldingRange) for notebooks ([#23830](https://github.com/astral-sh/ruff/pull/23830)) ### Core type checking - Split errors for possibly missing submodules into a new `possibly-missing-submodule` error code (enabled by default), and make `possibly-missing-attribute` ignored by default ([#23918](https://github.com/astral-sh/ruff/pull/23918)) - Improve handling of bidirectional inference when ([#23844](https://github.com/astral-sh/ruff/pull/23844)) - Fix inference of conditionally defined properties ([#23925](https://github.com/astral-sh/ruff/pull/23925)) ### Improvements to diagnostics - Clarify in diagnostics that `from __future__ import annotations` only stringifies type annotations ([#23928](https://github.com/astral-sh/ruff/pull/23928)) ### Performance improvements - Avoid duplicated work during multi-inference ([#23923](https://github.com/astral-sh/ruff/pull/23923)) ### Contributors - [@ibraheemdev](https://github.com/ibraheemdev) - [@sharkdp](https://github.com/sharkdp) - [@oconnor663](https://github.com/oconnor663) - [@mtshiba](https://github.com/mtshiba) - [@MichaReiser](https://github.com/MichaReiser) - [@carljm](https://github.com/carljm) - [@AlexWaygood](https://github.com/AlexWaygood) ## 0.0.22 Released on 2026-03-12. ### Bug fixes - Fix issue where variables could be inferred as `Divergent` if they were assigned using tuple unpacking in loops ([#23812](https://github.com/astral-sh/ruff/pull/23812)) - Allow `error = "all"` in a root `pyproject.toml` file to be overridden using `tool.ty.overrides` in a subdirectory's `pyproject.toml` file ([#23712](https://github.com/astral-sh/ruff/pull/23712)) - Only unsoundly upcast `type[]` types to their constructor `Callable` type during assignability checks, not during redundancy/subtyping checks ([#23834](https://github.com/astral-sh/ruff/pull/23834), [#23901](https://github.com/astral-sh/ruff/pull/23901)) - Fix stack overflow that could occur with certain recursive protocols ([#23870](https://github.com/astral-sh/ruff/pull/23870)) ### LSP server - Improve syntax highlighting by fixing [semantic token](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens) classification for generic class members in annotations ([#23811](https://github.com/astral-sh/ruff/pull/23811)) ### CLI - Add `ty explain ` CLI command ([#23766](https://github.com/astral-sh/ruff/pull/23766)) ### Core type checking - Add validation for type parameters with defaults after `TypeVarTuple` parameters ([#23807](https://github.com/astral-sh/ruff/pull/23807)) - Allow subtypes of `LiteralString` to be narrowed using equality checks ([#23794](https://github.com/astral-sh/ruff/pull/23794)) - Detect invalid partially stringified PEP-604 unions ([#23285](https://github.com/astral-sh/ruff/pull/23285)) - Disambiguate duplicate-looking overloaded callables in union display ([#23907](https://github.com/astral-sh/ruff/pull/23907)) - Don't promote module-literal types to `types.ModuleType` ([#23786](https://github.com/astral-sh/ruff/pull/23786)) - Improve type context support for `__setitem__` dunder calls ([#23800](https://github.com/astral-sh/ruff/pull/23800)) - Infer `t | {"foo": int}` as `TD` if `t` is an instance of a TypedDict `td` with a `foo: int` key ([#23806](https://github.com/astral-sh/ruff/pull/23806)) - Narrow `T` to `T & str` rather than `str` if `T` is a constrained TypeVar with `str` as one of its constraints ([#23850](https://github.com/astral-sh/ruff/pull/23850)) - Promote `None` to `None | Unknown` in invariant contexts ([#23790](https://github.com/astral-sh/ruff/pull/23790)) - Reject `type[Callable]` in type annotations ([#23753](https://github.com/astral-sh/ruff/pull/23753)) - Support enum member access through enum instances and members ([#23772](https://github.com/astral-sh/ruff/pull/23772)) - Eagerly narrow the type associated with keys of heterogeneous dict literals even when a dict literal appears as a subexpression inside a list or tuple literals ([#23569](https://github.com/astral-sh/ruff/pull/23569)) - Ensure that `T & ~S` is always inferred as a subtype of `U` during generic inference if `T` is understood as a subtype of `U` ([#23728](https://github.com/astral-sh/ruff/pull/23728)) - Optimize and improve cycle recovery by preventing "tainted" unions in cycle normalization ([#23563](https://github.com/astral-sh/ruff/pull/23563)) ### Installer - Prefer downloading releases from Astral's mirror over GitHub ([#2980](https://github.com/astral-sh/ty/pull/2980)) ### Contributors - [@charliermarsh](https://github.com/charliermarsh) - [@MichaReiser](https://github.com/MichaReiser) - [@mtshiba](https://github.com/mtshiba) - [@AlexWaygood](https://github.com/AlexWaygood) - [@ibraheemdev](https://github.com/ibraheemdev) - [@ntBre](https://github.com/ntBre) - [@oconnor663](https://github.com/oconnor663) - [@zsol](https://github.com/zsol) ## 0.0.21 Released on 2026-03-05. ### Bug fixes - Avoid stack overflow with recursive typevar ([#23652](https://github.com/astral-sh/ruff/pull/23652)) - Fix panic on incomplete except handlers ([#23708](https://github.com/astral-sh/ruff/pull/23708)) - Allow unions of different-length iterables in `*args` unpacking into optional positional parameters ([#23124](https://github.com/astral-sh/ruff/pull/23124)) - Don't replace `Any` attributes with `object` after `isinstance` narrowing ([#23725](https://github.com/astral-sh/ruff/pull/23725)) ### LSP server - Exclude decorators from class/def code folding ([#23543](https://github.com/astral-sh/ruff/pull/23543)) - Fix handling of non-Python text documents ([#23704](https://github.com/astral-sh/ruff/pull/23704)) ### Configuration - Add `all` selector to `ty.json` schema ([#23721](https://github.com/astral-sh/ruff/pull/23721)) - Fix precedence of `all` selector in TOML configurations ([#23723](https://github.com/astral-sh/ruff/pull/23723)) - Make `all` selector case-sensitive ([#23713](https://github.com/astral-sh/ruff/pull/23713)) ### Type checking - Add `invalid-enum-member-annotation` lint rule ([#23648](https://github.com/astral-sh/ruff/pull/23648)) - Add a diagnostic for an unused awaitable ([#23650](https://github.com/astral-sh/ruff/pull/23650)) - Add a diagnostic if a `TypeVar` is used to specialize a `ParamSpec`, or vice versa ([#23738](https://github.com/astral-sh/ruff/pull/23738)) - Add more type-variable default validation ([#23639](https://github.com/astral-sh/ruff/pull/23639)) - Add unbound type variable detection in annotations ([#23641](https://github.com/astral-sh/ruff/pull/23641)) - Apply narrowing to walrus values ([#23687](https://github.com/astral-sh/ruff/pull/23687)) - Do not union `Unknown` into unannotated container types ([#23718](https://github.com/astral-sh/ruff/pull/23718)) - Avoid inferring generics with negative intersections ([#23750](https://github.com/astral-sh/ruff/pull/23750)) - More precise types for `name` and `value` of an enum ([#23683](https://github.com/astral-sh/ruff/pull/23683)) - Better preserve user-provided union order in inferred specializations ([#23715](https://github.com/astral-sh/ruff/pull/23715)) - Support narrowing in ternary expressions ([#23726](https://github.com/astral-sh/ruff/pull/23726)) - Validate bare ParamSpec usage in type annotations, and support stringified ParamSpecs as the first argument to `Callable` ([#23625](https://github.com/astral-sh/ruff/pull/23625)) ### Contributors - [@charliermarsh](https://github.com/charliermarsh) - [@zsol](https://github.com/zsol) - [@Gankra](https://github.com/Gankra) - [@MichaReiser](https://github.com/MichaReiser) - [@AlexWaygood](https://github.com/AlexWaygood) - [@rizzip](https://github.com/rizzip) - [@iksuddle](https://github.com/iksuddle) - [@dcreager](https://github.com/dcreager) - [@BurntSushi](https://github.com/BurntSushi) - [@carljm](https://github.com/carljm) - [@oconnor663](https://github.com/oconnor663) ## 0.0.20 Released on 2026-03-02. ### Bug fixes - Disallow negative narrowing for `isinstance()` or `issubclass()` checks involving `type[]` types ([#23598](https://github.com/astral-sh/ruff/pull/23598)) - Fix binary operations between an instance of a `NewType` of `float` and an instance of `Any`/`Unknown` ([#23620](https://github.com/astral-sh/ruff/pull/23620)) - Fix bug where ty would think that a `Callable` with a variadic positional parameter could be a subtype of a `Callable` with a positional-or-keyword parameter ([#23610](https://github.com/astral-sh/ruff/pull/23610)) - Fix inference of `t.__mro__` if `t` is an instance of `type[Any]` ([#23632](https://github.com/astral-sh/ruff/pull/23632)) - Fix overloaded callable assignability for unary `Callable` targets ([#23277](https://github.com/astral-sh/ruff/pull/23277)) - Limit recursion depth when displaying self-referential function types ([#23647](https://github.com/astral-sh/ruff/pull/23647)) - Ensure that `python -m ty` works even when ty was installed into an ephemeral virtual environment ([#2852](https://github.com/astral-sh/ty/pull/2852)) ### LSP server - Add support for the LSP protocol's "type hierarchy" feature ([#23566](https://github.com/astral-sh/ruff/pull/23566)) ### Type checking - Add more ParamSpec validation for `P.args` and `P.kwargs` ([#23640](https://github.com/astral-sh/ruff/pull/23640)) - Ban nested `Required`/`NotRequired`, and ban them both outside of `TypedDict` fields ([#23627](https://github.com/astral-sh/ruff/pull/23627)) - Detect inconsistent generic base class specializations that appear in the same MRO ([#23615](https://github.com/astral-sh/ruff/pull/23615)) - Detect invalid uses of `@final` on non-methods ([#23604](https://github.com/astral-sh/ruff/pull/23604)) - Add partial support and validation for `Unpack` when used with tuple types ([#23651](https://github.com/astral-sh/ruff/pull/23651)) - Recurse into tuples and nested tuples when applying special-cased validation of arguments passed to `isinstance()` and `issubclass()` ([#23607](https://github.com/astral-sh/ruff/pull/23607)) - Reject ellipsis literals in odd places in type/annotation expressions ([#23611](https://github.com/astral-sh/ruff/pull/23611)) - Reject functions with PEP-695 type parameters that shadow type parameters from enclosing scopes ([#23619](https://github.com/astral-sh/ruff/pull/23619)) - Reject generic metaclasses parameterized by type variables ([#23628](https://github.com/astral-sh/ruff/pull/23628)) - Treat `dataclass_transform` dataclasses as neither frozen nor non-frozen ([#23366](https://github.com/astral-sh/ruff/pull/23366)) - Validate that type variable defaults don't reference later type parameters or type parameters out of scope ([#23623](https://github.com/astral-sh/ruff/pull/23623)) ### Typeshed - Sync vendored typeshed stubs ([#23642](https://github.com/astral-sh/ruff/pull/23642)). [Typeshed diff](https://github.com/python/typeshed/compare/1b3cec156330a93f6bb22b6636bca38c27f8f721...843c1fd5a148da85e523c1b4ee680226f89986aa) ### Contributors - [@Hugo-Polloli](https://github.com/Hugo-Polloli) - [@zanieb](https://github.com/zanieb) - [@sharkdp](https://github.com/sharkdp) - [@mtshiba](https://github.com/mtshiba) - [@carljm](https://github.com/carljm) - [@charliermarsh](https://github.com/charliermarsh) - [@sinon](https://github.com/sinon) - [@BurntSushi](https://github.com/BurntSushi) - [@oconnor663](https://github.com/oconnor663) - [@AlexWaygood](https://github.com/AlexWaygood) - [@zsol](https://github.com/zsol) ## 0.0.19 Released on 2026-02-26. ### Bug fixes - Fix panic in diagnostic rendering when attempting to render a code frame pointing to leading whitespace ([#23458](https://github.com/astral-sh/ruff/pull/23458)) - Fix panics and incorrect inference stemming from incorrectly considering overloads in another file as being associated with a function in the file being checked ([#21977](https://github.com/astral-sh/ruff/pull/21977)) - Fix panic when attempting to narrow the type of a dictionary key that was assigned using a multi-target assignment, e.g. `x = y = {"a": 1}` ([#23523](https://github.com/astral-sh/ruff/pull/23523)) - Fix infinite hang on mutually recursive `TypeAliasType` definitions ([#23397](https://github.com/astral-sh/ruff/pull/23397)) ### LSP server - Fix inlay hints for starred unpacking targets ([#23454](https://github.com/astral-sh/ruff/pull/23454)) ### Core type checking - Fix assignability, subtyping and equivalence checks relating to `typing.Generator` prior to Python 3.13 ([#23386](https://github.com/astral-sh/ruff/pull/23386)) - Understand that a scope's control flow terminates after `await foo()` if `foo` returns `typing.Awaitable[typing.Never]` or similar ([#23479](https://github.com/astral-sh/ruff/pull/23479)) - Implement stricter handling of calls to instances of `type[T]` types ([#23472](https://github.com/astral-sh/ruff/pull/23472)) - Support basic type narrowing for `case {...}:` patterns in `match` statements ([#23462](https://github.com/astral-sh/ruff/pull/23462)) - Fix bugs that could manifest in incorrect overload evaluation, false-positive complaints regarding `assert_type` calls or false-positive `redundant-cast` diagnostics by reimplementing the equivalence type relation as mutual subtyping of top and bottom materializations ([#23428](https://github.com/astral-sh/ruff/pull/23428)) - Fix equality and `__contains__` narrowing with PEP-695 type aliases ([#23545](https://github.com/astral-sh/ruff/pull/23545)) - Support `_value_` annotations on enum classes ([#22228](https://github.com/astral-sh/ruff/pull/22228)) ### Improvements to diagnostics - Improve diagnostics when attempting to specialize non-generic types ([#23516](https://github.com/astral-sh/ruff/pull/23516)) - Render subdiagnostics when `--output-format=github` is specified ([#23455](https://github.com/astral-sh/ruff/pull/23455)) ### Performance - Add a cached method for calculating the intersection of two types ([#23547](https://github.com/astral-sh/ruff/pull/23547)) - Add a cached method for calculating the union of two types ([#23565](https://github.com/astral-sh/ruff/pull/23565)) - Reduce the threshold above which `Literal` types in unions are upcasted to nominal-instance types in situations where the union type is recursively defined ([#23521](https://github.com/astral-sh/ruff/pull/23521)) - Control flow: isolate the calculation of "loop header reachability" in a dedicated, cached function ([#23520](https://github.com/astral-sh/ruff/pull/23520)) ### Contributors - [@AlexWaygood](https://github.com/AlexWaygood) - [@silamon](https://github.com/silamon) - [@ibraheemdev](https://github.com/ibraheemdev) - [@Hugo-Polloli](https://github.com/Hugo-Polloli) - [@charliermarsh](https://github.com/charliermarsh) - [@knutwannheden](https://github.com/knutwannheden) - [@oconnor663](https://github.com/oconnor663) - [@carljm](https://github.com/carljm) - [@mtshiba](https://github.com/mtshiba) ## 0.0.18 Released on 2026-02-20. ### Bug fixes - Support classes dynamically created via `type(...)` with cyclic bases ([#22792](https://github.com/astral-sh/ruff/pull/22792)) - Fix incorrect types inferred when unpacking mixed tuples ([#23437](https://github.com/astral-sh/ruff/pull/23437)) - Fix stack overflow for self-referential `TypeOf` in annotations ([#23407](https://github.com/astral-sh/ruff/pull/23407)) - Fix several server panics that could occur when computing semantic tokens for the current file ([#23403](https://github.com/astral-sh/ruff/pull/23403)), [#23398](https://github.com/astral-sh/ruff/pull/23398), [#23401](https://github.com/astral-sh/ruff/pull/23401)) ### LSP server - Add code folding support ([#23393](https://github.com/astral-sh/ruff/pull/23393)) - Add warning message when running `ty server` interactively ([#23416](https://github.com/astral-sh/ruff/pull/23416)) - Exclude test-related symbols from non-first-party packages in auto-import completions ([#23252](https://github.com/astral-sh/ruff/pull/23252)) - Fix bug where diagnostics could disappear after opening an external file ([#23447](https://github.com/astral-sh/ruff/pull/23447)) - Remove spurious destination for Go-To Definition on variables defined in a loop ([#23391](https://github.com/astral-sh/ruff/pull/23391)) - Use the fully qualified name when "baking" an inlay hint into the source code if the scope already contains a variable with the same name as the unqualified name ([#23265](https://github.com/astral-sh/ruff/pull/23265)) - Resolve TypeVars in `call_signature_details` parameter types ([#23149](https://github.com/astral-sh/ruff/pull/23149)) ### CLI - Add `--output-format` to `ty version` ([#23387](https://github.com/astral-sh/ruff/pull/23387)) ### Configuration - Add `replace-imports-with-any` option ([#23122](https://github.com/astral-sh/ruff/pull/23122)) - Support shellexpand for configuration paths ([#23274](https://github.com/astral-sh/ruff/pull/23274)) ### Type checking - Add a new diagnostic to detect invalid class patterns in `match` statements ([#22939](https://github.com/astral-sh/ruff/pull/22939)) - Allow `Self` in `ClassVar` type annotations ([#23362](https://github.com/astral-sh/ruff/pull/23362)) - Consider synthesized methods and `ClassVar`-qualified declarations when determining whether an abstract method has been overridden in a subclass ([#23381](https://github.com/astral-sh/ruff/pull/23381)) - Add a diagnostic when combining `Final` and `ClassVar` ([#23365](https://github.com/astral-sh/ruff/pull/23365)) - Fix return type of `assert_never` ([#23389](https://github.com/astral-sh/ruff/pull/23389)) - Fix `assert_type` diagnostic messages ([#23342](https://github.com/astral-sh/ruff/pull/23342)) - Ban PEP-613 type alias values from containing type-qualifier special forms ([#23444](https://github.com/astral-sh/ruff/pull/23444)) - Infer `LiteralString` for `f"{literal_str_a} {literal_str_b}"` ([#23346](https://github.com/astral-sh/ruff/pull/23346)) - Infer precise types for bit-shift operations on integer literals ([#23301](https://github.com/astral-sh/ruff/pull/23301)) - Make `[abstract-method-in-final-class]` diagnostics less verbose for classes with many abstract methods ([#23379](https://github.com/astral-sh/ruff/pull/23379)) - Improve diagnostics for abstract `@final` classes ([#23376](https://github.com/astral-sh/ruff/pull/23376)) - Only perform literal promotion for implicitly inferred literals ([#23107](https://github.com/astral-sh/ruff/pull/23107)) - Parenthesize callable types when they appear in the return annotation of other callable types ([#23327](https://github.com/astral-sh/ruff/pull/23327)) - Consider a call to a generic function returning `Never` to terminate control flow ([#23419](https://github.com/astral-sh/ruff/pull/23419)) - Support calls to intersection types ([#22469](https://github.com/astral-sh/ruff/pull/22469)) - Validate annotated assignments to attributes on self ([#23388](https://github.com/astral-sh/ruff/pull/23388)) - Treat a bytes-literal type as a subtype of `Sequence[]` ([#23329](https://github.com/astral-sh/ruff/pull/23329)) - Allow a string-literal argument to match against an `Iterable` parameter in type variable inference. ([#23326](https://github.com/astral-sh/ruff/pull/23326)) - Support narrowing from a `Callable` type returning a `TypeGuard` type ([#23280](https://github.com/astral-sh/ruff/pull/23280)) ### Performance - Consider all code paths as being ambiguously reachable in cases with pathologically large control-flow graphs ([#23399](https://github.com/astral-sh/ruff/pull/23399)) ### Typeshed - Sync vendored typeshed stubs ([#23279](https://github.com/astral-sh/ruff/pull/23279), [Typeshed diff](https://github.com/python/typeshed/compare/fa659b1def704dea3dc8e25c7857b23eac69df4d...1b3cec156330a93f6bb22b6636bca38c27f8f721)) ### Contributors - [@toby-bro](https://github.com/toby-bro) - [@Hugo-Polloli](https://github.com/Hugo-Polloli) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@BurntSushi](https://github.com/BurntSushi) - [@ibraheemdev](https://github.com/ibraheemdev) - [@knutwannheden](https://github.com/knutwannheden) - [@Glyphack](https://github.com/Glyphack) - [@charliermarsh](https://github.com/charliermarsh) - [@AlexWaygood](https://github.com/AlexWaygood) - [@abhijeetbodas2001](https://github.com/abhijeetbodas2001) - [@carljm](https://github.com/carljm) - [@sharkdp](https://github.com/sharkdp) ## 0.0.17 Released on 2026-02-13. ### Bug fixes - Avoid `Literal` promotion for constrained `TypeVar`s with `Literal` bounds ([#23209](https://github.com/astral-sh/ruff/pull/23209)) - Fix false positives in `TypeVar` shadowing checks ([#23222](https://github.com/astral-sh/ruff/pull/23222)) ### Core type checking - Support generic protocols ([#21902](https://github.com/astral-sh/ruff/pull/21902)) - Perform control-flow analysis in loops ([#22794](https://github.com/astral-sh/ruff/pull/22794)) - Support `typing.Self` in attribute annotations ([#23108](https://github.com/astral-sh/ruff/pull/23108)) - Support type narrowing in situations with calls to `NoReturn` functions ([#23109](https://github.com/astral-sh/ruff/pull/23109)) - Support type narrowing and reachability analysis based on `os.name` checks ([#23230](https://github.com/astral-sh/ruff/pull/23230)) - Detect overrides of `Final` class variables in subclasses ([#23180](https://github.com/astral-sh/ruff/pull/23180)) - Fix bound method access on `None` ([#23246](https://github.com/astral-sh/ruff/pull/23246)) - Fix method calls on subclasses of `Any` ([#23248](https://github.com/astral-sh/ruff/pull/23248)) - Disallow type variables within PEP-695 type variable bounds and constraints ([#22982](https://github.com/astral-sh/ruff/pull/22982)) - Emit error for attribute access on union where some elements lack the attribute ([#23042](https://github.com/astral-sh/ruff/pull/23042)) - Emit error for invalid typevar defaults ([#23194](https://github.com/astral-sh/ruff/pull/23194)) - Improve display of `ParamSpec`s in some situations ([#23211](https://github.com/astral-sh/ruff/pull/23211)) ### LSP server - Add hover and go-to-declaration support for subscript literals ([#22837](https://github.com/astral-sh/ruff/pull/22837)) - Assign lower completion ranking to deprecated names in auto import ([#23188](https://github.com/astral-sh/ruff/pull/23188)) - Improve spans of references to submodules imported in an `__init__.py` ([#21795](https://github.com/astral-sh/ruff/pull/21795)) - Include conditional symbols (like `datetime.UTC`) in auto-import in more cases ([#23249](https://github.com/astral-sh/ruff/pull/23249)) - Support auto-import for symbols in inlay hints ([#22111](https://github.com/astral-sh/ruff/pull/22111)) - Include overload declarations in find-references ([#23215](https://github.com/astral-sh/ruff/pull/23215)) ### Performance - Avoid `UnionBuilder` overhead when creating a new union from the filtered elements of an existing union ([#22352](https://github.com/astral-sh/ruff/pull/22352)) ### Other changes - Allow discovering dependencies in system Python environments ([#22994](https://github.com/astral-sh/ruff/pull/22994)) - Apply workspace settings to virtual files ([#23228](https://github.com/astral-sh/ruff/pull/23228)) - Add support for `--output-format=junit` ([#22125](https://github.com/astral-sh/ruff/pull/22125)) - Use a smaller diagnostic range for `inconsistent-mro` diagnostics ([#23213](https://github.com/astral-sh/ruff/pull/23213)) ### Contributors - [@carljm](https://github.com/carljm) - [@BurntSushi](https://github.com/BurntSushi) - [@charliermarsh](https://github.com/charliermarsh) - [@Glyphack](https://github.com/Glyphack) - [@cetanu](https://github.com/cetanu) - [@AlexWaygood](https://github.com/AlexWaygood) - [@joelostblom](https://github.com/joelostblom) - [@Gankra](https://github.com/Gankra) - [@mtshiba](https://github.com/mtshiba) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@Hugo-Polloli](https://github.com/Hugo-Polloli) - [@sharkdp](https://github.com/sharkdp) - [@alex](https://github.com/alex) - [@dcreager](https://github.com/dcreager) - [@oconnor663](https://github.com/oconnor663) ## 0.0.16 Released on 2026-02-10. ### Bug fixes - Allow stringified argument in PEP-613 alias to `Optional` ([#23200](https://github.com/astral-sh/ruff/pull/23200)) - Fix fuzzer panic on slice expression in unclosed comprehension ([#23146](https://github.com/astral-sh/ruff/pull/23146)) - Fix combinatorial explosion due to fixed-length tuple expansion in overload matching ([#23190](https://github.com/astral-sh/ruff/pull/23190)) - Respect `@no_type_check` when combined with other decorators ([#23177](https://github.com/astral-sh/ruff/pull/23177)) - Fix diagnostic location for an incorrect sub-call to a specialized ParamSpec ([#23036](https://github.com/astral-sh/ruff/pull/23036)) ### LSP server - Assign lower completions ranking to deprecated functions and classes ([#23089](https://github.com/astral-sh/ruff/pull/23089)) - Change goto-def for class constructors to always go to class definition ([#23071](https://github.com/astral-sh/ruff/pull/23071)) - Ensure diagnostic mode is consistent across projects inside the LSP server ([#23121](https://github.com/astral-sh/ruff/pull/23121)) - Don't include the class `Foo` in autocomplete suggestions when the user is typing out `Foo`'s bases ([#23141](https://github.com/astral-sh/ruff/pull/23141)) - Fix parameter references across files via keyword args ([#23012](https://github.com/astral-sh/ruff/pull/23012)) - Fix wrong inlay hints for overloaded function arguments ([#23179](https://github.com/astral-sh/ruff/pull/23179)) - Support diagnostics in newly created files inside neovim ([#23095](https://github.com/astral-sh/ruff/pull/23095)) - Exclude already-included classes when providing completion suggestions for class bases ([#23085](https://github.com/astral-sh/ruff/pull/23085)) ### CLI - Add support for `TY_OUTPUT_FORMAT` environment variable ([#23123](https://github.com/astral-sh/ruff/pull/23123)) - Fall back to `python3` found in `$PATH` if no environment is found ([#22843](https://github.com/astral-sh/ruff/pull/22843)) ### Type checking - Add `inconsistent-mro` autofix to move `Generic[]` to the end of the bases list ([#22998](https://github.com/astral-sh/ruff/pull/22998)) - Add precise return-type inference for `struct.unpack` ([#22562](https://github.com/astral-sh/ruff/pull/22562), [#23130](https://github.com/astral-sh/ruff/pull/23130)) - Disallow TypeVars within ClassVars ([#23184](https://github.com/astral-sh/ruff/pull/23184)) - Emit diagnostic on unbound call to abstract `@classmethod` or `@staticmethod` ([#23182](https://github.com/astral-sh/ruff/pull/23182)) - Fix false-positive diagnostics when providing the `total=` keyword to `TypedDict` classes that had PEP-695 type parameters ([#23114](https://github.com/astral-sh/ruff/pull/23114)) - Narrow both left- and right-hand operands where possible ([#23084](https://github.com/astral-sh/ruff/pull/23084)) - Narrow chained operators ([#23093](https://github.com/astral-sh/ruff/pull/23093)) - Narrow equality subscripts on either operand ([#23104](https://github.com/astral-sh/ruff/pull/23104)) - Recognize `__dataclass_transform__` to support SQLModel ([#23070](https://github.com/astral-sh/ruff/pull/23070)) - Relax the attribute narrowing condition to support deeper-nested attribute type narrowing ([#22440](https://github.com/astral-sh/ruff/pull/22440)) - Support constrained TypeVar compatibility across function boundaries ([#23103](https://github.com/astral-sh/ruff/pull/23103)) - Support comparison methods (`__gt__`, etc.) where a parameter is annotated with a `Literal` type ([#23100](https://github.com/astral-sh/ruff/pull/23100)) - Support partially specialized type context ([#22748](https://github.com/astral-sh/ruff/pull/22748)) - Use type context when inferring constructor argument types ([#23139](https://github.com/astral-sh/ruff/pull/23139)) - Validate `TypedDict` constructor calls for generic aliases and `type[...]` targets ([#23113](https://github.com/astral-sh/ruff/pull/23113)) ### Performance - Conservative narrowing places optimization ([#22734](https://github.com/astral-sh/ruff/pull/22734)) ### Contributors - [@rbange](https://github.com/rbange) - [@rayzeller](https://github.com/rayzeller) - [@charliermarsh](https://github.com/charliermarsh) - [@11happy](https://github.com/11happy) - [@figsoda](https://github.com/figsoda) - [@mtshiba](https://github.com/mtshiba) - [@ibraheemdev](https://github.com/ibraheemdev) - [@ngnpope](https://github.com/ngnpope) - [@sakgoyal](https://github.com/sakgoyal) - [@oconnor663](https://github.com/oconnor663) - [@ericmarkmartin](https://github.com/ericmarkmartin) - [@Hugo-Polloli](https://github.com/Hugo-Polloli) - [@Glyphack](https://github.com/Glyphack) - [@sharkdp](https://github.com/sharkdp) - [@carljm](https://github.com/carljm) - [@BurntSushi](https://github.com/BurntSushi) ## 0.0.15 Released on 2026-02-04. ### Bug fixes - Add support for resolving imports of packages installed into Debian/Ubuntu `dist-packages` directories ([#22466](https://github.com/astral-sh/ruff/pull/22466)) - Avoid `not-iterable` false positives when iterating over an instance of an intersection type with only negated elements ([#22089](https://github.com/astral-sh/ruff/pull/22089)) - Fix support for stringized annotations in very large files ([#22913](https://github.com/astral-sh/ruff/pull/22913)) - Don't emit Liskov diagnostics for methods with mangled names ([#23062](https://github.com/astral-sh/ruff/pull/23062)) - Enforce that a `Final` symbol cannot be reassigned even after a conditional binding ([#22986](https://github.com/astral-sh/ruff/pull/22986)) - Fix TypedDict construction from existing TypedDict values ([#22904](https://github.com/astral-sh/ruff/pull/22904)) - Fix `Self` resolution for classes nested within methods ([#22964](https://github.com/astral-sh/ruff/pull/22964)) - Fix bidirectional inference with PEP 695 union type aliases ([#22988](https://github.com/astral-sh/ruff/pull/22988)) - Fix edge-case bugs when narrowing tagged unions in `match` statements ([#22870](https://github.com/astral-sh/ruff/pull/22870)) - Fix false-positive diagnostics when iterating over an instance of an intersection that includes a TypeVar of which the upper bound is a union where the union includes a non-iterable type ([#22117](https://github.com/astral-sh/ruff/pull/22117)) - Fix lookup of `__contains__` to respect descriptors ([#23056](https://github.com/astral-sh/ruff/pull/23056)) - Fix narrowing of `nonlocal` variables with conditional assignments ([#22966](https://github.com/astral-sh/ruff/pull/22966)) - Fix several bugs that could affect `NewType`s of `NewType`s of `float` ([#22997](https://github.com/astral-sh/ruff/pull/22997)) - Fix several type narrowing bugs involving PEP-695 type aliases ([#22894](https://github.com/astral-sh/ruff/pull/22894)) - Fix spurious query cycles in decorated functions with parameter defaults, for improved performance and improved determinism ([#23014](https://github.com/astral-sh/ruff/pull/23014)) - Fix unary and comparison operators for TypeVars with union bounds ([#22925](https://github.com/astral-sh/ruff/pull/22925)) - Understand functions as method descriptors even if they are decorated with a decorator annotated as returning a PEP-695 alias to a `Callable` type ([#22902](https://github.com/astral-sh/ruff/pull/22902)) - `dataclass_transform`: Fix visibility of field specifiers when models are nested inside methods ([#23069](https://github.com/astral-sh/ruff/pull/23069)) ### LSP server - Fix hover showing `Unknown` for bare `Final` instance attributes ([#23003](https://github.com/astral-sh/ruff/pull/23003)) - Improve support for goto-type, goto-declaration, hover, and highlighting of string annotations ([#22878](https://github.com/astral-sh/ruff/pull/22878)) - Include setters and deleters when renaming properties ([#22999](https://github.com/astral-sh/ruff/pull/22999)) - Show type qualifiers like `Final` in on-hover hints ([#23005](https://github.com/astral-sh/ruff/pull/23005)) ### Configuration - Add new `unused-type-ignore-comment` rule ([#22790](https://github.com/astral-sh/ruff/pull/22790)) - Add a mechanism to ignore/warn/select all rules ([#22832](https://github.com/astral-sh/ruff/pull/22832)) - Support multiple workspace folders in a single ty LSP server instance ([#22953](https://github.com/astral-sh/ruff/pull/22953)) - Only add `./src` as a search path if `./src/__init__.py(i)` does not exist ([#22851](https://github.com/astral-sh/ruff/pull/22851)) ### Type checking - Add a diagnostic detecting if a variable is declared as `Final` but never has any bindings ([#23001](https://github.com/astral-sh/ruff/pull/23001)) - Add a diagnostic detecting overridden comparison dunder methods on `order=True` dataclasses ([#22689](https://github.com/astral-sh/ruff/pull/22689)) - Add a hint to `invalid-argument-type` and `invalid-assignment` diagnostics if a variable is annotated with a type from the `numbers` module ([#22931](https://github.com/astral-sh/ruff/pull/22931), [#22938](https://github.com/astral-sh/ruff/pull/22938)) - Add diagnostic hint on `unresolved-reference` to suggest using "list" instead of "List" ([#22827](https://github.com/astral-sh/ruff/pull/22827)) - Add new diagnostic for invalid dataclass field orders ([#19825](https://github.com/astral-sh/ruff/pull/19825)) - Allow a subclass method with a positional-only parameter to override a superclass method without that parameter if the parameter in the subclass method has a default value ([#23037](https://github.com/astral-sh/ruff/pull/23037)) - Allow self-referential imports outside the global scope ([#22963](https://github.com/astral-sh/ruff/pull/22963)) - Ban `...` in odd places inside tuple specializations ([#22889](https://github.com/astral-sh/ruff/pull/22889)) - Ban `Required`, `NotRequired` and `ReadOnly` in parameter annotations ([#22888](https://github.com/astral-sh/ruff/pull/22888)) - Ban legacy `TypeVar` bounds or constraints from containing type variables ([#22949](https://github.com/astral-sh/ruff/pull/22949)) - Ban multiple unpacked variadic tuples in a `tuple` specialization ([#22884](https://github.com/astral-sh/ruff/pull/22884)) - Detect generic `Callable`s in the return type of function signatures ([#22954](https://github.com/astral-sh/ruff/pull/22954)) - Detect invalid `isinstance()` and `issubclass()` calls against `TypedDict` classes ([#22887](https://github.com/astral-sh/ruff/pull/22887)) - Detect invalid `issubclass()` calls against `Protocol` classes with non-method members ([#22896](https://github.com/astral-sh/ruff/pull/22896)) - Detect invalid attempts to subclass `Protocol[]` and `Generic[]` simultaneously ([#22948](https://github.com/astral-sh/ruff/pull/22948)) - Emit a diagnostic on incorrect applications of the legacy convention for specifying positional-only parameters ([#22943](https://github.com/astral-sh/ruff/pull/22943)) - Emit an error if a `TypeVarTuple` is used to subscript `Generic` or `Protocol` without being unpacked ([#22952](https://github.com/astral-sh/ruff/pull/22952)) - Fallback to metaclass `__getattr__` or `__getattribute__` when looking up attributes on class objects ([#22985](https://github.com/astral-sh/ruff/pull/22985)) - Fix a bug where an overridden type in a dataclass subclass would not be respected if the dataclass subclass field had a default value but the superclass field did not ([#22965](https://github.com/astral-sh/ruff/pull/22965)) - Improve bidirectional type inference involving PEP-695 type aliases ([#22989](https://github.com/astral-sh/ruff/pull/22989)) - Improve detection of invalid `NewType`s with generic bases ([#22961](https://github.com/astral-sh/ruff/pull/22961)) - Improve reachability analysis when evaluating the truthiness of expressions that involve variables that may not be bound in all code paths ([#22971](https://github.com/astral-sh/ruff/pull/22971)) - Improve the error message if `**` is used with a non-mapping in the context of a call to an overloaded function ([#22921](https://github.com/astral-sh/ruff/pull/22921)) - Infer `ParamSpec` from class constructors for callable protocols ([#22853](https://github.com/astral-sh/ruff/pull/22853)) - Move the location of some `invalid-overload` diagnostics ([#22933](https://github.com/astral-sh/ruff/pull/22933)) - Point to an overload with an invalid `@final` decorator when emitting `invalid-overload` errors for invalid `@final` decorators ([#22893](https://github.com/astral-sh/ruff/pull/22893)) - Avoid false positives when iterating over an instance of an intersection with only negated elements by preserving "pure negation" types in descriptor lookups ([#22907](https://github.com/astral-sh/ruff/pull/22907)) - Promote `Literal` types when inferring elements for very large unannotated tuples, for improved performance ([#22841](https://github.com/astral-sh/ruff/pull/22841)) - Recognize functions with stub bodies in `Protocol` classes as implicitly abstract ([#22838](https://github.com/astral-sh/ruff/pull/22838)) - Reduce false positives involving heterogeneous dicts by tracking dictionary literal keys as individual places ([#22882](https://github.com/astral-sh/ruff/pull/22882)) - Reduce false positives when subscripting classes generic over `TypeVarTuple`s ([#22950](https://github.com/astral-sh/ruff/pull/22950)) - Remove special handling for `Any()` in `match` class patterns ([#23011](https://github.com/astral-sh/ruff/pull/23011)) - Support `type[None]` in type expressions ([#22892](https://github.com/astral-sh/ruff/pull/22892)) - Support legacy namespace packages declared using `pkg_resources.declare_namespace` ([#22987](https://github.com/astral-sh/ruff/pull/22987)) - Sync vendored typeshed stubs ([#23006](https://github.com/astral-sh/ruff/pull/23006)), [Typeshed diff](https://github.com/python/typeshed/compare/cd8b26b0ceef26cd84ab614088140d48680ac7f7...fa659b1def704dea3dc8e25c7857b23eac69df4d) - Validate signatures of dataclass `__post_init__` methods ([#22730](https://github.com/astral-sh/ruff/pull/22730)) ### Contributors - [@charliermarsh](https://github.com/charliermarsh) - [@stefanvanburen](https://github.com/stefanvanburen) - [@ibraheemdev](https://github.com/ibraheemdev) - [@abhijeetbodas2001](https://github.com/abhijeetbodas2001) - [@MichaReiser](https://github.com/MichaReiser) - [@dcreager](https://github.com/dcreager) - [@PrettyWood](https://github.com/PrettyWood) - [@sharkdp](https://github.com/sharkdp) - [@oconnor663](https://github.com/oconnor663) - [@Feiyang472](https://github.com/Feiyang472) - [@denyszhak](https://github.com/denyszhak) - [@mtshiba](https://github.com/mtshiba) - [@AlexWaygood](https://github.com/AlexWaygood) - [@11happy](https://github.com/11happy) - [@BurntSushi](https://github.com/BurntSushi) - [@carljm](https://github.com/carljm) - [@Gankra](https://github.com/Gankra) - [@MentalMegalodon](https://github.com/MentalMegalodon) - [@thejchap](https://github.com/thejchap) ## 0.0.14 Released on 2026-01-26. ### Bug fixes - Consider keyword arguments when unpacking a variadic argument ([#22796](https://github.com/astral-sh/ruff/pull/22796)) - Fix binary operator false-positive for constrained TypeVars ([#22782](https://github.com/astral-sh/ruff/pull/22782)) - Fix docstring rendering for literal blocks after doctests ([#22676](https://github.com/astral-sh/ruff/pull/22676)) - Fix false-positive `unsupported-operator` for "symmetric" TypeVars ([#22756](https://github.com/astral-sh/ruff/pull/22756)) - Fix panic when overriding a final method using an assignment ([#22831](https://github.com/astral-sh/ruff/pull/22831)) - Fix unary operator false-positive for constrained TypeVars ([#22783](https://github.com/astral-sh/ruff/pull/22783)) - Fix generic functions with a generic (ParamSpec) decorator ([#22544](https://github.com/astral-sh/ruff/pull/22544)) - Fix `memo.changed_at` assertion panics ([#22498](https://github.com/astral-sh/ruff/pull/22498)) ### LSP server - Look up attributes on metaclasses for Go to Definition ([#22758](https://github.com/astral-sh/ruff/pull/22758)) - Suppress type inlay hints for leading-underscore assignments ([#22855](https://github.com/astral-sh/ruff/pull/22855)) ### Configuration - Add `allowed-unresolved-imports` setting ([#22800](https://github.com/astral-sh/ruff/pull/22800)) ### Other changes - Add `assert-type-unspellable-subtype` diagnostic, for failed `assert_type()` where the actual type is a subtype of the named type that can't be spelled in a type expression ([#22815](https://github.com/astral-sh/ruff/pull/22815)) - Add a new `empty-body` return code for functions with stub bodies that have non-`None` return annotations ([#22846](https://github.com/astral-sh/ruff/pull/22846)) - Add diagnostic disambiguation for different type aliases with the same name ([#22852](https://github.com/astral-sh/ruff/pull/22852)) - Add support for dict literals and dict() calls as default values for parameters with TypedDict types ([#22161](https://github.com/astral-sh/ruff/pull/22161)) - Add support for subscripts on intersections ([#22654](https://github.com/astral-sh/ruff/pull/22654)) - Avoid duplicate syntax errors for `await` outside functions ([#22826](https://github.com/astral-sh/ruff/pull/22826)) - Emit an error if the same type parameter appears more than once in a `Generic[]` subscript ([#22738](https://github.com/astral-sh/ruff/pull/22738)) - Emit diagnostic for unimplemented abstract method on @final class ([#22753](https://github.com/astral-sh/ruff/pull/22753)) - Fix GitLab Code Quality output format for empty diagnostics ([#22833](https://github.com/astral-sh/ruff/pull/22833)) - Fix assignment in decorated method causing `Unknown` fallback ([#22778](https://github.com/astral-sh/ruff/pull/22778)) - Fix false negative when using a non-runtime-checkable protocol in a `match` class pattern ([#22836](https://github.com/astral-sh/ruff/pull/22836)) - Improve completion rankings for raise-from/except contexts ([#22775](https://github.com/astral-sh/ruff/pull/22775)) - Improve invalid assignment diagnostics with type context ([#22643](https://github.com/astral-sh/ruff/pull/22643)) - Improve support for kwarg splats in dictionary literals ([#22781](https://github.com/astral-sh/ruff/pull/22781)) - Infer `TypedDict` types with >=1 required key as being always truthy ([#22808](https://github.com/astral-sh/ruff/pull/22808)) - Point to an overload with an invalid `@final` decoator when emitting `invalid-overload` errors for invalid `@final` decorators ([#22812](https://github.com/astral-sh/ruff/pull/22812)) - Require both `*args` and `**kwargs` when calling a `ParamSpec` callable ([#22820](https://github.com/astral-sh/ruff/pull/22820)) - Stricter validation of `TypedDict` definitions ([#22811](https://github.com/astral-sh/ruff/pull/22811)) - Support recursive and stringified annotations in functional `typing.NamedTuple`s ([#22718](https://github.com/astral-sh/ruff/pull/22718)) - Support solving generics involving PEP 695 type aliases ([#22678](https://github.com/astral-sh/ruff/pull/22678)) - Use a more lenient fallback type for failed `namedtuple()` and `NamedTuple` calls ([#22765](https://github.com/astral-sh/ruff/pull/22765)) - Use type context from augmented assignment dunder calls ([#22540](https://github.com/astral-sh/ruff/pull/22540)) - Check that starred arguments in function calls are iterable ([#22805](https://github.com/astral-sh/ruff/pull/22805)) ### Contributors - [@AlexWaygood](https://github.com/AlexWaygood) - [@maifeeulasad](https://github.com/maifeeulasad) - [@RasmusNygren](https://github.com/RasmusNygren) - [@ntBre](https://github.com/ntBre) - [@Imbuzi](https://github.com/Imbuzi) - [@dhruvmanila](https://github.com/dhruvmanila) - [@ibraheemdev](https://github.com/ibraheemdev) - [@carljm](https://github.com/carljm) - [@Hugo-Polloli](https://github.com/Hugo-Polloli) - [@charliermarsh](https://github.com/charliermarsh) - [@MichaReiser](https://github.com/MichaReiser) - [@bxff](https://github.com/bxff) - [@felixscherz](https://github.com/felixscherz) - [@denyszhak](https://github.com/denyszhak) ## 0.0.13 Released on 2026-01-21. ### Bug fixes - Fix `--force-exclude` when excluding entire directories ([#22595](https://github.com/astral-sh/ruff/pull/22595)) - Fix missing syntax highlighting for aliased import names ([#22675](https://github.com/astral-sh/ruff/pull/22675)) - Highlight interpolated-parts in t-strings ([#22674](https://github.com/astral-sh/ruff/pull/22674)) - Fix the inferred MRO of functional namedtuple classes ([#22722](https://github.com/astral-sh/ruff/pull/22722)) - Make special cases for subscript inference exhaustive, ensuring that the special casing for tuple subscripts is applied when a union of tuples or an alias to a tuple type is subscripted ([#22035](https://github.com/astral-sh/ruff/pull/22035)) ### LSP server - Improve completion suggestions inside class definitions ([#22571](https://github.com/astral-sh/ruff/pull/22571)) - Improve performance of completions ([#22630](https://github.com/astral-sh/ruff/pull/22630)) - Remove completion suggestions for redundant re-exports that share the same top-most module ([#22581](https://github.com/astral-sh/ruff/pull/22581)) ### Core type checking - Add basic support for overloads in `ParamSpec` ([#21946](https://github.com/astral-sh/ruff/pull/21946)) - Allow `...` as a default value for any parameter if the function is in an `if TYPE_CHECKING` block ([#22624](https://github.com/astral-sh/ruff/pull/22624)) - Allow `if type(x) is Y` narrowing for types other than class-literal types ([#22729](https://github.com/astral-sh/ruff/pull/22729)) - Avoid overload errors when detecting dataclass-on-tuple ([#22687](https://github.com/astral-sh/ruff/pull/22687)) - Avoid reporting overload errors for successful union variants ([#22688](https://github.com/astral-sh/ruff/pull/22688)) - Ban `NewType`s with generic bases ([#22653](https://github.com/astral-sh/ruff/pull/22653)) - Fix PEP 695 type aliases not expanding in overload resolution ([#22589](https://github.com/astral-sh/ruff/pull/22589)) - Fix the return type for synthesized `NamedTuple.__new__` methods ([#22625](https://github.com/astral-sh/ruff/pull/22625)) - Emit diagnostics for `NamedTuple`, `TypedDict`, `Enum` or `Protocol` classes decorated with `@dataclass` ([#22672](https://github.com/astral-sh/ruff/pull/22672)) - Emit `invalid-type-form` diagnostics for stringified annotations where the quoted expression is invalid ([#22752](https://github.com/astral-sh/ruff/pull/22752)) - Infer the implicit type of `cls` in `__new__` methods ([#22584](https://github.com/astral-sh/ruff/pull/22584)) - Make `ModuleType` and `object` attributes available on namespace packages ([#22606](https://github.com/astral-sh/ruff/pull/22606)) - Make `NamedTuple(...)` and `namedtuple(...)` calls stricter ([#22601](https://github.com/astral-sh/ruff/pull/22601)) - Narrow on bool and byte subscripts ([#22684](https://github.com/astral-sh/ruff/pull/22684)) - Narrow on negative subscript indexing ([#22682](https://github.com/astral-sh/ruff/pull/22682)) - Override `__file__` to `str` when applicable on imported modules ([#22333](https://github.com/astral-sh/ruff/pull/22333)) - Add bidirectional inference for comprehensions ([#22564](https://github.com/astral-sh/ruff/pull/22564)) - Recognize string-literal types as subtypes of `Sequence[Literal[chars]]` ([#22415](https://github.com/astral-sh/ruff/pull/22415)) - Add right-hand-side narrowing for `if Foo is type(x)` expressions ([#22608](https://github.com/astral-sh/ruff/pull/22608)) - Add simple syntactic validation for the right-hand side of PEP-613 type aliases ([#22652](https://github.com/astral-sh/ruff/pull/22652)) - Add support for passing `typename` and `field_names` by keyword argument to `collections.namedtuple()` calls ([#22660](https://github.com/astral-sh/ruff/pull/22660)) - Add support for starred unpacking in class bases ([#22591](https://github.com/astral-sh/ruff/pull/22591)) - Validate constructor arguments when a class is used as a decorator ([#22377](https://github.com/astral-sh/ruff/pull/22377)) - Validate field names for `typing.NamedTuple(...)` ([#22599](https://github.com/astral-sh/ruff/pull/22599)) - Add diagnostic on overridden `__setattr__` and `__delattr__` in frozen dataclasses ([#21430](https://github.com/astral-sh/ruff/pull/21430)) - Fix unary operators on `NewType`s of `float` or `complex` ([#22605](https://github.com/astral-sh/ruff/pull/22605)) ### Configuration - Support overriding `respect-type-ignore-comments` ([#22615](https://github.com/astral-sh/ruff/pull/22615)) ### Diagnostics - Don't add a subdiagnostic pointing to the TypeVar definition if the TypeVar is `Self` ([#22646](https://github.com/astral-sh/ruff/pull/22646)) - Show final search path instead of "and 1 more paths" ([#22776](https://github.com/astral-sh/ruff/pull/22776)) - Group `type[]` elements together when displaying union types ([#22592](https://github.com/astral-sh/ruff/pull/22592)) ### Performance - Cache `ClassType::nearest_disjoint_base` ([#22065](https://github.com/astral-sh/ruff/pull/22065)) ### Other changes - Sync vendored typeshed stubs ([#22590](https://github.com/astral-sh/ruff/pull/22590), [Typeshed diff](https://github.com/python/typeshed/compare/d1d5fe58664b30a0c2dde3cd5c3dc8091f0f16ae...cd8b26b0ceef26cd84ab614088140d48680ac7f7) ### Contributors - [@bxff](https://github.com/bxff) - [@jhartum](https://github.com/jhartum) - [@thejchap](https://github.com/thejchap) - [@AlexWaygood](https://github.com/AlexWaygood) - [@charliermarsh](https://github.com/charliermarsh) - [@RasmusNygren](https://github.com/RasmusNygren) - [@mswart](https://github.com/mswart) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@11happy](https://github.com/11happy) - [@ibraheemdev](https://github.com/ibraheemdev) - [@sinon](https://github.com/sinon) - [@MichaReiser](https://github.com/MichaReiser) - [@carljm](https://github.com/carljm) - [@BurntSushi](https://github.com/BurntSushi) - [@dhruvmanila](https://github.com/dhruvmanila) - [@oconnor663](https://github.com/oconnor663) - [@zanieb](https://github.com/zanieb) ## 0.0.12 Released on 2026-01-14. ### Bug fixes - Avoid panic that could occur when `cast`ing an object to a TypedDict or union of TypedDicts ([#22509](https://github.com/astral-sh/ruff/pull/22509)) - Fix incorrect narrowing for `if type(x) == y` ([#22531](https://github.com/astral-sh/ruff/pull/22531)) - Fix stack overflow with recursive type aliases containing tuple types ([#22543](https://github.com/astral-sh/ruff/pull/22543)) - `functools.total_ordering`: ensure the signatures of generated methods reflect the signature of the user-provided method ([#22496](https://github.com/astral-sh/ruff/pull/22496)) - Support `dataclass_transform` as a function call ([#22378](https://github.com/astral-sh/ruff/pull/22378)) - Use the top materialization of classes for `if type(x) is y` narrowing. For example, `if type(x) is tuple` will cause the type of `x` to be intersected with `tuple[object, ...]` rather than `tuple[Unknown, ...]`. ([#22553](https://github.com/astral-sh/ruff/pull/22553)) - Avoid emitting Liskov violations with respect to a grandparent class if such violations could not be fixed without introducing Liskov violations with respect to a parent class ([#22484](https://github.com/astral-sh/ruff/pull/22484)) - Fix interaction between classmethod, contextmanager, and Self ([#22407](https://github.com/astral-sh/ruff/pull/22407)) - Check contravariant type variable bounds contravariantly in specialization inference ([#22488](https://github.com/astral-sh/ruff/pull/22488)) - Fix false positive for bounded type parameters with NewType ([#22542](https://github.com/astral-sh/ruff/pull/22542)) ### Core type checking - Add support for dynamic `type()` classes ([#22291](https://github.com/astral-sh/ruff/pull/22291), [#22499](https://github.com/astral-sh/ruff/pull/22499), [#22537](https://github.com/astral-sh/ruff/pull/22537), [#22480](https://github.com/astral-sh/ruff/pull/22480)) - Add support for functional `namedtuple` creation ([#22327](https://github.com/astral-sh/ruff/pull/22327), [#22573](https://github.com/astral-sh/ruff/pull/22573), [#22575](https://github.com/astral-sh/ruff/pull/22575), [#22574](https://github.com/astral-sh/ruff/pull/22574)) - Add a diagnostic for non-decorator uses of `final` ([#22555](https://github.com/astral-sh/ruff/pull/22555)) - Add diagnostic to catch generic enums ([#22482](https://github.com/astral-sh/ruff/pull/22482)) - Add diagnostics for `__init_subclass__` argument mismatch ([#22185](https://github.com/astral-sh/ruff/pull/22185)) - Add diagnostics to validate `TypeIs` and `TypeGuard` definitions ([#22300](https://github.com/astral-sh/ruff/pull/22300)) - Apply type narrowing to walrus targets ([#22369](https://github.com/astral-sh/ruff/pull/22369)) - Detect invalid `@total_ordering` applications in non-decorator contexts ([#22486](https://github.com/astral-sh/ruff/pull/22486)) - Fix `@Todo` type for starred expressions ([#22503](https://github.com/astral-sh/ruff/pull/22503)) - Improve disambiguation of types in diagnostics ([#22547](https://github.com/astral-sh/ruff/pull/22547)) - Include type parameters in the display for generic `Callable` types ([#22435](https://github.com/astral-sh/ruff/pull/22435)) - Infer `type[Unknown]` for calls to `type()` when overload evaluation is ambiguous ([#22569](https://github.com/astral-sh/ruff/pull/22569)) - Support assignment to unions of `TypedDict`s ([#22294](https://github.com/astral-sh/ruff/pull/22294)) - Use the key and value parameter types as type context for `__setitem__` dunder calls ([#22148](https://github.com/astral-sh/ruff/pull/22148)) - Narrow the right-hand side of `==`, `!=`, `is` and `is not` conditions when the left-hand side is not narrowable ([#22511](https://github.com/astral-sh/ruff/pull/22511)) ### LSP server - Fix `__file__` type in completions to show `str` instead of `str | None` when the inferred type is `str` ([#22510](https://github.com/astral-sh/ruff/pull/22510)) - Improve rendering of ReST directives in docstrings ([#22512](https://github.com/astral-sh/ruff/pull/22512)) ### Contributors - [@eclbg](https://github.com/eclbg) - [@RasmusNygren](https://github.com/RasmusNygren) - [@carljm](https://github.com/carljm) - [@drbh](https://github.com/drbh) - [@AryanBagade](https://github.com/AryanBagade) - [@bxff](https://github.com/bxff) - [@ibraheemdev](https://github.com/ibraheemdev) - [@charliermarsh](https://github.com/charliermarsh) - [@AlexWaygood](https://github.com/AlexWaygood) ## 0.0.11 Released on 2026-01-09. ### Bug fixes - Fix `super()` with TypeVar-annotated `self` and `cls` parameter ([#22208](https://github.com/astral-sh/ruff/pull/22208)) - Only consider fully static pivots when deriving transitive constraints ([#22444](https://github.com/astral-sh/ruff/pull/22444)) ### LSP server - Don't show diagnostics for excluded files ([#22455](https://github.com/astral-sh/ruff/pull/22455)) - Fix goto definition for relative imports in third-party files ([#22457](https://github.com/astral-sh/ruff/pull/22457)) - Improve completion ranking based on origin and exact match ([#22460](https://github.com/astral-sh/ruff/pull/22460)) - Rank top-level module symbols above most other symbols ([#22465](https://github.com/astral-sh/ruff/pull/22465)) ### Configuration - Enable `unused-ignore-comment` by default ([#22474](https://github.com/astral-sh/ruff/pull/22474)) ### Performance - Improve `UnionBuilder` performance by changing `Type::is_subtype_of` calls to `Type::is_redundant_with` ([#22337](https://github.com/astral-sh/ruff/pull/22337)) - Optimize union building for unions with many enum-literal members ([#22363](https://github.com/astral-sh/ruff/pull/22363))- ### Other changes - Declare support for Python 3.14 ([#2407](https://github.com/astral-sh/ty/pull/2407)) - Remove dark-mode handling from PyPI-uploaded README ([#2422](https://github.com/astral-sh/ty/pull/2422)) ### Contributors - [@dhruvmanila](https://github.com/dhruvmanila) - [@AlexWaygood](https://github.com/AlexWaygood) - [@charliermarsh](https://github.com/charliermarsh) - [@BurntSushi](https://github.com/BurntSushi) - [@MichaReiser](https://github.com/MichaReiser) - [@dcreager](https://github.com/dcreager) ## 0.0.10 Released on 2026-01-07. ### Bug fixes - Fix stack overflow due to too small stack size ([#22433](https://github.com/astral-sh/ruff/pull/22433)) - Fix stale semantic tokens after opening the same document with new content ([#22414](https://github.com/astral-sh/ruff/pull/22414)) - Fix handling of `ParamSpec` in overloaded functions ([#22416](https://github.com/astral-sh/ruff/pull/22416)) - Fix comparisons and arithmetic with `NewType`s of `float` ([#22105](https://github.com/astral-sh/ruff/pull/22105)) - Fix several issues with unannotated function return types ([#22425](https://github.com/astral-sh/ruff/pull/22425)) - Fix handling of subclasses in Callables ([#22411](https://github.com/astral-sh/ruff/pull/22411)) ### LSP server - Add support for explicit markdown code fences in docstring rendering ([#22373](https://github.com/astral-sh/ruff/pull/22373), [#22408](https://github.com/astral-sh/ruff/pull/22408)) - Offer completions for `T` when a value has type `Unknown | T` ([#22436](https://github.com/astral-sh/ruff/pull/22436)) - Sort keyword argument completions higher ([#22297](https://github.com/astral-sh/ruff/pull/22297)) ### Core type checking - Add support for `@total_ordering` ([#22181](https://github.com/astral-sh/ruff/pull/22181), [#22183](https://github.com/astral-sh/ruff/pull/22183)) - Better simplification of intersections of tuples ([#22094](https://github.com/astral-sh/ruff/pull/22094)) - Support comparisons between variable-length tuples ([#21824](https://github.com/astral-sh/ruff/pull/21824)) - Emit diagnostics for method definitions and other invalid statements in `TypedDict` class bodies ([#22351](https://github.com/astral-sh/ruff/pull/22351)) - Improve type-narrowing in calls to `len()` ([#22330](https://github.com/astral-sh/ruff/pull/22330)) ### CLI - Add `--add-ignore` CLI option ([#21696](https://github.com/astral-sh/ruff/pull/21696)) ### Configuration - `include = ["myscript"]` will now check `myscript` even though it doesn't have a Python extension ([#22243](https://github.com/astral-sh/ruff/pull/22243)) ### Performance - Optimize intersections with a single negated element ([#22344](https://github.com/astral-sh/ruff/pull/22344)) - Optimize negated types ([#22402](https://github.com/astral-sh/ruff/pull/22402)) ### Documentation - Link to `Callable` `__name__` FAQ directly from `unresolved-attribute` diagnostic ([#22437](https://github.com/astral-sh/ruff/pull/22437)) ### Contributors - [@dhruvmanila](https://github.com/dhruvmanila) - [@charliermarsh](https://github.com/charliermarsh) - [@oconnor663](https://github.com/oconnor663) - [@BurntSushi](https://github.com/BurntSushi) - [@RasmusNygren](https://github.com/RasmusNygren) - [@carljm](https://github.com/carljm) - [@Gankra](https://github.com/Gankra) - [@MichaReiser](https://github.com/MichaReiser) - [@AlexWaygood](https://github.com/AlexWaygood) ## 0.0.9 Released on 2026-01-05. ### Bug fixes - Emit a diagnostic if a class decorator is not a callable accepting a type ([#22375](https://github.com/astral-sh/ruff/pull/22375)) - Fix exhaustiveness inference for unions that include enums ([#22290](https://github.com/astral-sh/ruff/pull/22290)) ### Core type checking - Support `typing.TypeGuard` ([#20974](https://github.com/astral-sh/ruff/pull/20974)) - Treat `__setattr__` as fallback-only ([#22014](https://github.com/astral-sh/ruff/pull/22014)) - Don't expand type aliases via type mappings unless necessary. This means that the displayed signature of a bound methods will no longer eagerly expand type aliases into their aliased types ([#22241](https://github.com/astral-sh/ruff/pull/22241)) - Narrow `TypedDict` unions with `not in` ([#22349](https://github.com/astral-sh/ruff/pull/22349)) - Don't including `property` in subclasses properties ([#22088](https://github.com/astral-sh/ruff/pull/22088)) - Narrow tagged unions of `TypedDict`s in `match` statements ([#22299](https://github.com/astral-sh/ruff/pull/22299)) - Teach bidirectional inference about subtyping. This allows `x` to be inferred as `list[int]` for `x: Iterable[int] = [42]` ([#21930](https://github.com/astral-sh/ruff/pull/21930)) - Support narrowing for tagged unions of tuples where one element of the tuple is a `Literal` type ([#22303](https://github.com/astral-sh/ruff/pull/22303)) ### LSP server - Add autocomplete suggestions for keyword arguments in `class` statements ([#22110](https://github.com/astral-sh/ruff/pull/22110)) - Avoid showing misleading inlay hint for unpacked tuple arguments ([#22286](https://github.com/astral-sh/ruff/pull/22286)) ### Other changes - Sync vendored typeshed stubs ([#22302](https://github.com/astral-sh/ruff/pull/22302), [#22321](https://github.com/astral-sh/ruff/pull/22321), [#22324](https://github.com/astral-sh/ruff/pull/22324)). [Typeshed diff](https://github.com/python/typeshed/compare/3c2dbb1fde8e8d1d59b10161c8bf5fd06c0011cd...d1d5fe58664b30a0c2dde3cd5c3dc8091f0f16ae) ### Contributors - [@RasmusNygren](https://github.com/RasmusNygren) - [@ericmarkmartin](https://github.com/ericmarkmartin) - [@AlexWaygood](https://github.com/AlexWaygood) - [@ibraheemdev](https://github.com/ibraheemdev) - [@charliermarsh](https://github.com/charliermarsh) - [@felixscherz](https://github.com/felixscherz) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@mtshiba](https://github.com/mtshiba) ## 0.0.8 Released on 2025-12-29. ### Breaking changes - Rename `non-subscriptable` rule to `not-subscriptable` ([#22193](https://github.com/astral-sh/ruff/pull/22193)) ### Core type checking - Promote float and complex when promoting literals ([#22215](https://github.com/astral-sh/ruff/pull/22215)) - Callable type of a type object is not function-like ([#22226](https://github.com/astral-sh/ruff/pull/22226)) - Fix and simplify callable type materializations ([#22213](https://github.com/astral-sh/ruff/pull/22213)) ### LSP server - Add option to disable syntax errors ([#22217](https://github.com/astral-sh/ruff/pull/22217)) - Fix completion in decorators with missing declaration ([#22177](https://github.com/astral-sh/ruff/pull/22177)) - Better completions context detection when typing in decorator positions ([#22224](https://github.com/astral-sh/ruff/pull/22224)) - Limit the returned completions to reduce lag ([#22240](https://github.com/astral-sh/ruff/pull/22240)) ### Diagnostics - Improve wording of `unsupported-base` sub-diagnostic ([#22194](https://github.com/astral-sh/ruff/pull/22194)) - Preserve the invalid assignment diagnostic message when implicitly shadowing a definition ([#22219](https://github.com/astral-sh/ruff/pull/22219)) ### Other changes - Update docker image to use alpine 3.23 and trixie ([#2217](https://github.com/astral-sh/ty/pull/2217)) ### Contributors - [@RasmusNygren](https://github.com/RasmusNygren) - [@samypr100](https://github.com/samypr100) - [@silamon](https://github.com/silamon) - [@carljm](https://github.com/carljm) - [@MichaReiser](https://github.com/MichaReiser) - [@MatthewMckee4](https://github.com/MatthewMckee4) ## 0.0.7 Released on 2025-12-24. ### Bug fixes - Fix classification of modules in `import x as y` for semantic syntax highlighting ([#22175](https://github.com/astral-sh/ruff/pull/22175)) - Fix module resolution on network drives ([#22173](https://github.com/astral-sh/ruff/pull/22173)) - Render the entire diagnostic message in all output formats ([#22164](https://github.com/astral-sh/ruff/pull/22164)) ### Other changes - Add a dedicated diagnostic for TypedDict deletions ([#22123](https://github.com/astral-sh/ruff/pull/22123)) - Check `__delitem__` instead of `__getitem__` for `del x[k]` ([#22121](https://github.com/astral-sh/ruff/pull/22121)) - Fix `@staticmethod` combined with other decorators incorrectly binding `self` ([#22128](https://github.com/astral-sh/ruff/pull/22128)) - Fix implementation of `Top[Callable[..., object]]` ([#22145](https://github.com/astral-sh/ruff/pull/22145)) - Improve diagnostic when `callable` is used in a type expression instead of `collections.abc.Callable` or `typing.Callable` ([#22180](https://github.com/astral-sh/ruff/pull/22180)) - Improve diagnostic when a user tries to access a function attribute on a `Callable` type ([#22182](https://github.com/astral-sh/ruff/pull/22182)) - Include the specialization of a generic `TypedDict` as part of its display ([#22174](https://github.com/astral-sh/ruff/pull/22174)) - Support tuple narrowing based on member checks ([#22167](https://github.com/astral-sh/ruff/pull/22167)) - Synthesize `__delitem__` for TypedDict to allow deleting non-required keys ([#22122](https://github.com/astral-sh/ruff/pull/22122)) ### Contributors - [@MichaReiser](https://github.com/MichaReiser) - [@ntBre](https://github.com/ntBre) - [@AlexWaygood](https://github.com/AlexWaygood) - [@charliermarsh](https://github.com/charliermarsh) ## 0.0.6 Released on 2025-12-23. ### Bug fixes - FIx panic from unexpanded type aliases in implicit tuple aliases ([#22015](https://github.com/astral-sh/ruff/pull/22015)) - Support `type[T]` where `T` is a type alias to a union of types ([#22115](https://github.com/astral-sh/ruff/pull/22115)) - Support `==` narrowing for tuples in unions with disjoint types ([#22129](https://github.com/astral-sh/ruff/pull/22129)) - Respect debug text interpolation in f-strings ([#22151](https://github.com/astral-sh/ruff/pull/22151)) - Fix panic from unstable union-type ordering in fixed-point iteration ([#22070](https://github.com/astral-sh/ruff/pull/22070)) ### LSP server - Add `ty.configuration` and `ty.configurationFile` options ([#22053](https://github.com/astral-sh/ruff/pull/22053)) - Add `diagnosticMode: off` to disable diagnostics while retaining Go To Definition, etc. ([#22073](https://github.com/astral-sh/ruff/pull/22073)) - Set flag to avoid `type[T@f]` being inserted when you double-click on the inlay ([#22139](https://github.com/astral-sh/ruff/pull/22139)) - Use Markdown for completions documentation if the LSP client supports it ([#21752](https://github.com/astral-sh/ruff/pull/21752)) ### CLI - Abort printing diagnostics when pressing `Ctrl+C` ([#22083](https://github.com/astral-sh/ruff/pull/22083)) ### Configuration - Add `respect-type-ignore-comments` configuration option ([#22137](https://github.com/astral-sh/ruff/pull/22137)) - Support custom builtins via `__builtins__.pyi` ([#22021](https://github.com/astral-sh/ruff/pull/22021)) ### Other changes - Bind self with instance in `__get__` ([#22155](https://github.com/astral-sh/ruff/pull/22155)) - Support type inference between protocol instances ([#22120](https://github.com/astral-sh/ruff/pull/22120)) - Synthesize a precise `_fields` attribute for NamedTuples ([#22163](https://github.com/astral-sh/ruff/pull/22163)) - Synthesize a precise `_replace` method for NamedTuples ([#22153](https://github.com/astral-sh/ruff/pull/22153)) - Narrow "tagged unions" of `TypedDict`s ([#22104](https://github.com/astral-sh/ruff/pull/22104)) ### Contributors - [@mtshiba](https://github.com/mtshiba) - [@charliermarsh](https://github.com/charliermarsh) - [@Wizzerinus](https://github.com/Wizzerinus) - [@oconnor663](https://github.com/oconnor663) - [@MichaReiser](https://github.com/MichaReiser) - [@ibraheemdev](https://github.com/ibraheemdev) - [@MatthewMckee4](https://github.com/MatthewMckee4) ## 0.0.5 Released on 2025-12-20. ### Bug fixes - Fix debug-mode server panic when a user typed a class definition by ensuring class arguments are visited in source order for semantic tokens ([#22063](https://github.com/astral-sh/ruff/pull/22063)) ### LSP server - Classify docstrings in semantic tokens during syntax highlighting ([#22031](https://github.com/astral-sh/ruff/pull/22031)) ### CLI - Add `--force-exclude` option ([#22076](https://github.com/astral-sh/ruff/pull/22076)) - Only clear output between two successful checks ([#22078](https://github.com/astral-sh/ruff/pull/22078)) ### Other changes - Add support for `dict(...)` calls in `TypedDict` contexts ([#22113](https://github.com/astral-sh/ruff/pull/22113)) - Speedup bidirectional type-checking involving large unions by avoiding narrowing on non-generic calls ([#22102](https://github.com/astral-sh/ruff/pull/22102)) - Simplify inferred types by avoiding storing multi-inference attempts ([#22062](https://github.com/astral-sh/ruff/pull/22062), [#22103](https://github.com/astral-sh/ruff/pull/22103)) - Improve union builder performance ([#22048](https://github.com/astral-sh/ruff/pull/22048)) - Only prefer declared types in non-covariant positions ([#22068](https://github.com/astral-sh/ruff/pull/22068)) - Respect intersections in iterations ([#21965](https://github.com/astral-sh/ruff/pull/21965)) - Sync vendored typeshed stubs ([#22091](https://github.com/astral-sh/ruff/pull/22091)). [Typeshed diff](https://github.com/python/typeshed/compare/ef2b90c67e5c668b91b3ae121baf00ee5165c30b...3c2dbb1fde8e8d1d59b10161c8bf5fd06c0011cd) - Understand that the type of `X` on an enum class will be `int` if `X` is defined using `enum.nonmember` in the class definition ([#22025](https://github.com/astral-sh/ruff/pull/22025)) ### Contributors - [@charliermarsh](https://github.com/charliermarsh) - [@ibraheemdev](https://github.com/ibraheemdev) - [@RasmusNygren](https://github.com/RasmusNygren) - [@Hugo-Polloli](https://github.com/Hugo-Polloli) - [@carljm](https://github.com/carljm) - [@Gankra](https://github.com/Gankra) - [@MichaReiser](https://github.com/MichaReiser) ## 0.0.4 Released on 2025-12-18. ### LSP server - Add support for attribute docstrings ([#22036](https://github.com/astral-sh/ruff/pull/22036)) - Correctly encode multiline tokens for clients not supporting multiline tokens ([#22033](https://github.com/astral-sh/ruff/pull/22033)) - Autocompletions: Don't suggest keyword statements when only expressions are valid ([#22002](https://github.com/astral-sh/ruff/pull/22002)) - Fix goto-declaration on the right-hand side of `from module import submodule` ([#22042](https://github.com/astral-sh/ruff/pull/22042)) - Fix some configuration panics in the LSP ([#22040](https://github.com/astral-sh/ruff/pull/22040)) - Gracefully handle client requests that can't be deserialized ([#22051](https://github.com/astral-sh/ruff/pull/22051)) ### Other changes - Improve performance for large match statements ([#22045](https://github.com/astral-sh/ruff/pull/22045)) - Disable possibly-missing-imports by default ([#22041](https://github.com/astral-sh/ruff/pull/22041)) - Implement disjointness for TypedDicts, significantly speeding up checking of code that uses pydantic ([#22044](https://github.com/astral-sh/ruff/pull/22044)) ### Contributors - [@oconnor663](https://github.com/oconnor663) - [@MichaReiser](https://github.com/MichaReiser) - [@Gankra](https://github.com/Gankra) - [@RasmusNygren](https://github.com/RasmusNygren) - [@charliermarsh](https://github.com/charliermarsh) ## 0.0.3 Released on 2025-12-17. ### LSP server - Improve rendering of signatures in hovers ([#22007](https://github.com/astral-sh/ruff/pull/22007)) ### Core type checking - Apply narrowing to `len` calls based on argument size ([#22026](https://github.com/astral-sh/ruff/pull/22026)) - Don't add identical lower/upper bounds multiple times when inferring specializations ([#22030](https://github.com/astral-sh/ruff/pull/22030)) - Improve `unsupported-base` and `invalid-super-argument` diagnostics to avoid extremely long lines when encountering verbose types ([#22022](https://github.com/astral-sh/ruff/pull/22022)) - Improve disambiguation of types in many cases ([#22019](https://github.com/astral-sh/ruff/pull/22019)) - Respect deferred values in keyword arguments etc. for `.pyi` files ([#22029](https://github.com/astral-sh/ruff/pull/22029)) - Handle field specifier functions that accept `**kwargs` and recognize metaclass-based transformers as instances of `DataclassInstance` ([#22018](https://github.com/astral-sh/ruff/pull/22018)) ### Contributors - [@charliermarsh](https://github.com/charliermarsh) - [@sharkdp](https://github.com/sharkdp) - [@Gankra](https://github.com/Gankra) - [@zanieb](https://github.com/zanieb) - [@AlexWaygood](https://github.com/AlexWaygood) - [@dcreager](https://github.com/dcreager) ## 0.0.2 Released on 2025-12-16. This is the first Beta release of ty, which we're now ready to recommend to motivated users for production use. See our [blog post](https://astral.sh/blog/ty) for more details. ### LSP server - Improve display of completions to show actual insertion text ([#21988](https://github.com/astral-sh/ruff/pull/21988)) - Improve highlighting of special type syntax in hovers ([#22005](https://github.com/astral-sh/ruff/pull/22005)) - Improve syntax highlighting of constants ([#22006](https://github.com/astral-sh/ruff/pull/22006)) ### Core type checking - Infer precise types for `isinstance(…)` calls involving type variables ([#21999](https://github.com/astral-sh/ruff/pull/21999)) - Infer `TypeVar` specializations for `Callable` types ([#21551](https://github.com/astral-sh/ruff/pull/21551)) - Propagate `classmethod`-ness through decorators returning `Callable`s ([#21958](https://github.com/astral-sh/ruff/pull/21958)) - Improve rendering of default values for function args ([#22010](https://github.com/astral-sh/ruff/pull/22010)) - Don't use implicit superclass annotation when converting a class constructor into a `Callable` ([#22011](https://github.com/astral-sh/ruff/pull/22011)) ### Other - Type checking performance improvement ([#22000](https://github.com/astral-sh/ruff/pull/22000)) ### Contributors - [@sharkdp](https://github.com/sharkdp) - [@carljm](https://github.com/carljm) - [@Gankra](https://github.com/Gankra) - [@BurntSushi](https://github.com/BurntSushi) - [@dcreager](https://github.com/dcreager) - [@MichaReiser](https://github.com/MichaReiser) ## 0.0.1-alpha.35 Released on 2025-12-16. ### Bug fixes - Fix panic for stringified comprehensions and boolean expressions in type expression ([#21967](https://github.com/astral-sh/ruff/pull/21967)) - Avoid stack overflow when determining inferable typevars ([#21971](https://github.com/astral-sh/ruff/pull/21971)) - Fix false-positive `invalid-method-override` diagnostic on method that uses `Callable` with a `ParamSpec` ([#21934](https://github.com/astral-sh/ruff/pull/21934)) - Disallow explicit specialization of type variables themselves ([#21938](https://github.com/astral-sh/ruff/pull/21938)) - Fix hover type on named expression ("walrus expression") targets ([#21952](https://github.com/astral-sh/ruff/pull/21952)) ### LSP server - Add *"qualify ..."* code fix for undefined references ([#21968](https://github.com/astral-sh/ruff/pull/21968)) - Add new goto-definition targets on inlay hints ([#21950](https://github.com/astral-sh/ruff/pull/21950)) - Remove invalid statement-keyword completions in `for`-statements ([#21979](https://github.com/astral-sh/ruff/pull/21979)) ### Core type checking - Add support for `__qualname__` and other implicit class attributes ([#21966](https://github.com/astral-sh/ruff/pull/21966)) - Emit a diagnostic when a frozen dataclass inherits a non-frozen dataclass and vice versa ([#21962](https://github.com/astral-sh/ruff/pull/21962)) - Emit a diagnostic when a type variable with a default is followed by one without a default ([#21787](https://github.com/astral-sh/ruff/pull/21787)) - Improve diagnostics for unsupported binary operations and unsupported augmented assignments ([#21947](https://github.com/astral-sh/ruff/pull/21947)) - Improve check enforcing that an overloaded function must have an implementation ([#21978](https://github.com/astral-sh/ruff/pull/21978)) - Use unqualified names for displays of `TypeAliasType`s and unbound `ParamSpec`s/`TypeVar`s ([#21960](https://github.com/astral-sh/ruff/pull/21960)) ### Performance - Speed up ty on Linux by using jemalloc ([#21975](https://github.com/astral-sh/ruff/pull/21975)) ### Contributors - [@11happy](https://github.com/11happy) - [@dhruvmanila](https://github.com/dhruvmanila) - [@AlexWaygood](https://github.com/AlexWaygood) - [@mtshiba](https://github.com/mtshiba) - [@MichaReiser](https://github.com/MichaReiser) - [@Gankra](https://github.com/Gankra) - [@silamon](https://github.com/silamon) - [@dcreager](https://github.com/dcreager) - [@charliermarsh](https://github.com/charliermarsh) - [@RasmusNygren](https://github.com/RasmusNygren) - [@carljm](https://github.com/carljm) ## 0.0.1-alpha.34 Released on 2025-12-12. ### Bug fixes - Improve solving of a type variable with an upper bound when that type variable appears as one element in a union type ([#21893](https://github.com/astral-sh/ruff/pull/21893)) - Accurately emulate runtime semantics for `kw_only=True` dataclasses such that only fields declared in the immediate class body are understood as being keyword-only ([#21820](https://github.com/astral-sh/ruff/pull/21820)) - Avoid inferring types for invalid binary expressions in string annotations ([#21911](https://github.com/astral-sh/ruff/pull/21911)) - Fix logic used to determine whether two `@final` instance types are disjoint ([#21769](https://github.com/astral-sh/ruff/pull/21769)) - Fix logic used to determine whether two `@final` `type[]` types are disjoint ([#21770](https://github.com/astral-sh/ruff/pull/21770)) - Fix false-positive diagnostics that could arise when analysing cyclic types ([#21910](https://github.com/astral-sh/ruff/pull/21910)), ([#21909](https://github.com/astral-sh/ruff/pull/21909)) ### LSP server - Fix outdated version in publish diagnostics after `didChange` ([#21943](https://github.com/astral-sh/ruff/pull/21943)) - Fix workspace symbols to return members too ([#21926](https://github.com/astral-sh/ruff/pull/21926)) - Adjust scope completions to use all reachable symbols ([#21872](https://github.com/astral-sh/ruff/pull/21872)) - Classify `cls` as class parameter for semantic highlighting ([#21944](https://github.com/astral-sh/ruff/pull/21944)) - Don't show on-hover tooltips for expressions with no inferred type ([#21924](https://github.com/astral-sh/ruff/pull/21924)) - Ignore `__all__` for document and workspace symbol requests ([#21928](https://github.com/astral-sh/ruff/pull/21928)) - Recognize `__all__ += submodule.__all__` in auto-import ([#21918](https://github.com/astral-sh/ruff/pull/21918)) - Stabilize rename ([#21940](https://github.com/astral-sh/ruff/pull/21940)) ### Other changes - Support checking files without extensions ([#21867](https://github.com/astral-sh/ruff/pull/21867)) - Improve performance and semantics by deferring inference of all parameter and return-type annotations ([#21906](https://github.com/astral-sh/ruff/pull/21906)) - Improve resolution of absolute imports in tests ([#21817](https://github.com/astral-sh/ruff/pull/21817)) - Infer the implicit type of the `cls` parameter in `@classmethod` method bodies ([#21685](https://github.com/astral-sh/ruff/pull/21685)) - Support the implicit type of the `cls` parameter in signatures of `@classmethod` methods ([#21771](https://github.com/astral-sh/ruff/pull/21771)) - Uniformly use "not supported" in diagnostics ([#21916](https://github.com/astral-sh/ruff/pull/21916)) - Implement the [equivalence relation](https://typing.python.org/en/latest/spec/glossary.html#term-equivalent) for `TypedDict`s ([#21784](https://github.com/astral-sh/ruff/pull/21784)) - Ensure that the type of the class object `C` is always considered assignable to `type[C[Unknown]]` if `C` is a generic class ([#21883](https://github.com/astral-sh/ruff/pull/21883)) - Improve bad specialization results and error messages ([#21840](https://github.com/astral-sh/ruff/pull/21840)) - Support `NewType`s of `float` and `complex` ([#21886](https://github.com/astral-sh/ruff/pull/21886)) ### Contributors - [@charliermarsh](https://github.com/charliermarsh) - [@oconnor663](https://github.com/oconnor663) - [@MichaReiser](https://github.com/MichaReiser) - [@BurntSushi](https://github.com/BurntSushi) - [@lucach](https://github.com/lucach) - [@ibraheemdev](https://github.com/ibraheemdev) - [@mtshiba](https://github.com/mtshiba) - [@dcreager](https://github.com/dcreager) - [@sharkdp](https://github.com/sharkdp) - [@carljm](https://github.com/carljm) - [@Gankra](https://github.com/Gankra) ## 0.0.1-alpha.33 Released on 2025-12-09. ### Bug fixes - Fix assignability problem between `tuple[Any, ...]` and `tuple[int, *tuple[int, ...]]` ([#21803](https://github.com/astral-sh/ruff/pull/21803)) - Avoid diagnostic when `typing_extensions.ParamSpec` uses a `default` parameter ([#21839](https://github.com/astral-sh/ruff/pull/21839)) - Avoid crash for invalid `Annotated` subscript ([#21837](https://github.com/astral-sh/ruff/pull/21837)) - Avoid crash for invalid `Final` subscript ([#21828](https://github.com/astral-sh/ruff/pull/21828)) - Fix overload filtering to prefer more precise match when `*args: Any` is involved ([#21859](https://github.com/astral-sh/ruff/pull/21859)) - Handle various invalid explicit specializations for `ParamSpec` ([#21821](https://github.com/astral-sh/ruff/pull/21821)) - Fix stack overflow with recursive generic protocols (depth limit) ([#21858](https://github.com/astral-sh/ruff/pull/21858)) ### LSP server - Add autocomplete suggestions for parameters in function calls ([#21796](https://github.com/astral-sh/ruff/pull/21796)) - Don't create a related diagnostic for the primary annotation of sub-diagnostics ([#21845](https://github.com/astral-sh/ruff/pull/21845)) - Stabilize auto-import ([#21851](https://github.com/astral-sh/ruff/pull/21851)) - Suppress inlay hints when assigning a trivial initializer call ([#21848](https://github.com/astral-sh/ruff/pull/21848)) - Use concise message for LSP clients not supporting related diagnostic information ([#21850](https://github.com/astral-sh/ruff/pull/21850)) - Fix add-import action for `reveal_type` ([#21668](https://github.com/astral-sh/ruff/pull/21668)) ### Core type checking - Infer type variables within generic unions ([#21862](https://github.com/astral-sh/ruff/pull/21862)) - Type inference for `@asynccontextmanager` ([#21876](https://github.com/astral-sh/ruff/pull/21876)) - Make Python-version subdiagnostics less verbose ([#21849](https://github.com/astral-sh/ruff/pull/21849)) ### Contributors - [@BurntSushi](https://github.com/BurntSushi) - [@dhruvmanila](https://github.com/dhruvmanila) - [@AlexWaygood](https://github.com/AlexWaygood) - [@carljm](https://github.com/carljm) - [@Gankra](https://github.com/Gankra) - [@charliermarsh](https://github.com/charliermarsh) - [@RasmusNygren](https://github.com/RasmusNygren) - [@sharkdp](https://github.com/sharkdp) - [@MichaReiser](https://github.com/MichaReiser) ## 0.0.1-alpha.32 Released on 2025-12-05. ### LSP server - Provide auto-import completion suggestions for modules in more situations ([#21799](https://github.com/astral-sh/ruff/pull/21799)) - Always register the ty server as a [rename provider](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_rename) if the LSP client doesn't support dynamic registration ([#21789](https://github.com/astral-sh/ruff/pull/21789)) - Support auto-import of re-exported symbols in completion suggestions ([#21779](https://github.com/astral-sh/ruff/pull/21779)) - Support renaming import aliases ([#21792](https://github.com/astral-sh/ruff/pull/21792)) ### Core type checking - Support `ParamSpec` ([#21445](https://github.com/astral-sh/ruff/pull/21445)) - Improve the accuracy of the inferred `Callable` supertype of generic classes ([#21798](https://github.com/astral-sh/ruff/pull/21798)) - Increase the limit on the number of elements in a non-recursively defined literal union ([#21683](https://github.com/astral-sh/ruff/pull/21683)) - Fix panics on mutually recursive generic protocols by normalizing the bounds/constraints of cyclic type variables ([#21800](https://github.com/astral-sh/ruff/pull/21800)) ### Other changes - Minor improvements to `assert_type` diagnostics ([#21811](https://github.com/astral-sh/ruff/pull/21811)) - Fix a panic in recursive + generic type aliases ([#21718](https://github.com/astral-sh/ruff/pull/21718)) - Fix a panic when instantiating a type variable with invalid constraints ([#21663](https://github.com/astral-sh/ruff/pull/21663)) ### Contributors - [@BurntSushi](https://github.com/BurntSushi) - [@dhruvmanila](https://github.com/dhruvmanila) - [@MichaReiser](https://github.com/MichaReiser) - [@mtshiba](https://github.com/mtshiba) - [@dcreager](https://github.com/dcreager) - [@carljm](https://github.com/carljm) - [@AlexWaygood](https://github.com/AlexWaygood) ## 0.0.1-alpha.31 Released on 2025-12-04. ### Bug fixes - Fix incorrect `possibly-missing-attribute` diagnostics for `asyncio` imports on Python 3.14 ([#21776](https://github.com/astral-sh/ruff/pull/21776)) - Fix panic for recursive type aliases ([#21778](https://github.com/astral-sh/ruff/pull/21778)) ### Core type checking - Try ancestor `pyproject.toml` directories as search-paths if module resolution fails ([#21745](https://github.com/astral-sh/ruff/pull/21745)) - Sync vendored typeshed stubs ([#21715](https://github.com/astral-sh/ruff/pull/21715)) [Typeshed diff](https://github.com/python/typeshed/compare/f8cdc0bd526301e873cd952eb0d457bdf2554e57...ef2b90c67e5c668b91b3ae121baf00ee5165c30b) ### LSP server - Don't send publish diagnostics for clients supporting pull diagnostics ([#21772](https://github.com/astral-sh/ruff/pull/21772)) - Fix crash when hovering over string annotations with unknown symbols ([#21782](https://github.com/astral-sh/ruff/pull/21782)) ### Diagnostics - Add subdiagnostic hint if the user wrote `X = Any` rather than `X: Any` ([#21777](https://github.com/astral-sh/ruff/pull/21777)) - Improve the display of various special-form types ([#21775](https://github.com/astral-sh/ruff/pull/21775)) ### Contributors - [@sharkdp](https://github.com/sharkdp) - [@AlexWaygood](https://github.com/AlexWaygood) - [@MichaReiser](https://github.com/MichaReiser) - [@Gankra](https://github.com/Gankra) ## 0.0.1-alpha.30 Released on 2025-12-03. ### Bug fixes - Fix exhaustiveness checking for `match` statements over unions of generic instance types ([#21726](https://github.com/astral-sh/ruff/pull/21726)) - Don't introduce invalid syntax when autofixing `override-of-final-method` ([#21699](https://github.com/astral-sh/ruff/pull/21699)) - Suppress false positives when `dataclasses.dataclass(...)(cls)` is called imperatively ([#21729](https://github.com/astral-sh/ruff/pull/21729)) - Fix false positives for `class F(Generic[*Ts]): ...` ([#21723](https://github.com/astral-sh/ruff/pull/21723)) - Don't confuse multiple occurrences of `typing.Self` when binding bound methods ([#21754](https://github.com/astral-sh/ruff/pull/21754)) - Fix subtyping between `type[T]` and a union type, where `T` is a type variable in scope ([#21740](https://github.com/astral-sh/ruff/pull/21740)) - Fix subtyping between `type[T]` and `U`, where `T` is a type variable in scope and `U` is a type variable not in scope ([#21766](https://github.com/astral-sh/ruff/pull/21766)) - Fix false positives for `type[tuple[...]]` ([#21652](https://github.com/astral-sh/ruff/pull/21652)) ### Memory usage improvements - Significantly reduce memory usage (especially when ty is used as an LSP server) by enabling least-recently-used ([LRU](https://en.wikipedia.org/wiki/Page_replacement_algorithm#Least_recently_used)) cache eviction for module ASTs ([#21749](https://github.com/astral-sh/ruff/pull/21749)) ### LSP server - Add code action to ignore diagnostic on the current line ([#21595](https://github.com/astral-sh/ruff/pull/21595)) - Exclude `typing_extensions` from autocomplete suggestions unless it's really available ([#21731](https://github.com/astral-sh/ruff/pull/21731)) - Fix auto-import code action to handle pre-existing imports ([#21733](https://github.com/astral-sh/ruff/pull/21733)) - Fix "find all references" for types defined in stub files ([#21732](https://github.com/astral-sh/ruff/pull/21732)) - Fix "find all references" for symbols defined via aliased imports ([#21736](https://github.com/astral-sh/ruff/pull/21736)) ### Improvements to handling of type aliases - Default-specialize generic type aliases when they appear unspecialized in type expressions ([#21765](https://github.com/astral-sh/ruff/pull/21765)) - Infer a type alias as being a generic type alias if it includes a type variable in its definition, even in cases where the value subscripted with the type variable is inferred as having a dynamic type such as `Any` or `Unknown` ([#21730](https://github.com/astral-sh/ruff/pull/21730)) ### New `NamedTuple` diagnostics - Detect `NamedTuple` classes that have field names starting with underscores, which is banned at runtime ([#21697](https://github.com/astral-sh/ruff/pull/21697)) - Add a diagnostic detecting overrides of prohibited `NamedTuple` attributes ([#21717](https://github.com/astral-sh/ruff/pull/21717)) - Detect illegal uses of `super()` in methods of `NamedTuple` classes ([#21700](https://github.com/astral-sh/ruff/pull/21700)) ### Improvements to existing diagnostics - Improve diagnostics for unsupported comparison operations ([#21737](https://github.com/astral-sh/ruff/pull/21737)) - For `invalid-type-arguments` diagnostics, show the user where the type variable was defined ([#21727](https://github.com/astral-sh/ruff/pull/21727)) - Extend `invalid-explicit-override` to also cover properties decorated with `@override` that do not override anything ([#21756](https://github.com/astral-sh/ruff/pull/21756)) - Improve `@override`, `@final` and Liskov checks in cases where there are multiple reachable definitions ([#21767](https://github.com/astral-sh/ruff/pull/21767)) ### Contributors - [@MichaReiser](https://github.com/MichaReiser) - [@charliermarsh](https://github.com/charliermarsh) - [@dcreager](https://github.com/dcreager) - [@carljm](https://github.com/carljm) - [@AlexWaygood](https://github.com/AlexWaygood) - [@BurntSushi](https://github.com/BurntSushi) - [@ibraheemdev](https://github.com/ibraheemdev) - [@woodruffw](https://github.com/woodruffw) - [@sharkdp](https://github.com/sharkdp) ## 0.0.1-alpha.29 Released on 2025-11-28. ### Bug Fix - Fix multiple panics due to recursive type definitions ([#20566](https://github.com/astral-sh/ruff/pull/20566)) ### Type inference - Support `type[T]` where `T` is a type variable ([#21650](https://github.com/astral-sh/ruff/pull/21650)) - More precise inference for a failed specialization of a generic type ([#21651](https://github.com/astral-sh/ruff/pull/21651)) - Detect invalid overrides of methods that are marked as `typing.final` ([#21646](https://github.com/astral-sh/ruff/pull/21646)) - Fix subtyping of `type[Any]` / `type[T]` and protocols ([#21678](https://github.com/astral-sh/ruff/pull/21678)) - Added generics support for implicit and explicit (`typing.TypeAlias`) type aliases ([#21553](https://github.com/astral-sh/ruff/pull/21553)) ### LSP server - Add `import ...` code action for unresolved references ([#21629](https://github.com/astral-sh/ruff/pull/21629)) - Include all members on `type` in autocompletion suggestions for `type[]` types ([#21670](https://github.com/astral-sh/ruff/pull/21670)) - Mark comprehension targets as definitions in semantic highlighting ([#21636](https://github.com/astral-sh/ruff/pull/21636)) - Add IDE autofixes for two "Did you mean...?" suggestions ([#21667](https://github.com/astral-sh/ruff/pull/21667)) - Prettier rendering of `code:: lang` in docstrings ([#21665](https://github.com/astral-sh/ruff/pull/21665)) - Support go-to for patterns and typevars ([#21671](https://github.com/astral-sh/ruff/pull/21671)) ### Diagnostics - Add subdiagnostic hint if a variable with type `Never` is used in a type expression ([#21660](https://github.com/astral-sh/ruff/pull/21660)) - Improve diagnostic messages for invalid type arguments during explicit specialization ([#21635](https://github.com/astral-sh/ruff/pull/21635)) ### Contributors - [@lucach](https://github.com/lucach) - [@carljm](https://github.com/carljm) - [@ibraheemdev](https://github.com/ibraheemdev) - [@Gankra](https://github.com/Gankra) - [@AlexWaygood](https://github.com/AlexWaygood) - [@mtshiba](https://github.com/mtshiba) - [@oconnor663](https://github.com/oconnor663) - [@sharkdp](https://github.com/sharkdp) ## 0.0.1-alpha.28 Released on 2025-11-25. ### Bug fixes - Fix panic for unclosed string literal in type annotation position ([#21592](https://github.com/astral-sh/ruff/pull/21592)) ### Type inference - Check method definitions on subclasses for Liskov violations ([#21436](https://github.com/astral-sh/ruff/pull/21436)) - Eagerly evaluate `types.UnionType` elements as type expressions ([#21531](https://github.com/astral-sh/ruff/pull/21531)) - Extend Liskov checks to also cover classmethods and staticmethods ([#21598](https://github.com/astral-sh/ruff/pull/21598)) - Implement `typing.override` ([#21627](https://github.com/astral-sh/ruff/pull/21627)) - Narrow type context during literal promotion in generic class constructors ([#21574](https://github.com/astral-sh/ruff/pull/21574)) - Retain the function-like-ness of `Callable` types when binding `self` ([#21614](https://github.com/astral-sh/ruff/pull/21614)) - Substitute for `typing.Self` when checking protocol members ([#21569](https://github.com/astral-sh/ruff/pull/21569)) - Implement `TypedDict` structural assignment ([#21467](https://github.com/astral-sh/ruff/pull/21467)) - Make implicit submodule imports re-exported ([#21573](https://github.com/astral-sh/ruff/pull/21573)) - Support PEP 613 `typing.TypeAlias` type aliases ([#21394](https://github.com/astral-sh/ruff/pull/21394)) - Support generic aliases in `type[...]`, like `type[C[int]]` ([#21552](https://github.com/astral-sh/ruff/pull/21552)) - Tighten up handling of subscripts in type expressions ([#21503](https://github.com/astral-sh/ruff/pull/21503)) ### LSP server - Improve go-to-definition and add go-to-definition for inlay hints ([#21545](https://github.com/astral-sh/ruff/pull/21545), [#21546](https://github.com/astral-sh/ruff/pull/21546), [#21544](https://github.com/astral-sh/ruff/pull/21544), [#21616](https://github.com/astral-sh/ruff/pull/21616), [#21548](https://github.com/astral-sh/ruff/pull/21548)) - Implement go-to-type for inlay type hints ([#21533](https://github.com/astral-sh/ruff/pull/21533)) - Add "remove unused ignore comment" code action ([#21582](https://github.com/astral-sh/ruff/pull/21582)) - Don't suggest completions that aren't subclasses of `BaseException` after `raise` ([#21571](https://github.com/astral-sh/ruff/pull/21571)) - Implement double click to insert inlay hint ([#21600](https://github.com/astral-sh/ruff/pull/21600)) - Fix edge cases for autocomplete suppressions in variable bindings ([#21576](https://github.com/astral-sh/ruff/pull/21576)) - Implement docstring rendering to markdown ([#21550](https://github.com/astral-sh/ruff/pull/21550)) - Support string annotations ([#21577](https://github.com/astral-sh/ruff/pull/21577)) - Improve import detection for completions and support `from ...` completions ([#21547](https://github.com/astral-sh/ruff/pull/21547)) - Improve handling of hover/goto on imports ([#21572](https://github.com/astral-sh/ruff/pull/21572)) - Don't allow edits of some more invalid syntax types in inlay hints ([#21621](https://github.com/astral-sh/ruff/pull/21621)) - Resolve applicable overloads for hover on an overloaded function call ([#21417](https://github.com/astral-sh/ruff/pull/21417)) - Consistently add the `DEFINITION` modifier when computing semantic tokens ([#21521](https://github.com/astral-sh/ruff/pull/21521)) - Suppress autocomplete suggestions during variable binding ([#21549](https://github.com/astral-sh/ruff/pull/21549)) ### CLI - Exit with code `2` if there's any IO error ([#21508](https://github.com/astral-sh/ruff/pull/21508)) ### Diagnostics - Add hint about resolved Python version when a user attempts to import a member added on a newer version ([#21615](https://github.com/astral-sh/ruff/pull/21615)) - Attach subdiagnostics to `unresolved-import` errors for relative imports as well as absolute imports ([#21554](https://github.com/astral-sh/ruff/pull/21554)) - Avoid expression re-inference for diagnostics ([#21267](https://github.com/astral-sh/ruff/pull/21267)) - Improve message rendering of unused suppression diagnostic ([#21580](https://github.com/astral-sh/ruff/pull/21580)) - Improve concise diagnostics for invalid exceptions when a user catches a tuple of objects ([#21578](https://github.com/astral-sh/ruff/pull/21578)) - Improve diagnostics when `NotImplemented` is called ([#21523](https://github.com/astral-sh/ruff/pull/21523)) - Improve diagnostics when a submodule is not available as an attribute on a module-literal type ([#21561](https://github.com/astral-sh/ruff/pull/21561)) - Improve several "Did you mean?" suggestions ([#21597](https://github.com/astral-sh/ruff/pull/21597)) - Switch the error code from `unresolved-attribute` to `possibly-missing-attribute` for submodules that may not be available ([#21618](https://github.com/astral-sh/ruff/pull/21618)) ### Other - Improve debug messages when imports fail ([#21555](https://github.com/astral-sh/ruff/pull/21555)) ### Contributors - [@Gankra](https://github.com/Gankra) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@AlexWaygood](https://github.com/AlexWaygood) - [@RasmusNygren](https://github.com/RasmusNygren) - [@dcreager](https://github.com/dcreager) - [@BurntSushi](https://github.com/BurntSushi) - [@carljm](https://github.com/carljm) - [@MichaReiser](https://github.com/MichaReiser) - [@sharkdp](https://github.com/sharkdp) - [@oconnor663](https://github.com/oconnor663) - [@lucach](https://github.com/lucach) - [@ibraheemdev](https://github.com/ibraheemdev) ## 0.0.1-alpha.27 Released on 2025-11-18. ### Bug fixes - Fix panic for cyclic star imports ([#21428](https://github.com/astral-sh/ruff/pull/21428)) - Fix crashes when using a homebrew Python install ([#21405](https://github.com/astral-sh/ruff/pull/21405)) - Fix incorrect inference of `enum.auto()` for enums with non-`int` mixins, and imprecise inference of `enum.auto()` for single-member enums ([#20541](https://github.com/astral-sh/ruff/pull/20541)) - Fix global symbol lookup from eagerly executed scopes such as comprehensions and classes ([#21317](https://github.com/astral-sh/ruff/pull/21317)) - Fix false positive for instance attributes that are declared as `Final` in the class body but have their value assigned in the class's `__init__` method ([#21158](https://github.com/astral-sh/ruff/pull/21158)) - Use the return type of `__get__` for descriptor lookups even when `__get__` is called with incorrect arguments ([#21424](https://github.com/astral-sh/ruff/pull/21424)) - Consider parameters being declared `global` a syntax error ([#21312](https://github.com/astral-sh/ruff/pull/21312)) ### Type inference - Support `typing.NewType` ([#21157](https://github.com/astral-sh/ruff/pull/21157)) - Support `Callable` in implicit type aliases ([#21496](https://github.com/astral-sh/ruff/pull/21496)) - Support `typing.Union` in implicit type aliases ([#21363](https://github.com/astral-sh/ruff/pull/21363)) - Precise inference for generator expressions ([#21437](https://github.com/astral-sh/ruff/pull/21437)) - Support storing attributes in comprehension scopes ([#20856](https://github.com/astral-sh/ruff/pull/20856)) - Support `isinstance()` and `issubclass()` narrowing when the second argument is a `typing.py` stdlib alias ([#21391](https://github.com/astral-sh/ruff/pull/21391)) - Support `type[…]` and `Type[…]` in implicit type aliases ([#21421](https://github.com/astral-sh/ruff/pull/21421)) - Support attribute-expression `TYPE_CHECKING` conditionals ([#21449](https://github.com/astral-sh/ruff/pull/21449)) - Support class-arguments for dataclass transformers ([#21457](https://github.com/astral-sh/ruff/pull/21457)) - Support legacy `typing` special forms in implicit type aliases ([#21433](https://github.com/astral-sh/ruff/pull/21433)) - Support stringified annotations in value-position `Annotated` instances ([#21447](https://github.com/astral-sh/ruff/pull/21447)) - Support all parameters of dataclass transforms ([#21474](https://github.com/astral-sh/ruff/pull/21474)) - Support `__hash__` semantics and `unsafe_hash` for dataclasses ([#21470](https://github.com/astral-sh/ruff/pull/21470)) - Improve handling of version-specific features of dataclasses ([#21453](https://github.com/astral-sh/ruff/pull/21453)) - Correctly infer the specialization of a non-invariant PEP-695 generic class that has an annotated `self` parameter in its `__init__` method ([#21325](https://github.com/astral-sh/ruff/pull/21325)) - Improve use of type context when inferring the result of a generic constructor call ([#20933](https://github.com/astral-sh/ruff/pull/20933), [#21442](https://github.com/astral-sh/ruff/pull/21442)) - Improve use of type context when inferring the result of a generic call expression ([#21210](https://github.com/astral-sh/ruff/pull/21210)) - Improve heuristics used to decide when it is appropriate to "promote" a `Literal` type such as `Literal[42]` to its instance supertype (in this case, `int`) when solving type variables ([#21439](https://github.com/astral-sh/ruff/pull/21439)) - Improve use of type context to infer conditional expressions ([#21443](https://github.com/astral-sh/ruff/pull/21443)) - Make `__getattr__` available for `ModuleType` instances ([#21450](https://github.com/astral-sh/ruff/pull/21450)) - Introduce implicit local variables for `from` imports of submodules in `__init__.py(i)` ([#21173](https://github.com/astral-sh/ruff/pull/21173)) - Make implicit submodule locals only occur in global scope of an `__init__.py(i)` ([#21370](https://github.com/astral-sh/ruff/pull/21370)) - Make implicit submodule locals also occur for absolute `from` imports in `__init__.py(i)` files ([#21372](https://github.com/astral-sh/ruff/pull/21372)) - Consider `from thispackage import y` a re-export of `y` in `__init__.pyi` ([#21387](https://github.com/astral-sh/ruff/pull/21387)) - Allow PEP-604 unions in stubs and `TYPE_CHECKING` blocks prior to 3.10 ([#21379](https://github.com/astral-sh/ruff/pull/21379)) - Ensure annotation/type expressions in stub files are always deferred ([#21401](https://github.com/astral-sh/ruff/pull/21401), [#21456](https://github.com/astral-sh/ruff/pull/21456)) - Silence false-positive diagnostics when using `typing.Dict` or `typing.Callable` as the second argument to `isinstance()` ([#21386](https://github.com/astral-sh/ruff/pull/21386)) - Sync vendored typeshed stubs ([#21466](https://github.com/astral-sh/ruff/pull/21466)). [Typeshed diff](https://github.com/python/typeshed/compare/bf7214784877c52638844c065360d4814fae4c65...f8cdc0bd526301e873cd952eb0d457bdf2554e57) ### LSP server - Support for notebooks in VS Code ([#21175](https://github.com/astral-sh/ruff/pull/21175)) - Fix goto-definition for `float` and `complex` in type annotation positions ([#21388](https://github.com/astral-sh/ruff/pull/21388)) - Support goto-definition on call argument inlay hints ([#20349](https://github.com/astral-sh/ruff/pull/20349)) - Add more keywords to scope-based completions ([#21383](https://github.com/astral-sh/ruff/pull/21383)) - Add synthetic members to completions on dataclasses ([#21446](https://github.com/astral-sh/ruff/pull/21446)) - Only suggest the `import` keyword in autocompletions for `from ` statements ([#21291](https://github.com/astral-sh/ruff/pull/21291)) - Suppress completion suggestions following `as` tokens ([#21460](https://github.com/astral-sh/ruff/pull/21460)) - Suppress invalid suggestions in `import` statements ([#21484](https://github.com/astral-sh/ruff/pull/21484)) - Suppress redundant inlay hints for function args ([#21365](https://github.com/astral-sh/ruff/pull/21365)) - Suppress some trivial expression inlay hints ([#21367](https://github.com/astral-sh/ruff/pull/21367)) - Suppress inlay hints for `+1` and `-1` ([#21368](https://github.com/astral-sh/ruff/pull/21368)) - Improve [semantic token](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens) classification for names ([#21399](https://github.com/astral-sh/ruff/pull/21399)) - Classify parameter declarations as definitions when computing semantic tokens ([#21420](https://github.com/astral-sh/ruff/pull/21420)) ### Diagnostics - Better invalid-assignment diagnostics ([#21476](https://github.com/astral-sh/ruff/pull/21476)) - Better concise diagnostic messages ([#21498](https://github.com/astral-sh/ruff/pull/21498)) - Improve subscript assignment diagnostics ([#21411](https://github.com/astral-sh/ruff/pull/21411), [#21452](https://github.com/astral-sh/ruff/pull/21452)) - Improve diagnostic range for `non-subscriptable` diagnostics ([#21461](https://github.com/astral-sh/ruff/pull/21461)) - Improve diagnostics for invalid exceptions ([#21475](https://github.com/astral-sh/ruff/pull/21475)) - Add hyperlinks to rule codes in CLI ([#21502](https://github.com/astral-sh/ruff/pull/21502)) ### Performance improvements - Cache computation of dataclass/NamedTuple/TypedDict fields ([#21512](https://github.com/astral-sh/ruff/pull/21512)) - Faster subscript assignment checks for (unions of) `TypedDict`s ([#21378](https://github.com/astral-sh/ruff/pull/21378)) - Reduce memory allocations for string-literal types ([#21497](https://github.com/astral-sh/ruff/pull/21497)) ### Contributors - [@thejchap](https://github.com/thejchap) - [@mtshiba](https://github.com/mtshiba) - [@ibraheemdev](https://github.com/ibraheemdev) - [@Gankra](https://github.com/Gankra) - [@charliecloudberry](https://github.com/charliecloudberry) - [@lucach](https://github.com/lucach) - [@AlexWaygood](https://github.com/AlexWaygood) - [@MichaReiser](https://github.com/MichaReiser) - [@Glyphack](https://github.com/Glyphack) - [@dcreager](https://github.com/dcreager) - [@saada](https://github.com/saada) - [@11happy](https://github.com/11happy) - [@oconnor663](https://github.com/oconnor663) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@BurntSushi](https://github.com/BurntSushi) - [@RasmusNygren](https://github.com/RasmusNygren) - [@sharkdp](https://github.com/sharkdp) ## 0.0.1-alpha.26 Released on 2025-11-10. ### Bug fixes - Language server: For [semantic tokens](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens), fix range filtering for tokens starting at the end of the requested range ([#21193](https://github.com/astral-sh/ruff/pull/21193)) - Fix panic due to simplifying `Divergent` types out of intersections types ([#21253](https://github.com/astral-sh/ruff/pull/21253)) - Fix merging of `--exclude` CLI flag and `src.exclude` config-file setting ([#21341](https://github.com/astral-sh/ruff/pull/21341)) ### Type inference - Infer type of `self` for decorated methods and properties ([#21123](https://github.com/astral-sh/ruff/pull/21123)) - Add support for properties that return `Self` ([#21335](https://github.com/astral-sh/ruff/pull/21335)) - Understand legacy and PEP 695 `ParamSpec` ([#21139](https://github.com/astral-sh/ruff/pull/21139)) - Type inference for comprehensions ([#20962](https://github.com/astral-sh/ruff/pull/20962)) - Reachability and narrowing for enum methods ([#21130](https://github.com/astral-sh/ruff/pull/21130)) - Implicit type aliases: Support for PEP 604 unions, `Literal`s, `Optional`, and `Annotated` ([#21195](https://github.com/astral-sh/ruff/pull/21195), [#21296](https://github.com/astral-sh/ruff/pull/21296), [#21321](https://github.com/astral-sh/ruff/pull/21321)) - `dict` is not assignable to `TypedDict` ([#21238](https://github.com/astral-sh/ruff/pull/21238)) - Allow values of type `None` in type expressions ([#21263](https://github.com/astral-sh/ruff/pull/21263)) - Add narrowing for `isinstance()` and `issubclass()` checks that use PEP-604 unions ([#21334](https://github.com/astral-sh/ruff/pull/21334)) - Do not promote `Literal` types when solving type variables in contravariant positions ([#21164](https://github.com/astral-sh/ruff/pull/21164), )) - Fix lookup of `__new__` methods on instances ([#21147](https://github.com/astral-sh/ruff/pull/21147)) - Fix narrowing of generic classes in class patterns for `match` statements ([#21150](https://github.com/astral-sh/ruff/pull/21150)) - Improve understanding of disjointness for `@final` classes ([#21167](https://github.com/astral-sh/ruff/pull/21167)) - Fix the inferred signature of the synthesized `__init__` method of a non-dataclass inheriting from a generic dataclass ([#21159](https://github.com/astral-sh/ruff/pull/21159)) - Improve exhaustiveness analysis for type variables with bounds or constraints ([#21172](https://github.com/astral-sh/ruff/pull/21172)) - Prefer exact matches when solving constrained type variables ([#21165](https://github.com/astral-sh/ruff/pull/21165)) - Simplify unions containing multiple type variables during inference ([#21275](https://github.com/astral-sh/ruff/pull/21275)) - Use the declared attribute type when inferring union attribute assignments ([#21170](https://github.com/astral-sh/ruff/pull/21170)) - Sync vendored typeshed stubs ([#21178](https://github.com/astral-sh/ruff/pull/21178)). [Typeshed diff](https://github.com/python/typeshed/compare/d6f4a0f7102b1400a21742cf9b7ea93614e2b6ec...bf7214784877c52638844c065360d4814fae4c65) - Use declared attribute types as type context when solving type variables ([#21143](https://github.com/astral-sh/ruff/pull/21143)) - Don't union in the inferred type of a parameter's default value when inferring the type of an annotated parameter ([#21208](https://github.com/astral-sh/ruff/pull/21208)) - Support subscripting typing.Literal with a type alias ([#21207](https://github.com/astral-sh/ruff/pull/21207)) ### LSP server - Don't provide completions when in a class or function definition ([#21146](https://github.com/astral-sh/ruff/pull/21146)) - Favor symbols defined in the current file over imported symbols ([#21194](https://github.com/astral-sh/ruff/pull/21194)) and builtin symbols ([#21285](https://github.com/astral-sh/ruff/pull/21285)) ### Diagnostics - Add diagnostics for `isinstance()` and `issubclass()` calls that use invalid PEP-604 unions for their second argument ([#21343](https://github.com/astral-sh/ruff/pull/21343)) - Don't assume in diagnostic messages that a `TypedDict` key error is about subscript access ([#21166](https://github.com/astral-sh/ruff/pull/21166)) ### Other changes - Consistently wrap tokens in parser diagnostics in `backticks` instead of 'quotes' ([#21163](https://github.com/astral-sh/ruff/pull/21163)) - Discover the `site-packages` directory from the environment that ty is installed in ([#21286](https://github.com/astral-sh/ruff/pull/21286)), improving the ergonomics of `uvx ty check` - Support implicit imports of submodules in `__init__.pyi` ([#20855](https://github.com/astral-sh/ruff/pull/20855)) - Use "cannot" consistently over "can not" in diagnostics ([#21255](https://github.com/astral-sh/ruff/pull/21255)) - Resolve `from foo import bar` to the `foo.bar` submodule rather than using the `__getattr__` function in `foo/__init__.py` (in situations where they both exist)([#21260](https://github.com/astral-sh/ruff/pull/21260)) ### Contributors - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@dhruvmanila](https://github.com/dhruvmanila) - [@sharkdp](https://github.com/sharkdp) - [@Gankra](https://github.com/Gankra) - [@saada](https://github.com/saada) - [@zanieb](https://github.com/zanieb) - [@MichaReiser](https://github.com/MichaReiser) - [@ibraheemdev](https://github.com/ibraheemdev) - [@AlexWaygood](https://github.com/AlexWaygood) - [@lucach](https://github.com/lucach) - [@mtshiba](https://github.com/mtshiba) - [@carljm](https://github.com/carljm) ## 0.0.1-alpha.25 Released on 2025-10-29. ### Bug fixes - Fix bug where ty would think all types had an `__mro__` attribute ([#20995](https://github.com/astral-sh/ruff/pull/20995)) - Fix rare panic with highly cyclic `TypeVar` definitions ([#21059](https://github.com/astral-sh/ruff/pull/21059)) - Fix infinite recursion with generic type aliases ([#20969](https://github.com/astral-sh/ruff/pull/20969)) - Add missing newline before first diagnostic in CLI output ([#21058](https://github.com/astral-sh/ruff/pull/21058)) - Make the ty server's auto-import feature skip symbols in the current module ([#21100](https://github.com/astral-sh/ruff/pull/21100)) - Don't provide goto-definition for definitions which are not reexported in builtins ([#21127](https://github.com/astral-sh/ruff/pull/21127)) - Avoid duplicate diagnostics during multi-inference of standalone expressions ([#21056](https://github.com/astral-sh/ruff/pull/21056)) ### Type inference and diagnostics - Use constructor parameter types as context to inform solving type variables ([#21054](https://github.com/astral-sh/ruff/pull/21054)) - Consider `__len__` when determining the truthiness of an instance of a tuple class or a `@final` class ([#21049](https://github.com/astral-sh/ruff/pull/21049)) - Delegate truthiness inference of an enum `Literal` type to its enum-instance supertype ([#21060](https://github.com/astral-sh/ruff/pull/21060)) - Improve `invalid-argument-type` diagnostics where a union type was provided ([#21044](https://github.com/astral-sh/ruff/pull/21044)) ### LSP server - Suggest `type_check_only` items last in completions ([#20910](https://github.com/astral-sh/ruff/pull/20910)) - Render `import <...>` in completions when "label details" isn't supported ([#21109](https://github.com/astral-sh/ruff/pull/21109)) - Update workspace diagnostic progress every 50ms ([#21019](https://github.com/astral-sh/ruff/pull/21019)) ### CLI - Add `--no-progress` option to suppress the rendering of a progress bar ([#21063](https://github.com/astral-sh/ruff/pull/21063)) ### Contributors - [@ibraheemdev](https://github.com/ibraheemdev) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@BurntSushi](https://github.com/BurntSushi) - [@mtshiba](https://github.com/mtshiba) - [@AlexWaygood](https://github.com/AlexWaygood) - [@MichaReiser](https://github.com/MichaReiser) - [@decorator-factory](https://github.com/decorator-factory) ## 0.0.1-alpha.24 Released on 2025-10-23. ### Breaking changes - Rename `unknown-rule` lint to `ignore-comment-unknown-rule` ([#20948](https://github.com/astral-sh/ruff/pull/20948)) ### Type inference and diagnostics - Infer a type of `Self` for unannotated `self` parameters in methods ([#20922](https://github.com/astral-sh/ruff/pull/20922)) - Prefer the declared type over the inferred type for invariant collection literals ([#20927](https://github.com/astral-sh/ruff/pull/20927)) - Use declared variable types as bidirectional type context for solving type variables ([#20796](https://github.com/astral-sh/ruff/pull/20796)) - Support `dataclass_transform` for base class models ([#20783](https://github.com/astral-sh/ruff/pull/20783)) - Support dataclass-transform `field_specifiers` ([#20888](https://github.com/astral-sh/ruff/pull/20888)) - `dataclass_transform` support for fields with an `alias` ([#20961](https://github.com/astral-sh/ruff/pull/20961)) - Add support for legacy namespace packages ([#20897](https://github.com/astral-sh/ruff/pull/20897)) - Add suggestion to "unknown rule" diagnostics ([#20948](https://github.com/astral-sh/ruff/pull/20948)) - Improve error messages for "unresolved attribute" diagnostics ([#20963](https://github.com/astral-sh/ruff/pull/20963)) - Avoid unnecessarily widening generic specializations ([#20875](https://github.com/astral-sh/ruff/pull/20875)) - Truncate `Literal` type display in some situations ([#20928](https://github.com/astral-sh/ruff/pull/20928)) ### Bug fixes - Fix panic involving cyclic `TypeVar` default ([#20967](https://github.com/astral-sh/ruff/pull/20967)) - Fix panic involving ever-growing default types ([#20991](https://github.com/astral-sh/ruff/pull/20991)) - Fix panic involving infinitely expanding implicit attribute types ([#20988](https://github.com/astral-sh/ruff/pull/20988)) - Fix autocomplete suggestions when the cursor is at the end of a file ([#20993](https://github.com/astral-sh/ruff/pull/20993)) - Fix inconsistent highlighting of self ([#20986](https://github.com/astral-sh/ruff/pull/20986)) - Fix out-of-order semantic token for function with regular argument after kwargs ([#21013](https://github.com/astral-sh/ruff/pull/21013)) - Fix panic on recursive class definitions in a stub that use constrained type variables ([#20955](https://github.com/astral-sh/ruff/pull/20955)) - Fix panic when attempting to validate the members of a protocol that inherits from a protocol in another module ([#20956](https://github.com/astral-sh/ruff/pull/20956)) - Fix rare hang relating to multithreading ([#21038](https://github.com/astral-sh/ruff/pull/21038)) - Fix non-deterministic overload function inference ([#20966](https://github.com/astral-sh/ruff/pull/20966)) - Fix auto-import edits made by autocompletions for files with an existing `from __future__` import ([#20987](https://github.com/astral-sh/ruff/pull/20987)) ### LSP server - Support goto-definition for binary and unary operators ([#21001](https://github.com/astral-sh/ruff/pull/21001)) - Support goto-definition on vendored typeshed stubs ([#21020](https://github.com/astral-sh/ruff/pull/21020)) - Provide completions on `TypeVar`s ([#20943](https://github.com/astral-sh/ruff/pull/20943)) - Display variance when hovering over type variables ([#20900](https://github.com/astral-sh/ruff/pull/20900)) - Avoid sending an unnecessary "clear diagnostics" message for clients supporting [pull diagnostics](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_pullDiagnostics). ([#20989](https://github.com/astral-sh/ruff/pull/20989)) ### Other changes - Report `continue` and `break` statements outside loops as syntax errors ([#20944](https://github.com/astral-sh/ruff/pull/20944)) ### Contributors - [@TaKO8Ki](https://github.com/TaKO8Ki) - [@sharkdp](https://github.com/sharkdp) - [@InvalidPathException](https://github.com/InvalidPathException) - [@AlexWaygood](https://github.com/AlexWaygood) - [@mtshiba](https://github.com/mtshiba) - [@ibraheemdev](https://github.com/ibraheemdev) - [@Gankra](https://github.com/Gankra) - [@MichaReiser](https://github.com/MichaReiser) ## 0.0.1-alpha.23 Released on 2025-10-16. ### Bug fixes - Fix handling of dataclass `field()`s without default values ([#20914](https://github.com/astral-sh/ruff/pull/20914)) - Fix false-positive diagnostics on `super()` calls ([#20814](https://github.com/astral-sh/ruff/pull/20814), [#20843](https://github.com/astral-sh/ruff/pull/20843)) - Fix `match` pattern value narrowing to use equality semantics ([#20882](https://github.com/astral-sh/ruff/pull/20882)) - Fix "missing root" panic when handling completion requests ([#20917](https://github.com/astral-sh/ruff/pull/20917)) - Fix overwriting of declared base class attributes through undeclared subclass members ([#20764](https://github.com/astral-sh/ruff/pull/20764)) - Fix runaway execution time for mutually referential instance attributes ([#20645](https://github.com/astral-sh/ruff/pull/20645)) ### CLI - For `unresolved-import` diagnostics, limit the shown import paths to at most five, unless in verbose mode ([#20912](https://github.com/astral-sh/ruff/pull/20912)) - Write files that are slow to type check to log output ([#20836](https://github.com/astral-sh/ruff/pull/20836)) - Remove "pre-release software" warning ([#20817](https://github.com/astral-sh/ruff/pull/20817)) ### LSP server - Improve ranking of autocomplete suggestions ([#20807](https://github.com/astral-sh/ruff/pull/20807)) ### Type inference and diagnostics - Use return type annotations as context for bidirectional type inference ([#20528](https://github.com/astral-sh/ruff/pull/20528)) - Use bidirectional type context for `TypedDict` construction ([#20806](https://github.com/astral-sh/ruff/pull/20806)) - Add support for unpacking of heterogeneous tuples in unions ([#20377](https://github.com/astral-sh/ruff/pull/20377)) - Add a new diagnostic for generic classes that reference typevars from an enclosing scope ([#20822](https://github.com/astral-sh/ruff/pull/20822)) - Add hint when accessing standard library module attributes that are not available on the configured Python version ([#20909](https://github.com/astral-sh/ruff/pull/20909)) - Treat functions, methods, and dynamic types as function-like `Callable`s ([#20842](https://github.com/astral-sh/ruff/pull/20842)) - Treat `Callable`s as bound-method descriptors in special cases ([#20802](https://github.com/astral-sh/ruff/pull/20802)) - Treat `Callable` dunder members as bound method descriptors ([#20860](https://github.com/astral-sh/ruff/pull/20860)) - Sync vendored typeshed stubs ([#20876](https://github.com/astral-sh/ruff/pull/20876)). [Typeshed diff](https://github.com/python/typeshed/compare/91055c730ffcda6311654cf32d663858ece69bad...d6f4a0f7102b1400a21742cf9b7ea93614e2b6ec) ### Performance improvements - Improve performance by caching union simplification type relations ([#20477](https://github.com/astral-sh/ruff/pull/20477)) ### Contributors - [@mtshiba](https://github.com/mtshiba) - [@AlexWaygood](https://github.com/AlexWaygood) - [@ericmarkmartin](https://github.com/ericmarkmartin) - [@carljm](https://github.com/carljm) - [@ntBre](https://github.com/ntBre) - [@sharkdp](https://github.com/sharkdp) - [@BurntSushi](https://github.com/BurntSushi) - [@Gankra](https://github.com/Gankra) - [@MichaReiser](https://github.com/MichaReiser) - [@dcreager](https://github.com/dcreager) ## 0.0.1-alpha.22 Released on 2025-10-10. ### Bug fixes - Enforce that `typing_extensions` must come from a stdlib search path. This fixes a panic that could occur with a confusing backtrace if the `extra-paths` setting was incorrectly used to point to a virtual environment ([#20715](https://github.com/astral-sh/ruff/pull/20715)) - Fix server panic when opening a project located at `/` in the file system ([#20684](https://github.com/astral-sh/ruff/pull/20684)) - Fix panics when using `--output-format=gitlab` in CI environments ([#20550](https://github.com/astral-sh/ruff/pull/20550)) - Fix stack overflows that could occur when attempting to determine if a recursive `NamedTuple` type was disjoint from another type ([#20538](https://github.com/astral-sh/ruff/pull/20538)) - Fix panics in type inference when legacy TypeVars had bounds, constraints, or defaults that cyclically referred back to the TypeVar definition (directly or indirectly) ([#20598](https://github.com/astral-sh/ruff/pull/20598)) - Fix situations where a panic during resolution of type-checker query cycles would manifest in a hang ([#20577](https://github.com/astral-sh/ruff/pull/20577)) - When analyzing a .py file, do not error if there's also a .pyi for that module ([#20461](https://github.com/astral-sh/ruff/pull/20461)) - Recognise that the runtime object `typing.Protocol` is an instance of `_ProtocolMeta` ([#20488](https://github.com/astral-sh/ruff/pull/20488)) - Fix logic that attempted to determine whether a user had explicitly activated a Conda environment, which has implications for the search paths ty uses for module resolution ([#20675](https://github.com/astral-sh/ruff/pull/20675)) - Fix false negatives when iterables with the wrong type are unpacked into a function with a `*args` variadic parameter ([#20511](https://github.com/astral-sh/ruff/pull/20511)) ### Support for Python 3.14 - Use 3.14 as the default version ([#20725](https://github.com/astral-sh/ruff/pull/20725), [#20759](https://github.com/astral-sh/ruff/pull/20759), [#20760](https://github.com/astral-sh/ruff/pull/20760)) - Annotations are deferred by default for 3.14+ ([#20799](https://github.com/astral-sh/ruff/pull/20799)) - Fix false positives when accessing `__annotate__` (Py3.14+) or `__warningregistry__` as a module global ([#20154](https://github.com/astral-sh/ruff/pull/20154)) ### Improvements to `TypeVar` solving and inference of generic types - Improve solving of a type variable `T` if it appears in a union with non-`TypeVar`s (`T | None`, `T | str | None`, etc.) ([#20749](https://github.com/astral-sh/ruff/pull/20749)) - More precise type inference for dictionary literals ([#20523](https://github.com/astral-sh/ruff/pull/20523)) - When solving type variables, use type context to inform whether `Literal` types should be promoted to instance types ([#20776](https://github.com/astral-sh/ruff/pull/20776)) - Use annotated parameters as type context when solving type variables ([#20635](https://github.com/astral-sh/ruff/pull/20635)) - Correctly infer the return type of method calls when the method is annotated as returning `Self` ([#20517](https://github.com/astral-sh/ruff/pull/20517), [#20754](https://github.com/astral-sh/ruff/pull/20754)) - Use type context for inference of generic function calls ([#20476](https://github.com/astral-sh/ruff/pull/20476)) - Use `C[T]` instead of `C[Unknown]` for the upper bound of `Self` ([#20479](https://github.com/astral-sh/ruff/pull/20479)) ### Improvements to assignability, subtyping, and union simplification - Fix overly strict assignability implementation for intersections with negated gradual elements ([#20773](https://github.com/astral-sh/ruff/pull/20773)) - Ensure that `C[Any]` is understood as a subtype of `C[object]` if `C` is a covariant generic class ([#20592](https://github.com/astral-sh/ruff/pull/20592)) - Ensure that `~T` is never considered to be assignable to `T` where `T` is a type variable ([#20606](https://github.com/astral-sh/ruff/pull/20606)) - Improve assignability/subtyping between two protocol types ([#20368](https://github.com/astral-sh/ruff/pull/20368)) - Simplify `Any | (Any & T)` to `Any` ([#20593](https://github.com/astral-sh/ruff/pull/20593)) - Optimise and generalise union/intersection simplification ([#20602](https://github.com/astral-sh/ruff/pull/20602)) - Make protocol satisfiability checks more principled when a protocol has a method member that is generic over type variables scoped to the function ([#20568](https://github.com/astral-sh/ruff/pull/20568)) - Fix subtyping of invariant generics specialized with `Any`, ensuring that (for example) `list[Any]` is not considered a subtype of `list[Any]` ([#20650](https://github.com/astral-sh/ruff/pull/20650)) ### Server - Add LSP debug information command ([#20379](https://github.com/astral-sh/ruff/pull/20379)) - Add support for inlay hints on attribute assignment ([#20485](https://github.com/astral-sh/ruff/pull/20485)) ### Improvements to diagnostics - Improve diagnostics when a positional-only parameter is passed using a keyword argument ([#20495](https://github.com/astral-sh/ruff/pull/20495)) - Improve disambiguations of class names in diagnostics ([#20603](https://github.com/astral-sh/ruff/pull/20603), [#20756](https://github.com/astral-sh/ruff/pull/20756)) - Improve diagnostics for bad `@overload` definitions ([#20745](https://github.com/astral-sh/ruff/pull/20745)) - Truncate type display for long unions in some situations ([#20730](https://github.com/astral-sh/ruff/pull/20730)) - Rename "possibly unbound" diagnostics to "possibly missing" ([#20492](https://github.com/astral-sh/ruff/pull/20492)) ### Improvements to enum support - Allow multiple aliases to point to the same member ([#20669](https://github.com/astral-sh/ruff/pull/20669)) - implement `auto()` for `StrEnum` ([#20524](https://github.com/astral-sh/ruff/pull/20524)) ### Improvements to ty's `@overload` implementation - Support single-starred argument for overload call ([#20223](https://github.com/astral-sh/ruff/pull/20223)) - Filter overloads using variadic parameters ([#20547](https://github.com/astral-sh/ruff/pull/20547)) ### Other typing semantics and features - Do not union the inferred type of a module-global symbol with `Unknown` for the symbol's type when accessed from external scopes ([#20664](https://github.com/astral-sh/ruff/pull/20664)) - Ensure that class objects are understood as callable even if they do not override `object.__new__` or `object.__init__` ([#20521](https://github.com/astral-sh/ruff/pull/20521)) - Add support for `**kwargs` ([#20430](https://github.com/astral-sh/ruff/pull/20430)) - Ensure first-party module-resolution search paths always appear in a sensible order ([#20629](https://github.com/astral-sh/ruff/pull/20629)) - Respect `dataclass_transform` parameters for metaclass-based models ([#20780](https://github.com/astral-sh/ruff/pull/20780)) - Sync vendored typeshed stubs ([#20658](https://github.com/astral-sh/ruff/pull/20658)). [Typeshed diff](https://github.com/python/typeshed/compare/47dbbd6c914a5190d54bc5bd498d1e6633d97db2...91055c730ffcda6311654cf32d663858ece69bad) - Bring ty's `TypeIs` narrowing behaviour closer to ty's narrowing behaviour for `isinstance()` checks. ([#20591](https://github.com/astral-sh/ruff/pull/20591)) - `dataclass_transform`: Support `frozen_default` and `kw_only_default` ([#20761](https://github.com/astral-sh/ruff/pull/20761)) - Allow any string `Literal` type expression as a key when constructing a `TypedDict` ([#20792](https://github.com/astral-sh/ruff/pull/20792)) - Add `--venv` as an alias to `--python` on the command line ([#20718](https://github.com/astral-sh/ruff/pull/20718)) - Add search paths listed in `PYTHONPATH` to search paths used for ty's module resolution ([#20441](https://github.com/astral-sh/ruff/pull/20441), [#20490](https://github.com/astral-sh/ruff/pull/20490)) ### Contributors - [@thejchap](https://github.com/thejchap) - [@mtshiba](https://github.com/mtshiba) - [@Danielkonge](https://github.com/Danielkonge) - [@dcreager](https://github.com/dcreager) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@Gankra](https://github.com/Gankra) - [@BurntSushi](https://github.com/BurntSushi) - [@carljm](https://github.com/carljm) - [@dhruvmanila](https://github.com/dhruvmanila) - [@sharkdp](https://github.com/sharkdp) - [@mmlb](https://github.com/mmlb) - [@fgiacome](https://github.com/fgiacome) - [@Guillaume-Fgt](https://github.com/Guillaume-Fgt) - [@AlexWaygood](https://github.com/AlexWaygood) - [@Renkai](https://github.com/Renkai) - [@InvalidPathException](https://github.com/InvalidPathException) - [@ibraheemdev](https://github.com/ibraheemdev) - [@fatelei](https://github.com/fatelei) - [@github-actions](https://github.com/github-actions) - [@MichaReiser](https://github.com/MichaReiser) - [@ntBre](https://github.com/ntBre) - [@danparizher](https://github.com/danparizher) ## 0.0.1-alpha.21 ### Bug fixes - Fix inference of constructor calls to generic classes that have explicitly annotated `self` parameters in their `__init__` methods ([#20325](https://github.com/astral-sh/ruff/pull/20325)) - Fix a stack overflow when computing completions for recursive types ([#20354](https://github.com/astral-sh/ruff/pull/20354)) - Fix panic in `BoundMethodType::into_callable_type()` ([#20369](https://github.com/astral-sh/ruff/pull/20369)) - Fix stack overflows in binary comparison inference ([#20446](https://github.com/astral-sh/ruff/pull/20446)) - Fix many "too many cycle iterations" panics concerning recursive type aliases and/or recursive generics ([#20359](https://github.com/astral-sh/ruff/pull/20359)) - Fix stack overflow involving subtype checks for recursive type aliases ([#20259](https://github.com/astral-sh/ruff/pull/20259)) - Fix panic when inferring the type of an infinitely-nested-tuple implicit instance attribute ([#20333](https://github.com/astral-sh/ruff/pull/20333)) ### Server - Add autocomplete suggestions for unimported symbols ([#20207](https://github.com/astral-sh/ruff/pull/20207), [#20439](https://github.com/astral-sh/ruff/pull/20439)) - Include generated `NamedTuple` methods such as `_make`, `_asdict` and `_replace` in autocomplete suggestions ([#20356](https://github.com/astral-sh/ruff/pull/20356)) ### Configuration - Automatically add `python/` to `environment.root` if a `python/` folder exists in the root of a repository ([#20263](https://github.com/astral-sh/ruff/pull/20263)) ### CLI - Add GitHub output format ([#20358](https://github.com/astral-sh/ruff/pull/20358)) - Add GitLab output format ([#20155](https://github.com/astral-sh/ruff/pull/20155)) ### Typing semantics and features - Add support for generic [PEP-695 type aliases](https://peps.python.org/pep-0695/#generic-type-alias) ([#20219](https://github.com/astral-sh/ruff/pull/20219)) - Allow annotation expressions to be `ast::Attribute` nodes ([#20413](https://github.com/astral-sh/ruff/pull/20413)) - Allow protocols to participate in nominal subtyping as well as structural subtyping ([#20314](https://github.com/astral-sh/ruff/pull/20314)) - Attribute access on top/bottom materializations ([#20221](https://github.com/astral-sh/ruff/pull/20221)) - Bind `Self` type variables to the method, not the class ([#20366](https://github.com/astral-sh/ruff/pull/20366)) - Ensure various special-cased bound methods are understood as assignable to `Callable` ([#20330](https://github.com/astral-sh/ruff/pull/20330)) - Ensure various special-cased builtin functions are understood as assignable to `Callable` ([#20331](https://github.com/astral-sh/ruff/pull/20331)) - Fall back to `object` for attribute access on synthesized protocols ([#20286](https://github.com/astral-sh/ruff/pull/20286)) - Fix signature of `NamedTupleLike._make` ([#20302](https://github.com/astral-sh/ruff/pull/20302)) - Fix subtyping/assignability of function- and class-literal types to callback protocols ([#20363](https://github.com/astral-sh/ruff/pull/20363)) - Implement the legacy PEP-484 convention for indicating positional-only parameters ([#20248](https://github.com/astral-sh/ruff/pull/20248)) - Infer more precise types for collection literals ([#20360](https://github.com/astral-sh/ruff/pull/20360)) - Make `TypeIs` invariant in its type argument ([#20428](https://github.com/astral-sh/ruff/pull/20428)) - Narrow specialized generics using `isinstance()` ([#20256](https://github.com/astral-sh/ruff/pull/20256)) - Proper assignability/subtyping checks for protocols with method members ([#20165](https://github.com/astral-sh/ruff/pull/20165)) - Reduce false positives for `ParamSpec`s and `TypeVarTuple`s ([#20239](https://github.com/astral-sh/ruff/pull/20239)) - Overload evaluation: retry parameter matching for argument type expansion ([#20153](https://github.com/astral-sh/ruff/pull/20153)) - Simplify unions of enum literals and subtypes thereof ([#20324](https://github.com/astral-sh/ruff/pull/20324)) - Support "legacy" `typing.Self` in combination with [PEP-695](https://peps.python.org/pep-0695) generic contexts ([#20304](https://github.com/astral-sh/ruff/pull/20304)) - Treat `Hashable`, and similar protocols, equivalently to `object` for subtyping/assignability ([#20284](https://github.com/astral-sh/ruff/pull/20284)) - Treat `__new__` as a static method ([#20212](https://github.com/astral-sh/ruff/pull/20212)) - `TypedDict`: Add support for `typing.ReadOnly` ([#20241](https://github.com/astral-sh/ruff/pull/20241)) - Detect syntax errors stemming from `yield from` expressions inside async functions ([#20051](https://github.com/astral-sh/ruff/pull/20051)) - `"foo".startswith` is not an instance of `types.MethodWrapperType` ([#20317](https://github.com/astral-sh/ruff/pull/20317)) - Eliminate definitely-impossible types from union in equality narrowing ([#20164](https://github.com/astral-sh/ruff/pull/20164)) - Infer more precise types for the `name` and `value` properties on enum members ([#20311](https://github.com/astral-sh/ruff/pull/20311)) - Initial support for `slots=True` in dataclasses ([#20278](https://github.com/astral-sh/ruff/pull/20278)) - Improve type narrowing in situations involving nested functions ([#19932](https://github.com/astral-sh/ruff/pull/19932)) - Support type aliases in binary comparison inference ([#20445](https://github.com/astral-sh/ruff/pull/20445)) - Sync vendored typeshed stubs ([#20394](https://github.com/astral-sh/ruff/pull/20394)). [Typeshed diff](https://github.com/python/typeshed/compare/2480d7e7c74493a024eaf254c5d2c6f452c80ee2...47dbbd6c914a5190d54bc5bd498d1e6633d97db2) ### Diagnostics - Improve specialization-error diagnostics ([#20326](https://github.com/astral-sh/ruff/pull/20326)) ### Contributors - [@thejchap](https://github.com/thejchap) - [@AlexWaygood](https://github.com/AlexWaygood) - [@mtshiba](https://github.com/mtshiba) - [@JelleZijlstra](https://github.com/JelleZijlstra) - [@ibraheemdev](https://github.com/ibraheemdev) - [@TaKO8Ki](https://github.com/TaKO8Ki) - [@Glyphack](https://github.com/Glyphack) - [@ericmarkmartin](https://github.com/ericmarkmartin) - [@Renkai](https://github.com/Renkai) - [@sharkdp](https://github.com/sharkdp) - [@11happy](https://github.com/11happy) - [@BurntSushi](https://github.com/BurntSushi) - [@carljm](https://github.com/carljm) - [@dhruvmanila](https://github.com/dhruvmanila) - [@github-actions](https://github.com/github-actions) - [@ntBre](https://github.com/ntBre) ## 0.0.1-alpha.20 ### Bug fixes - Server: Cancel background tasks when shutdown is requested ([#20039](https://github.com/astral-sh/ruff/pull/20039)) - Server: Close signature help after `)` ([#20017](https://github.com/astral-sh/ruff/pull/20017)) - Server: Fix incorrect docstring in call signature completion ([#20021](https://github.com/astral-sh/ruff/pull/20021)) - Fix 'too many cycle iterations' for unions of literals ([#20137](https://github.com/astral-sh/ruff/pull/20137)) - Fix namespace packages that behave like partial stubs ([#19994](https://github.com/astral-sh/ruff/pull/19994)) - Fix server hang (unchanged diagnostics) when changing file on Windows ([#19991](https://github.com/astral-sh/ruff/pull/19991)) - Apply `KW_ONLY` sentinel only to local fields ([#19986](https://github.com/astral-sh/ruff/pull/19986)) - Ignore field specifiers when not specified in `@dataclass_transform` ([#20002](https://github.com/astral-sh/ruff/pull/20002)) ### Server - Add completion support for `import` and `from ... import` statements ([#19883](https://github.com/astral-sh/ruff/pull/19883)) - Add type as detail to completion items ([#20047](https://github.com/astral-sh/ruff/pull/20047)) - Ask the LSP client to watch all project search paths ([#19975](https://github.com/astral-sh/ruff/pull/19975)) - Fix incorrect inlay hint type ([#20044](https://github.com/astral-sh/ruff/pull/20044)) - Update goto definition, goto declaration and hover to consider constructor method (`__init__`) ([#20014](https://github.com/astral-sh/ruff/pull/20014)) - Add docstrings to completions based on type ([#20008](https://github.com/astral-sh/ruff/pull/20008)) - Fix goto targets for keyword arguments in nested function calls ([#20013](https://github.com/astral-sh/ruff/pull/20013)) - Introduce multiline pretty printer to render function signatures across multiple lines ([#19979](https://github.com/astral-sh/ruff/pull/19979)) ### Configuration - Distinguish base conda from child conda ([#19990](https://github.com/astral-sh/ruff/pull/19990)) ### Typing semantics and features - Add support for PEP 750: t-strings ([#20085](https://github.com/astral-sh/ruff/pull/20085)) - Add support for PEP 800: Disjoint bases ([#20084](https://github.com/astral-sh/ruff/pull/20084)) - Add precise inference for unpacking a TypeVar if the TypeVar has an upper bound with a precise tuple spec ([#19985](https://github.com/astral-sh/ruff/pull/19985)) - Add precise iteration and unpacking inference for string literals and bytes literals ([#20023](https://github.com/astral-sh/ruff/pull/20023)) - Completely ignore typeshed's stub for `Any` ([#20079](https://github.com/astral-sh/ruff/pull/20079)) - Enforce that an attribute on a class `X` must be callable in order to satisfy a member on a protocol `P` ([#20142](https://github.com/astral-sh/ruff/pull/20142)) - Evaluate static truthiness of non-definitely-bound symbols to "ambiguous" ([#19579](https://github.com/astral-sh/ruff/pull/19579)) - Fix the inferred interface of specialized generic protocols ([#19866](https://github.com/astral-sh/ruff/pull/19866)) - Infer slightly more precise types for comprehensions ([#20111](https://github.com/astral-sh/ruff/pull/20111)) - Disable boundness analysis for implicit instance attributes ([#20128](https://github.com/astral-sh/ruff/pull/20128)) - Add `Top[]` and `Bottom[]` special forms ([#20054](https://github.com/astral-sh/ruff/pull/20054)) - Preserve qualifiers when accessing attributes on unions/intersections ([#20114](https://github.com/astral-sh/ruff/pull/20114)) - Strict validation of protocol members ([#17750](https://github.com/astral-sh/ruff/pull/17750)) - Support `__init_subclass__` ([#20190](https://github.com/astral-sh/ruff/pull/20190)) - Unpack variadic argument type in specialization ([#20130](https://github.com/astral-sh/ruff/pull/20130)) - Use `invalid-assignment` error code for invalid assignments to `ClassVar`s ([#20156](https://github.com/astral-sh/ruff/pull/20156)) - Use specialized parameter type for overload filter ([#19964](https://github.com/astral-sh/ruff/pull/19964)) - `__class_getitem__` is a classmethod ([#20192](https://github.com/astral-sh/ruff/pull/20192)) - Add cycle detection for find_legacy_typevars ([#20124](https://github.com/astral-sh/ruff/pull/20124)) - Add support for cyclic legacy generic protocols ([#20125](https://github.com/astral-sh/ruff/pull/20125)) - Don't eagerly unpack aliases in user-authored unions ([#20055](https://github.com/astral-sh/ruff/pull/20055)) - Don't mark entire type-alias scopes as Deferred ([#20086](https://github.com/astral-sh/ruff/pull/20086)) - Ensure union normalization really normalizes ([#20147](https://github.com/astral-sh/ruff/pull/20147)) - Improve cycle-detection coverage for apply_type_mapping ([#20159](https://github.com/astral-sh/ruff/pull/20159)) - Linear variance inference for PEP-695 type parameters ([#18713](https://github.com/astral-sh/ruff/pull/18713)) - Minor `TypedDict` fixes ([#20146](https://github.com/astral-sh/ruff/pull/20146)) - Typecheck dict methods for `TypedDict` ([#19874](https://github.com/astral-sh/ruff/pull/19874)) - Validate constructor call of `TypedDict` ([#19810](https://github.com/astral-sh/ruff/pull/19810)) - Sync vendored typeshed stubs ([#20031](https://github.com/astral-sh/ruff/pull/20031), [#20083](https://github.com/astral-sh/ruff/pull/20083), [#20188](https://github.com/astral-sh/ruff/pull/20188)) [Typeshed diff](https://github.com/python/typeshed/compare/893b9a760deb3be64d13c748318e95a752230961...2480d7e7c74493a024eaf254c5d2c6f452c80ee2) ### Diagnostics - Add search paths info to unresolved import diagnostics ([#20040](https://github.com/astral-sh/ruff/pull/20040)) - Better error message for attempting to assign to a read-only property ([#20150](https://github.com/astral-sh/ruff/pull/20150)) - Improve diagnostics for bad calls to functions ([#20022](https://github.com/astral-sh/ruff/pull/20022)) - Improve disambiguation of types via fully qualified names ([#20141](https://github.com/astral-sh/ruff/pull/20141)) - Print diagnostics with fully qualified name to disambiguate some cases ([#19850](https://github.com/astral-sh/ruff/pull/19850)) ### Performance - Avoid unnecessary argument type expansion ([#19999](https://github.com/astral-sh/ruff/pull/19999)) - Limit argument expansion size for overload call evaluation ([#20041](https://github.com/astral-sh/ruff/pull/20041)) - Optimize TDD atom ordering ([#20098](https://github.com/astral-sh/ruff/pull/20098)) ### Contributors - [@carljm](https://github.com/carljm) - [@sharkdp](https://github.com/sharkdp) - [@dylwil3](https://github.com/dylwil3) - [@dcreager](https://github.com/dcreager) - [@MichaReiser](https://github.com/MichaReiser) - [@ericmarkmartin](https://github.com/ericmarkmartin) - [@Renkai](https://github.com/Renkai) - [@JelleZijlstra](https://github.com/JelleZijlstra) - [@BurntSushi](https://github.com/BurntSushi) - [@AlexWaygood](https://github.com/AlexWaygood) - [@github-actions](https://github.com/github-actions) - [@PrettyWood](https://github.com/PrettyWood) - [@dhruvmanila](https://github.com/dhruvmanila) - [@Glyphack](https://github.com/Glyphack) - [@Gankra](https://github.com/Gankra) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@leandrobbraga](https://github.com/leandrobbraga) ## 0.0.1-alpha.19 ### Bug fixes - Fix false-positive diagnostics if a function parameter is annotated with `type[P]` where `P` is a protocol class ([#19947](https://github.com/astral-sh/ruff/pull/19947)) - Fix ANSI colors in terminal output on old Windows terminals ([#19984](https://github.com/astral-sh/ruff/pull/19984)) - Fix protocol interface inference for protocols in stub files with `ClassVar` members and "subprotocols" that extend other protocols ([#19950](https://github.com/astral-sh/ruff/pull/19950)) - Fix inference of equality comparisons between enum members ([#19666](https://github.com/astral-sh/ruff/pull/19666)) - Remove incorrect type narrowing for `if type(x) is C[int]` ([#19926](https://github.com/astral-sh/ruff/pull/19926)) - Improve detection of `TypeError`s resulting from protocol classes illegally inheriting from non-protocol classes ([#19941](https://github.com/astral-sh/ruff/pull/19941)). We previously detected this error, but only when the protocol class illegally inherited from a non-generic class or an unspecialized generic class. We now also detect it when the protocol class inherits from a specialized generic class. - Fix incorrectly precise type inference in some situations involving nested scopes ([#19908](https://github.com/astral-sh/ruff/pull/19908)) - Fix unpacking a type alias with a precise tuple spec ([#19981](https://github.com/astral-sh/ruff/pull/19981)) ### `NamedTuple` semantics improvements - Synthesize read-only properties for all declared members on `NamedTuple` classes ([#19899](https://github.com/astral-sh/ruff/pull/19899)) - Allow any instance of a `NamedTuple` class to be passed to a function parameter annotated with `typing.NamedTuple` ([#19915](https://github.com/astral-sh/ruff/pull/19915)) - Detect `NamedTuple` classes where fields without default values illegally follow fields with default values ([#19945](https://github.com/astral-sh/ruff/pull/19945)). This causes `TypeError` to be raised at runtime. - Detect illegal multiple inheritance with `NamedTuple` ([#19943](https://github.com/astral-sh/ruff/pull/19943)). This causes `TypeError` to be raised at runtime. ### Other typing and semantics improvements - Add support for stubs packages with `partial` in their `py.typed` files ([#19931](https://github.com/astral-sh/ruff/pull/19931)) - Look for `site-packages` directories in `/lib64/` as well as `/lib/` on non-Windows systems ([#19978](https://github.com/astral-sh/ruff/pull/19978)). This change fixes a number of `unresolved-import` false-positive diagnostics reported by Poetry users. - Add diagnostics for invalid `await` expressions ([#19711](https://github.com/astral-sh/ruff/pull/19711)) - Add `else`-branch narrowing for `if type(a) is A` when `A` is `@final` ([#19925](https://github.com/astral-sh/ruff/pull/19925)) - Improve solving of typevars with defaults, and `typing.Self` ([#19786](https://github.com/astral-sh/ruff/pull/19786)) - Support the `kw_only` parameter for `dataclasses.dataclass()` and `dataclasses.field()` ([#19677](https://github.com/astral-sh/ruff/pull/19677)) - Sync vendored typeshed stubs ([#19923](https://github.com/astral-sh/ruff/pull/19923)). [Typeshed diff](https://github.com/python/typeshed/compare/3f08a4ed10b321c378107c236a06a33584869a9b...893b9a760deb3be64d13c748318e95a752230961). ### Server improvements - Improve goto/hover for definitions ([#19976](https://github.com/astral-sh/ruff/pull/19976)) ### Performance improvements - Short-circuit a server [inlay hints request](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_inlayHint) if all settings under `ty.inlayHints` are disabled ([#19963](https://github.com/astral-sh/ruff/pull/19963)) - Speedup server tracing checks ([#19965](https://github.com/astral-sh/ruff/pull/19965)) - Add caching to logic for inferring whether a class is a `NamedTuple`, a dataclass or a `TypedDict` ([#19912](https://github.com/astral-sh/ruff/pull/19912)) - Speedup project file discovery ([#19913](https://github.com/astral-sh/ruff/pull/19913)) ### Contributors - [@dcreager](https://github.com/dcreager) - [@MichaReiser](https://github.com/MichaReiser) - [@sharkdp](https://github.com/sharkdp) - [@github-actions](https://github.com/github-actions) - [@mtshiba](https://github.com/mtshiba) - [@theammir](https://github.com/theammir) - [@AlexWaygood](https://github.com/AlexWaygood) - [@thejchap](https://github.com/thejchap) - [@Gankra](https://github.com/Gankra) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@carljm](https://github.com/carljm) ## 0.0.1-alpha.18 ### Bug fixes - Fix goto definition on imports ([#19834](https://github.com/astral-sh/ruff/pull/19834)) - Support non-generic recursive type aliases that use [the `type` statement](https://docs.python.org/3/reference/simple_stmts.html#grammar-token-python-grammar-type_stmt) ([#19805](https://github.com/astral-sh/ruff/pull/19805)) - Handle cycles when finding implicit attributes ([#19833](https://github.com/astral-sh/ruff/pull/19833)) ### Server - Implement support for "rename" language server feature ([#19551](https://github.com/astral-sh/ruff/pull/19551)) - Add `ty.experimental.rename` server setting ([#19800](https://github.com/astral-sh/ruff/pull/19800)) - Add `ty.inlayHints.variableTypes` server setting ([#19780](https://github.com/astral-sh/ruff/pull/19780)) - Add inlay hints for call arguments (configured by `ty.inlayHints.callArgumentNames` server setting) ([#19269](https://github.com/astral-sh/ruff/pull/19269)) - Enable goto definition to jump to the runtime definition in the standard library for stdlib symbols (rather than the type definition in typeshed's stubs) ([#19529](https://github.com/astral-sh/ruff/pull/19529)) - Support LSP client settings ([#19614](https://github.com/astral-sh/ruff/pull/19614)) - Update goto range for attribute access to only target the attribute ([#19848](https://github.com/astral-sh/ruff/pull/19848)) - Warn users if the server received unknown options ([#19779](https://github.com/astral-sh/ruff/pull/19779)) - Render docstrings in hover ([#19882](https://github.com/astral-sh/ruff/pull/19882)) - Resolve docstrings for modules ([#19898](https://github.com/astral-sh/ruff/pull/19898)) ### Typing semantics and features - Add precise inference for indexing, slicing and unpacking `NamedTuple` instances ([#19560](https://github.com/astral-sh/ruff/pull/19560)) - Disallow `typing.TypedDict` in type expressions ([#19777](https://github.com/astral-sh/ruff/pull/19777)) - Implement module-level `__getattr__` support ([#19791](https://github.com/astral-sh/ruff/pull/19791)) - Improve ability to solve TypeVars when they appear in unions ([#19829](https://github.com/astral-sh/ruff/pull/19829)) - Improve subscript narrowing for `collections.ChainMap`, `collections.Counter`, `collections.deque` and `collections.OrderedDict` ([#19781](https://github.com/astral-sh/ruff/pull/19781)) - Extend all tuple special casing to tuple subclasses ([#19669](https://github.com/astral-sh/ruff/pull/19669)) - Use separate Rust types for bound and unbound type variables ([#19796](https://github.com/astral-sh/ruff/pull/19796)) - Validate writes to `TypedDict` keys ([#19782](https://github.com/astral-sh/ruff/pull/19782)) - `typing.Self` is bound by the method, not the class ([#19784](https://github.com/astral-sh/ruff/pull/19784)) - Fix deferred name loading in PEP695 generic classes/functions ([#19888](https://github.com/astral-sh/ruff/pull/19888)) - Improve handling of symbol-lookup edge cases involving class scopes ([#19795](https://github.com/astral-sh/ruff/pull/19795)) ### Performance - Improve performance around tuple types ([#19840](https://github.com/astral-sh/ruff/pull/19840)) - Improve performance of subtyping and assignability checks for protocols ([#19824](https://github.com/astral-sh/ruff/pull/19824)) - Improve multithreaded performance for large codebases ([#19867](https://github.com/astral-sh/ruff/pull/19867)) ### Memory usage optimizations - Reduce memory usage of `TupleSpec` and `TupleType` ([#19872](https://github.com/astral-sh/ruff/pull/19872)) - Reduce size of member table ([#19572](https://github.com/astral-sh/ruff/pull/19572)) ### Contributors - [@AlexWaygood](https://github.com/AlexWaygood) - [@Gankra](https://github.com/Gankra) - [@ntbre](https://github.com/ntBre) - [@MichaReiser](https://github.com/MichaReiser) - [@PrettyWood](https://github.com/PrettyWood) - [@dhruvmanila](https://github.com/dhruvmanila) - [@carljm](https://github.com/carljm) - [@dcreager](https://github.com/dcreager) - [@UnboundVariable](https://github.com/UnboundVariable) - [@sharkdp](https://github.com/sharkdp) - [@oconnor663](https://github.com/oconnor663) - [@MatthewMckee4](https://github.com/MatthewMckee4) ## 0.0.1-alpha.17 ### Bug fixes - Always refresh diagnostics after a watched files change ([#19697](https://github.com/astral-sh/ruff/pull/19697)) - Correctly instantiate generic class that inherits `__init__` from generic base class ([#19693](https://github.com/astral-sh/ruff/pull/19693)) - Don't panic with argument that doesn't actually implement Iterable ([#19602](https://github.com/astral-sh/ruff/pull/19602)) - Fix "peek definition" in playground ([#19592](https://github.com/astral-sh/ruff/pull/19592)) - Fix empty spans following a line terminator and unprintable character spans in diagnostics ([#19535](https://github.com/astral-sh/ruff/pull/19535)) - Fix incorrect diagnostic when calling `__setitem__` ([#19645](https://github.com/astral-sh/ruff/pull/19645)) - Fix lookup order of class variables before they are defined ([#19743](https://github.com/astral-sh/ruff/pull/19743)) - Fix more false positives related to `Generic` or `Protocol` being subscripted with a `ParamSpec` or `TypeVarTuple` ([#19764](https://github.com/astral-sh/ruff/pull/19764)) - Keep track of type qualifiers in stub declarations without right-hand side ([#19756](https://github.com/astral-sh/ruff/pull/19756)) ### Server - Add progress reporting to workspace diagnostics ([#19616](https://github.com/astral-sh/ruff/pull/19616)) - Add stub mapping support to signature help ([#19570](https://github.com/astral-sh/ruff/pull/19570)) - Added support for "document symbols" and "workspace symbols" ([#19521](https://github.com/astral-sh/ruff/pull/19521)) - Fix server panic in workspace diagnostics request handler when typing ([#19631](https://github.com/astral-sh/ruff/pull/19631)) - Implement caching for workspace and document diagnostics ([#19605](https://github.com/astral-sh/ruff/pull/19605)) - Implement long-polling for workspace diagnostics ([#19670](https://github.com/astral-sh/ruff/pull/19670)) - Implement streaming for workspace diagnostics ([#19657](https://github.com/astral-sh/ruff/pull/19657)) - Implemented support for "selection range" language server feature ([#19567](https://github.com/astral-sh/ruff/pull/19567)) ### CLI - Add progress bar to `--watch` mode ([#19729](https://github.com/astral-sh/ruff/pull/19729)) - Clear the terminal screen in `--watch` mode ([#19712](https://github.com/astral-sh/ruff/pull/19712)) - Resolve file symlinks in src walk ([#19674](https://github.com/astral-sh/ruff/pull/19674)) ### Typing semantics and features - Support `async`/`await`, `async with` and `yield from` ([#19595](https://github.com/astral-sh/ruff/pull/19595)) - Add support for `async for` loops and async iterables ([#19634](https://github.com/astral-sh/ruff/pull/19634)) - Don't include already-bound legacy typevars in function generic context ([#19558](https://github.com/astral-sh/ruff/pull/19558)) - Infer types for key-based access on `TypedDict`s ([#19763](https://github.com/astral-sh/ruff/pull/19763)) - Improve `isinstance()` truthiness analysis for generic types ([#19668](https://github.com/astral-sh/ruff/pull/19668)) - Infer `type[tuple[int, str]]` as the meta-type of `tuple[int, str]` ([#19741](https://github.com/astral-sh/ruff/pull/19741)) - Remove false positives when subscripting `Generic` or `Protocol` with a `ParamSpec` or `TypeVarTuple` ([#19749](https://github.com/astral-sh/ruff/pull/19749)) - Remove special casing for string-literal-in-tuple `__contains__` ([#19642](https://github.com/astral-sh/ruff/pull/19642)) - Remove special casing for tuple addition ([#19636](https://github.com/astral-sh/ruff/pull/19636)) - Return `Option` from `infer_tuple_type_expression` ([#19735](https://github.com/astral-sh/ruff/pull/19735)) - Support `as`-patterns in reachability analysis ([#19728](https://github.com/astral-sh/ruff/pull/19728)) - Support `__setitem__` and improve `__getitem__` related diagnostics ([#19578](https://github.com/astral-sh/ruff/pull/19578)) - Synthesize precise `__getitem__` overloads for tuple subclasses ([#19493](https://github.com/astral-sh/ruff/pull/19493)) - Track different uses of legacy typevars, including context when rendering typevars ([#19604](https://github.com/astral-sh/ruff/pull/19604)) - Upcast heterogeneous and mixed tuples to homogeneous tuples where it's necessary to solve a `TypeVar` ([#19635](https://github.com/astral-sh/ruff/pull/19635)) - Fix incorrect lazy scope narrowing ([#19744](https://github.com/astral-sh/ruff/pull/19744)) - Synthesize `__replace__` for dataclasses ([#19545](https://github.com/astral-sh/ruff/pull/19545)) ### Diagnostics - Add diagnostics for async context managers ([#19704](https://github.com/astral-sh/ruff/pull/19704)) - Display generic function signature properly ([#19544](https://github.com/astral-sh/ruff/pull/19544)) - Improve the `Display` for generic `type[]` types ([#19667](https://github.com/astral-sh/ruff/pull/19667)) - Remap Jupyter notebook cell indices in `ruff_db` ([#19698](https://github.com/astral-sh/ruff/pull/19698)) ### Documentation - Add the `ty` badge ([#897](https://github.com/astral-sh/ty/pull/897)) ### Contributors - [@mtshiba](https://github.com/mtshiba) - [@MichaReiser](https://github.com/MichaReiser) - [@sharkdp](https://github.com/sharkdp) - [@github-actions](https://github.com/github-actions) - [@UnboundVariable](https://github.com/UnboundVariable) - [@jorenham](https://github.com/jorenham) - [@silamon](https://github.com/silamon) - [@AlexWaygood](https://github.com/AlexWaygood) - [@thejchap](https://github.com/thejchap) - [@ngroman](https://github.com/ngroman) - [@leandrobbraga](https://github.com/leandrobbraga) - [@dcreager](https://github.com/dcreager) - [@ntbre](https://github.com/ntBre) - [@MatthewMckee4](https://github.com/MatthewMckee4) ## 0.0.1-alpha.16 ### Bug fixes - Fix server panics when hovering over invalid syntax in `Callable` annotations ([#19517](https://github.com/astral-sh/ruff/pull/19517)) - `match` statements: Fix narrowing and reachability of class patterns with arguments ([#19512](https://github.com/astral-sh/ruff/pull/19512)) - Fix server panics when hovering over illegal `Literal[…]` annotations with inner subscript expressions ([#19489](https://github.com/astral-sh/ruff/pull/19489)) - Pass down specialization to generic dataclass bases ([#19472](https://github.com/astral-sh/ruff/pull/19472)) ### Server - Add support for "go to definition" for attribute accesses and keyword arguments ([#19417](https://github.com/astral-sh/ruff/pull/19417)) - Add support for "go to definition" for import statements ([#19428](https://github.com/astral-sh/ruff/pull/19428)) - Add support for "document highlights" ([#19515](https://github.com/astral-sh/ruff/pull/19515)) - Add partial support for "find references" ([#19475](https://github.com/astral-sh/ruff/pull/19475)) - Prefer the runtime definition, not the stub definition, on a go-to-definition request for a class or function. Currently this is only implemented for definitions originating outside of the stdlib. ([#19471](https://github.com/astral-sh/ruff/pull/19471)) - Add [semantic token](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens) support for more identifiers ([#19473](https://github.com/astral-sh/ruff/pull/19473)) - Avoid rechecking the entire project when a file in the editor is opened or closed ([#19463](https://github.com/astral-sh/ruff/pull/19463)) ### Typing semantics and features - Handle splatted arguments in function calls ([#18996](https://github.com/astral-sh/ruff/pull/18996)) - Improve place lookup and narrowing in lazy scopes ([#19321](https://github.com/astral-sh/ruff/pull/19321)) - Add exhaustiveness checking and reachability analysis for `match` statements ([#19508](https://github.com/astral-sh/ruff/pull/19508)) - Improve reachability analysis for `isinstance(…)` branches ([#19503](https://github.com/astral-sh/ruff/pull/19503)) - Make tuple subclass constructors sound ([#19469](https://github.com/astral-sh/ruff/pull/19469)) - Extend tuple `__len__` and `__bool__` special casing to also cover tuple subclasses ([#19289](https://github.com/astral-sh/ruff/pull/19289)) - Add support for `dataclasses.field` ([#19553](https://github.com/astral-sh/ruff/pull/19553)) - Add support for `dataclasses.InitVar` ([#19527](https://github.com/astral-sh/ruff/pull/19527)) - Add support for `@warnings.deprecated` and `typing_extensions.deprecated` ([#19376](https://github.com/astral-sh/ruff/pull/19376)) - Do not consider a type `T` to satisfy a method member on a protocol unless the method is available on the meta-type of `T` ([#19187](https://github.com/astral-sh/ruff/pull/19187)) - Implement expansion of enums into unions of literals ([#19382](https://github.com/astral-sh/ruff/pull/19382)) - Support iterating over enums ([#19486](https://github.com/astral-sh/ruff/pull/19486)) - Detect enums if metaclass is a subtype of `EnumType` / `EnumMeta` ([#19481](https://github.com/astral-sh/ruff/pull/19481)) - Infer single-valuedness for enums deriving from `int` or `str` ([#19510](https://github.com/astral-sh/ruff/pull/19510)) - Detect illegal non-enum attribute accesses in `Literal` annotations ([#19477](https://github.com/astral-sh/ruff/pull/19477)) - Disallow assignment to `Final` class attributes ([#19457](https://github.com/astral-sh/ruff/pull/19457)) - Handle implicit instance attributes declared `Final` ([#19462](https://github.com/astral-sh/ruff/pull/19462)) - Disallow `Final` in function parameter- and return-type annotations ([#19480](https://github.com/astral-sh/ruff/pull/19480)) - Disallow illegal uses of `ClassVar` ([#19483](https://github.com/astral-sh/ruff/pull/19483)) - Make `del x` force a local resolution of `x` in the current scope ([#19389](https://github.com/astral-sh/ruff/pull/19389)) - Perform type narrowing for places marked `global` ([#19381](https://github.com/astral-sh/ruff/pull/19381)) - Infer correct types for attribute accesses on intersections with negative parts ([#19524](https://github.com/astral-sh/ruff/pull/19524)) - Sync vendored typeshed stubs ([typeshed diff](https://github.com/python/typeshed/compare/84e41f2853d7af3d651d620f093031cba849bd1d...08225953c98cfd375d80bc88865e5aae77d2c07f)) ### Memory usage optimizations - Reduce ty's memory usage (for example: [#19409](https://github.com/astral-sh/ruff/pull/19409), [#19435](https://github.com/astral-sh/ruff/pull/19435), [#19414](https://github.com/astral-sh/ruff/pull/19414)) ### Contributors - [@sharkdp](https://github.com/sharkdp) - [@BurntSushi](https://github.com/BurntSushi) - [@oconnor663](https://github.com/oconnor663) - [@Gankra](https://github.com/Gankra) - [@carljm](https://github.com/carljm) - [@AlexWaygood](https://github.com/AlexWaygood) - [@MichaReiser](https://github.com/MichaReiser) - [@dcreager](https://github.com/dcreager) - [@mtshiba](https://github.com/mtshiba) - [@UnboundVariable](https://github.com/UnboundVariable) ## 0.0.1-alpha.15 ### Bug fixes - Avoid stale diagnostics for open-files diagnostic mode ([#19273](https://github.com/astral-sh/ruff/pull/19273)) - Fix inconsistent semantic syntax highlighting for parameters ([#19418](https://github.com/astral-sh/ruff/pull/19418)) - Fix checking of virtual files after re-opening from an unsaved edit ([#19277](https://github.com/astral-sh/ruff/pull/19277)) - Show the correct ty version in the LSP server ([#19284](https://github.com/astral-sh/ruff/pull/19284)) - Do not surface settings errors in unrelated Python files ([#19206](https://github.com/astral-sh/ruff/pull/19206)) - Do not ignore conditionally defined dataclass fields ([#19197](https://github.com/astral-sh/ruff/pull/19197)) - Fix panic for attribute expressions with empty value ([#19069](https://github.com/astral-sh/ruff/pull/19069)) - Fix assignabiliy of dataclasses to `Callable` types ([#19192](https://github.com/astral-sh/ruff/pull/19192)) - Fix `__setattr__` call check precedence during attribute assignment ([#18347](https://github.com/astral-sh/ruff/pull/18347)) ### Server - Add definition and declaration providers (go-to-definition, go-to-declaration) ([#19371](https://github.com/astral-sh/ruff/pull/19371)) - Add signature help provider (show signature and docstring when writing a call expression) ([#19194](https://github.com/astral-sh/ruff/pull/19194)) - Add "kind" to completion suggestions ([#19216](https://github.com/astral-sh/ruff/pull/19216)) - Add completions for submodules that aren't attributes of their parent ([#19266](https://github.com/astral-sh/ruff/pull/19266)) - Filter out private type aliases from stub files when offering autocomplete suggestions ([#19282](https://github.com/astral-sh/ruff/pull/19282)) - Handle configuration errors in the LSP more gracefully ([#19262](https://github.com/astral-sh/ruff/pull/19262)) - Use Python version and path from VSCode Python extension ([#19012](https://github.com/astral-sh/ruff/pull/19012)) - Publish errors in settings as LSP diagnostics ([#19335](https://github.com/astral-sh/ruff/pull/19335)) ### Typing semantics and features - Add support for `nonlocal` statements ([#19112](https://github.com/astral-sh/ruff/pull/19112)) - Support empty function bodies in `if TYPE_CHECKING` blocks ([#19372](https://github.com/astral-sh/ruff/pull/19372)) - Emit a diagnostic when attempting to modify a `typing.Final`-qualified symbol ([#19178](https://github.com/astral-sh/ruff/pull/19178)) - Infer enum literal types when accessing enum members ([#19328](https://github.com/astral-sh/ruff/pull/19328)) - Synthesize `__setattr__` for frozen dataclasses ([#19307](https://github.com/astral-sh/ruff/pull/19307)) - Improve equivalence for module-literal types ([#19243](https://github.com/astral-sh/ruff/pull/19243)) - Reduce false positives for `TypedDict` types ([#19354](https://github.com/astral-sh/ruff/pull/19354)) - Emit an error for `global` uses if there is no explicit definition in the global scope ([#19344](https://github.com/astral-sh/ruff/pull/19344)) - Sync vendored typeshed stubs ([typeshed diff](https://github.com/python/typeshed/compare/f64707592dd3c32f756ddeebd012acb2b072aa0d...84e41f2853d7af3d651d620f093031cba849bd1d)) ### CLI - Add a `-q`/`--quiet` mode, `-qq` for silent output mode ([#19233](https://github.com/astral-sh/ruff/pull/19233)) ### Contributors - [@AlexWaygood](https://github.com/AlexWaygood) - [@github-actions](https://github.com/github-actions) - [@dhruvmanila](https://github.com/dhruvmanila) - [@sharkdp](https://github.com/sharkdp) - [@renovate](https://github.com/renovate) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@UnboundVariable](https://github.com/UnboundVariable) - [@oconnor663](https://github.com/oconnor663) - [@zanieb](https://github.com/zanieb) - [@MichaReiser](https://github.com/MichaReiser) - [@charliermarsh](https://github.com/charliermarsh) - [@Gankra](https://github.com/Gankra) - [@thejchap](https://github.com/thejchap) - [@BurntSushi](https://github.com/BurntSushi) - [@mdqst](https://github.com/mdqst) ## 0.0.1-alpha.14 ### Bug fixes - Add cycle detection to ty's implementation of disjointness between types, fixing a possible source of stack overflows when analysing recursive types ([#19139](https://github.com/astral-sh/ruff/pull/19139)) - Don't allow first-party code to shadow the stdlib `types` module ([#19128](https://github.com/astral-sh/ruff/pull/19128)). This fixes another possible source of stack overflows. - Fix descriptor lookups for most types that overlap with `None` ([#19120](https://github.com/astral-sh/ruff/pull/19120)). This means that e.g. `object().__str__()` now correctly binds the `self` argument of the `__str__` method, as the `object` type overlaps with `None`. ### Server - Filter a symbol from a stub file in autocomplete suggestions if it is an implementation detail of the stub ([#19121](https://github.com/astral-sh/ruff/pull/19121)) - Add initial support for [semantic tokens](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens) ([#19108](https://github.com/astral-sh/ruff/pull/19108)). This feature allows editors to apply more advanced syntax highlighting. Currently, the supported tokens are: `Namespace`, `Class`, `Parameter`, `SelfParameter`,`ClsParameter`, `Variable`, `Property`, `Function`, `Method`, `Keyword`, `String`, `Number`, `Decorator`, `BuiltinConstant` and `TypeParameter`. - Initial support for [workspace diagnostics](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_diagnostic) ([#18939](https://github.com/astral-sh/ruff/pull/18939)). Enable this feature by setting the `ty.diagnosticMode` configuration setting to `"workspace"`. - Use Python syntax highlighting in on-hover content ([#19082](https://github.com/astral-sh/ruff/pull/19082)) ### Typing semantics and features - Understand that calls to functions returning `Never` / `NoReturn` are terminal with respect to control flow ([#18333](https://github.com/astral-sh/ruff/pull/18333)) - Add subtyping between `type[]` types and `Callable` types ([#19026](https://github.com/astral-sh/ruff/pull/19026)) - Support bare `ClassVar` annotations ([#15768](https://github.com/astral-sh/ruff/pull/15768)) - Understand that two protocols with equivalent method members are equivalent ([#18659](https://github.com/astral-sh/ruff/pull/18659)) - Support declared-only instance attributes such as `self.x: int` ([#19048](https://github.com/astral-sh/ruff/pull/19048)) - Sync vendored typeshed stubs ([#19174](https://github.com/astral-sh/ruff/pull/19174)): [typeshed diff](https://github.com/python/typeshed/compare/3f727b0cd6620b7fca45318dd34542b1e1c7dbfb...f64707592dd3c32f756ddeebd012acb2b072aa0d) - Use the inferred type as the declared type for bare `Final` symbols ([#19142](https://github.com/astral-sh/ruff/pull/19142)) ### Contributors - [@iyakushev](https://github.com/iyakushev) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@zanieb](https://github.com/zanieb) - [@sharkdp](https://github.com/sharkdp) - [@UnboundVariable](https://github.com/UnboundVariable) - [@abhijeetbodas2001](https://github.com/abhijeetbodas2001) - [@github-actions](https://github.com/github-actions) - [@dhruvmanila](https://github.com/dhruvmanila) - [@AlexWaygood](https://github.com/AlexWaygood) - [@carljm](https://github.com/carljm) - [@CodeMan62](https://github.com/CodeMan62) ## 0.0.1-alpha.13 ### Bug fixes - Fix stack overflows related to mutually recursive protocols ([#19003](https://github.com/astral-sh/ruff/pull/19003)) - Don't add incorrect subdiagnostic for `unresolved-reference` in `staticmethod`s and `classmethod`s ([#18487](https://github.com/astral-sh/ruff/pull/18487)) - Fix rendering of long lines in diagnostic messages that are indented with tabs ([#18962](https://github.com/astral-sh/ruff/pull/18962)) - Fix reachability of star import definitions for nonlocal lookups ([#19066](https://github.com/astral-sh/ruff/pull/19066)) ### Typing semantics and features - Support variable-length tuples in unpacking assignments ([#18948](https://github.com/astral-sh/ruff/pull/18948)) - Allow declared-only class-level attributes to be accessed on the class ([#19071](https://github.com/astral-sh/ruff/pull/19071)) - Infer nonlocal types as unions of all reachable bindings ([#18750](https://github.com/astral-sh/ruff/pull/18750)) - Use all reachable bindings for instance attributes and deferred lookups ([#18955](https://github.com/astral-sh/ruff/pull/18955)) - Improve protocol member type checking and relation handling ([#18847](https://github.com/astral-sh/ruff/pull/18847)) - Rework disjointness of protocol instances vs types with possibly unbound attributes, preventing some false instances of `Never` in `hasattr` narrowing ([#19043](https://github.com/astral-sh/ruff/pull/19043)) - Make tuple instantiations sound ([#18987](https://github.com/astral-sh/ruff/pull/18987)) - Add subdiagnostic about empty bodies in more cases ([#18942](https://github.com/astral-sh/ruff/pull/18942)) - Improve type-inference for `__import__(name)` and `importlib.import_module(name)` ([#19008](https://github.com/astral-sh/ruff/pull/19008)) - Eagerly evaluate certain constraints when analyzing control flow ([#18998](https://github.com/astral-sh/ruff/pull/18998), [#19044](https://github.com/astral-sh/ruff/pull/19044), [#19068](https://github.com/astral-sh/ruff/pull/19068)) - Update typeshed stubs ([#19060](https://github.com/astral-sh/ruff/pull/19060)): [typeshed diff](https://github.com/python/typeshed/compare/ecd5141cc036366cc9e3ca371096d6a14b0ccd13...3f727b0cd6620b7fca45318dd34542b1e1c7dbfb) ### Server - Add `builtins` to completions ([#18982](https://github.com/astral-sh/ruff/pull/18982)) - Support LSP go-to with vendored typeshed stubs ([#19057](https://github.com/astral-sh/ruff/pull/19057)) ### Documentation - The ty documentation is now available at [docs.astral.sh/ty](https://docs.astral.sh/ty) ([#744](https://github.com/astral-sh/ty/pull/744)) ### Performance - Remove `ScopedExpressionId` ([#19019](https://github.com/astral-sh/ruff/pull/19019)) ### Contributors - [@InSyncWithFoo](https://github.com/InSyncWithFoo) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@dcreager](https://github.com/dcreager) - [@mtshiba](https://github.com/mtshiba) - [@BurntSushi](https://github.com/BurntSushi) - [@sharkdp](https://github.com/sharkdp) - [@ibraheemdev](https://github.com/ibraheemdev) - [@github-actions](https://github.com/github-actions) - [@carljm](https://github.com/carljm) - [@AlexWaygood](https://github.com/AlexWaygood) - [@MichaReiser](https://github.com/MichaReiser) - [@zanieb](https://github.com/zanieb) ## 0.0.1-alpha.12 ### Bug fixes - Avoid duplicate diagnostic when reporting errors in unpacked assignments ([#18897](https://github.com/astral-sh/ruff/pull/18897)) - Fix panics when "pulling types" for `ClassVar` or `Final` parameterized with >1 argument ([#18824](https://github.com/astral-sh/ruff/pull/18824)). These could cause issues when hovering over symbols in an IDE. ### Improved modeling of Python runtime semantics - Add support for `@staticmethod`s ([#18809](https://github.com/astral-sh/ruff/pull/18809)) - Discover implicit class attribute assignments in `@classmethod`-decorated methods. Recognize that assignments in the body of a `@staticmethod`-decorated method are never instance attributes ([#18587](https://github.com/astral-sh/ruff/pull/18587)) - Report when a dataclass contains more than one `KW_ONLY` field ([#18731](https://github.com/astral-sh/ruff/pull/18731)) ### Type narrowing improvements - Ty will now perform `isinstance()` and `issubclass()` narrowing when the second argument is a union type, intersection type or `TypeVar` type ([#18900](https://github.com/astral-sh/ruff/pull/18900)) - Ty now narrows types in comprehensions and generator expressions ([#18934](https://github.com/astral-sh/ruff/pull/18934)) - Understand two `NominalInstanceType`s as disjoint types if attempting to use multiple inheritance with their underlying classes would result in an instance memory layout conflict ([#18864](https://github.com/astral-sh/ruff/pull/18864)) ### Other typing semantics features - Support "mixed" tuples such as `tuple[int, *tuple[str, ...]]` ([#18600](https://github.com/astral-sh/ruff/pull/18600), [#18901](https://github.com/astral-sh/ruff/pull/18901)) - Support type inference for subscript expressions on union types ([#18846](https://github.com/astral-sh/ruff/pull/18846)) - Introduce a new subtyping framework in which gradual types can participate, allowing for more advanced union type simplification ([#18799](https://github.com/astral-sh/ruff/pull/18799)) - Surface the matched overload directly when reporting a diagnostic for an invalid call to an overloaded function ([#18452](https://github.com/astral-sh/ruff/pull/18452)) ### Improvements to server autocompletions - Add completions for `from module import ` ([#18830](https://github.com/astral-sh/ruff/pull/18830)) - Enforce sort order of completions ([#18917](https://github.com/astral-sh/ruff/pull/18917)) - Include imported sub-modules as attributes on modules for completions ([#18898](https://github.com/astral-sh/ruff/pull/18898)) ### Configuration - Anchor all `src.exclude` patterns, for consistency with `src.include` patterns ([#18685](https://github.com/astral-sh/ruff/pull/18685)) - Change `environment.root` to accept multiple paths ([#18913](https://github.com/astral-sh/ruff/pull/18913)) - Rename `src.root` setting to `environment.root` ([#18760](https://github.com/astral-sh/ruff/pull/18760)) - Support `--python=` ([#18827](https://github.com/astral-sh/ruff/pull/18827)) ### Contributors - [@BurntSushi](https://github.com/BurntSushi) - [@InSyncWithFoo](https://github.com/InSyncWithFoo) - [@suneettipirneni](https://github.com/suneettipirneni) - [@AlexWaygood](https://github.com/AlexWaygood) - [@dhruvmanila](https://github.com/dhruvmanila) - [@sharkdp](https://github.com/sharkdp) - [@MichaReiser](https://github.com/MichaReiser) - [@med1844](https://github.com/med1844) - [@dcreager](https://github.com/dcreager) - [@carljm](https://github.com/carljm) ## 0.0.1-alpha.11 ### Breaking changes - Stabilize auto-complete; remove the opt-in experimental setting ([#18650](https://github.com/astral-sh/ruff/pull/18650)) ### Bug fixes - Fix binary expression inference between Boolean literals and `bool` instances ([#18663](https://github.com/astral-sh/ruff/pull/18663)) - Fix panic that could occur when printing a class's "header" in diagnostic messages ([#18670](https://github.com/astral-sh/ruff/pull/18670)) - Fix panic when attempting to provide autocompletions for an instance of a class that assigns attributes to `self[0]` ([#18707](https://github.com/astral-sh/ruff/pull/18707)) - Fix panics when "pulling types" for various special forms that have the wrong number of parameters. These could cause issues when hovering over symbols in an IDE. ([#18642](https://github.com/astral-sh/ruff/pull/18642)) ### Typing semantics and features - Support type narrowing for attribute and subscript expressions ([#17643](https://github.com/astral-sh/ruff/pull/17643)) - Add partial support for `TypeIs` ([#18589](https://github.com/astral-sh/ruff/pull/18589)) - Support `dataclasses.KW_ONLY` ([#18677](https://github.com/astral-sh/ruff/pull/18677)) - Filter overloads based on `Any` / `Unknown` ([#18607](https://github.com/astral-sh/ruff/pull/18607)) - Improve reachability analysis ([#18621](https://github.com/astral-sh/ruff/pull/18621)) - Model `T: Never` as a subtype of `Never` ([#18687](https://github.com/astral-sh/ruff/pull/18687)) - Update typeshed stubs ([#18679](https://github.com/astral-sh/ruff/pull/18679)): [typeshed diff](https://github.com/python/typeshed/compare/5a3c495d2f6fa9b68cd99f39feba4426e4d17ea9...ecd5141cc036366cc9e3ca371096d6a14b0ccd13) ### Configuration - Allow overriding rules for specific files ([#18648](https://github.com/astral-sh/ruff/pull/18648)) ### Server - Add `python.ty.disableLanguageServices` config ([#18230](https://github.com/astral-sh/ruff/pull/18230)) ### Contributors - [@dhruvmanila](https://github.com/dhruvmanila) - [@felixscherz](https://github.com/felixscherz) - [@MichaReiser](https://github.com/MichaReiser) - [@alpaylan](https://github.com/alpaylan) - [@mtshiba](https://github.com/mtshiba) - [@github-actions](https://github.com/github-actions) - [@BurntSushi](https://github.com/BurntSushi) - [@InSyncWithFoo](https://github.com/InSyncWithFoo) - [@AlexWaygood](https://github.com/AlexWaygood) - [@abhijeetbodas2001](https://github.com/abhijeetbodas2001) - [@sharkdp](https://github.com/sharkdp) - [@ibraheemdev](https://github.com/ibraheemdev) ## 0.0.1-alpha.10 ### Server - Improve support for `object.` completions ([#18629](https://github.com/astral-sh/ruff/pull/18629)) ### Configuration - Add file inclusion and exclusion ([#18498](https://github.com/astral-sh/ruff/pull/18498)) - Infer the Python version from `--python=` on Unix ([#18550](https://github.com/astral-sh/ruff/pull/18550)) ### Bug fixes - Delay computation of 'unbound' visibility for implicit instance attributes ([#18669](https://github.com/astral-sh/ruff/pull/18669)). This fixes a significant performance regression in version 0.0.1-alpha.9. ### Typing semantics and features - Support the `del` statement; model implicit deletion of except handler names ([#18593](https://github.com/astral-sh/ruff/pull/18593)) ### Release - Include ruff/ directory in release source tarballs ([#617](https://github.com/astral-sh/ty/pull/617)) ### Contributors - [@AlexWaygood](https://github.com/AlexWaygood) - [@BurntSushi](https://github.com/BurntSushi) - [@Gankra](https://github.com/Gankra) - [@mtshiba](https://github.com/mtshiba) - [@sharkdp](https://github.com/sharkdp) - [@dhruvmanila](https://github.com/dhruvmanila) - [@MichaReiser](https://github.com/MichaReiser) ## 0.0.1-alpha.9 ### Typing semantics and features - Add generic inference for dataclasses ([#18443](https://github.com/astral-sh/ruff/pull/18443)) - Add support for global `__debug__` constant ([#18540](https://github.com/astral-sh/ruff/pull/18540)) - Argument type expansion for overload call evaluation ([#18382](https://github.com/astral-sh/ruff/pull/18382)) - Exclude members starting with `_abc_` from a protocol interface ([#18467](https://github.com/astral-sh/ruff/pull/18467)) - Infer `list[T]` for starred target in unpacking ([#18401](https://github.com/astral-sh/ruff/pull/18401)) - Infer `list[T]` when unpacking non-tuple type ([#18438](https://github.com/astral-sh/ruff/pull/18438)) - Support type annotation for legacy typing aliases for generic classes ([#18404](https://github.com/astral-sh/ruff/pull/18404)) - Allow using `dataclasses.dataclass` as a function ([#18440](https://github.com/astral-sh/ruff/pull/18440)) - Type narrowing for attribute/subscript assignments ([#18041](https://github.com/astral-sh/ruff/pull/18041)) ### Diagnostics - Add hints to `invalid-type-form` for common mistakes ([#18543](https://github.com/astral-sh/ruff/pull/18543)) - Add subdiagnostic suggestion to `unresolved-reference` diagnostic when variable exists on `self` ([#18444](https://github.com/astral-sh/ruff/pull/18444)) - Track the origin of the `environment.python` setting for better error messages ([#18483](https://github.com/astral-sh/ruff/pull/18483)) ### CLI - Fix `--python` argument for Windows, and improve error messages for bad `--python` arguments ([#18457](https://github.com/astral-sh/ruff/pull/18457)) ### Bug fixes - Meta-type of type variables should be `type[..]` ([#18439](https://github.com/astral-sh/ruff/pull/18439)) - Only consider a type `T` a subtype of a protocol `P` if all of `P`'s members are fully bound on `T` ([#18466](https://github.com/astral-sh/ruff/pull/18466)) - Fix false positives for legacy `ParamSpec`s inside `Callable` type expressions ([#18426](https://github.com/astral-sh/ruff/pull/18426)) - Fix panic when pulling types for `UnaryOp` expressions inside `Literal` slices ([#18536](https://github.com/astral-sh/ruff/pull/18536)) - Fix panic when trying to pull types for attribute expressions inside `Literal` type expressions ([#18535](https://github.com/astral-sh/ruff/pull/18535)) - Fix panic when trying to pull types for subscript expressions inside `Callable` type expressions ([#18534](https://github.com/astral-sh/ruff/pull/18534)) - Treat lambda functions as instances of `types.FunctionType` ([#18431](https://github.com/astral-sh/ruff/pull/18431)) - Implement disjointness between `Callable` and `SpecialForm` ([#18503](https://github.com/astral-sh/ruff/pull/18503)) ### Server - Fix stale diagnostics in documents on Windows ([#18544](https://github.com/astral-sh/ruff/pull/18544)) - Add support for `object.` completions ([#18468](https://github.com/astral-sh/ruff/pull/18468)) - Only provide declarations and bindings as completions ([#18456](https://github.com/astral-sh/ruff/pull/18456)) ### Documentation - Add `CONDA_PREFIX` to `--python` documentation ([#18574](https://github.com/astral-sh/ruff/pull/18574)) - Update list of referenced environment variables ([#612](https://github.com/astral-sh/ty/pull/612)) - Document how the default value for `python-version` is determined ([#18549](https://github.com/astral-sh/ruff/pull/18549)) - Document the `"all"` option for `python-platform` ([#18548](https://github.com/astral-sh/ruff/pull/18548)) ### Contributors - [@AlexWaygood](https://github.com/AlexWaygood) - [@charliermarsh](https://github.com/charliermarsh) - [@mtshiba](https://github.com/mtshiba) - [@benbaror](https://github.com/benbaror) - [@sharkdp](https://github.com/sharkdp) - [@carljm](https://github.com/carljm) - [@MichaReiser](https://github.com/MichaReiser) - [@lipefree](https://github.com/lipefree) - [@BurntSushi](https://github.com/BurntSushi) - [@DetachHead](https://github.com/DetachHead) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@suneettipirneni](https://github.com/suneettipirneni) - [@abhijeetbodas2001](https://github.com/abhijeetbodas2001) - [@ibraheemdev](https://github.com/ibraheemdev) - [@dhruvmanila](https://github.com/dhruvmanila) ## 0.0.1-alpha.8 ### Typing semantics and features - Add subtyping between Callable types and class literals with `__init__` ([#17638](https://github.com/astral-sh/ruff/pull/17638)) - Implement implicit inheritance from `Generic[]` for PEP-695 generic classes ([#18283](https://github.com/astral-sh/ruff/pull/18283)) - Infer the Python version from the environment if feasible ([#18057](https://github.com/astral-sh/ruff/pull/18057)) - Support ephemeral uv virtual environments ([#18335](https://github.com/astral-sh/ruff/pull/18335)) - Model that some `Callable` types should have all `FunctionType` attributes available ([#18242](https://github.com/astral-sh/ruff/pull/18242)) ### Diagnostics - Add diagnostic hints for a function that has a non-`None` return-type annotation but no return statements ([#18359](https://github.com/astral-sh/ruff/pull/18359)) - Add hint if async context manager is used in non-async with statement ([#18299](https://github.com/astral-sh/ruff/pull/18299)) - Improve diagnostics if the user attempts to import a stdlib module that does not exist on their configured Python version ([#18403](https://github.com/astral-sh/ruff/pull/18403)) - Tell the user why we inferred a certain Python version when reporting version-specific syntax errors ([#18295](https://github.com/astral-sh/ruff/pull/18295)) ### Bug fixes - Fix multithreading related hangs and panics ([#18238](https://github.com/astral-sh/ruff/pull/18238)) - Ensure `Literal` types are considered assignable to anything their `Instance` supertypes are assignable to ([#18351](https://github.com/astral-sh/ruff/pull/18351)) - Callable types are disjoint from non-callable `@final` nominal instance types ([#18368](https://github.com/astral-sh/ruff/pull/18368)) - Support callability of bound/constrained typevars ([#18389](https://github.com/astral-sh/ruff/pull/18389)) ### Server - Fix server hang after shutdown request ([#18414](https://github.com/astral-sh/ruff/pull/18414)) - Improve completions by leveraging scopes ([#18281](https://github.com/astral-sh/ruff/pull/18281)) - Support cancellation and retry in the server ([#18273](https://github.com/astral-sh/ruff/pull/18273)) - Support publishing diagnostics in the server ([#18309](https://github.com/astral-sh/ruff/pull/18309)) ### CLI - Add `--config-file` CLI arg ([#18083](https://github.com/astral-sh/ruff/pull/18083)) ### Contributors - [@AlexWaygood](https://github.com/AlexWaygood) - [@BurntSushi](https://github.com/BurntSushi) - [@lipefree](https://github.com/lipefree) - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@dhruvmanila](https://github.com/dhruvmanila) - [@zanieb](https://github.com/zanieb) - [@carljm](https://github.com/carljm) - [@thejchap](https://github.com/thejchap) - [@sharkdp](https://github.com/sharkdp) - [@InSyncWithFoo](https://github.com/InSyncWithFoo) - [@MichaReiser](https://github.com/MichaReiser) ## 0.0.1-alpha.7 ### Bug fixes - Implement Python's floor-division semantics for `Literal` `int`s ([#18249](https://github.com/astral-sh/ruff/pull/18249)) - Don't warn about a `yield` expression not being in a function if the `yield` expression is in a function ([#18008](https://github.com/astral-sh/ruff/pull/18008)) - Fix inference of attribute writes to unions/intersections that including module-literal types ([#18313](https://github.com/astral-sh/ruff/pull/18313)) - Fix false-positive diagnostics in binary comparison inference logic for intersection types ([#18266](https://github.com/astral-sh/ruff/pull/18266)) - Fix instance vs callable subtyping/assignability ([#18260](https://github.com/astral-sh/ruff/pull/18260)) - Ignore `ClassVar` declarations when resolving instance members ([#18241](https://github.com/astral-sh/ruff/pull/18241)) - Fix crash when hovering over a `ty_extensions.Intersection[A, B]` expression in an IDE context ([#18321](https://github.com/astral-sh/ruff/pull/18321)) - Respect `MRO_NO_OBJECT_FALLBACK` policy when looking up symbols on `type` instances ([#18312](https://github.com/astral-sh/ruff/pull/18312)) - `get_protocol_members` returns a frozenset, not a tuple ([#18284](https://github.com/astral-sh/ruff/pull/18284)) ### Typing semantics and features - Support `import ` and `from import module` ([#18137](https://github.com/astral-sh/ruff/pull/18137)) - Support frozen dataclasses ([#17974](https://github.com/astral-sh/ruff/pull/17974)) - Understand that the presence of a `__getattribute__` method indicates arbitrary members can exist on a type ([#18280](https://github.com/astral-sh/ruff/pull/18280)) - Add a subdiagnostic if `invalid-return-type` is emitted on a method with an empty body on a non-protocol subclass of a protocol class ([#18243](https://github.com/astral-sh/ruff/pull/18243)) - Improve `invalid-type-form` diagnostic where a module-literal type is used in a type expression and the module has a member which would be valid in a type expression ([#18244](https://github.com/astral-sh/ruff/pull/18244)) - Split `invalid-base` error code into two error codes ([#18245](https://github.com/astral-sh/ruff/pull/18245)) - Rename `call-possibly-unbound-method` to `possibly-unbound-implicit-call` ([#18017](https://github.com/astral-sh/ruff/pull/18017)) ### Configuration - Add `tests` to `src.root` by default if a `tests/` directory exists and is not a package ([#18286](https://github.com/astral-sh/ruff/pull/18286)) - Tell the user why we inferred the Python version we inferred ([#18082](https://github.com/astral-sh/ruff/pull/18082)) - Add support for detecting activated Conda and Pixi environments ([#18267](https://github.com/astral-sh/ruff/pull/18267)) - Move `respect-ignore-files` configuration setting under `src` section ([#18322](https://github.com/astral-sh/ruff/pull/18322)) ### Server - Fix server panic when calling `system_mut` ([#18252](https://github.com/astral-sh/ruff/pull/18252)) - Abort process if worker thread panics ([#18211](https://github.com/astral-sh/ruff/pull/18211)) - Gracefully handle salsa cancellations and panics in background request handlers ([#18254](https://github.com/astral-sh/ruff/pull/18254)) ### Contributors - [@felixscherz](https://github.com/felixscherz) - [@carljm](https://github.com/carljm) - [@j178](https://github.com/j178) - [@thejchap](https://github.com/thejchap) - [@brainwane](https://github.com/brainwane) - [@AlexWaygood](https://github.com/AlexWaygood) - [@lipefree](https://github.com/lipefree) - [@InSyncWithFoo](https://github.com/InSyncWithFoo) - [@brandtbucher](https://github.com/brandtbucher) - [@MichaReiser](https://github.com/MichaReiser) - [@maxmynter](https://github.com/maxmynter) - [@fabridamicelli](https://github.com/fabridamicelli) - [@sharkdp](https://github.com/sharkdp) ## 0.0.1-alpha.6 ### Server - Add rule link to server diagnostics ([#18128](https://github.com/astral-sh/ruff/pull/18128)) - Avoid panicking when there are multiple workspaces ([#18151](https://github.com/astral-sh/ruff/pull/18151)) - Show related information in diagnostic ([#17359](https://github.com/astral-sh/ruff/pull/17359)) ### Configuration - Default `src.root` setting to `['.', '']` if an `src/` directory does not exist but a `/` directory does exist ([#18141](https://github.com/astral-sh/ruff/pull/18141)) ### Typing semantics and features - Consider a class with a dynamic element in its MRO assignable to any subtype of `type` ([#18205](https://github.com/astral-sh/ruff/pull/18205)) - Ensure that a function-literal type is always considered equivalent to itself ([#18227](https://github.com/astral-sh/ruff/pull/18227)) - Promote literals when inferring class specializations from constructors ([#18102](https://github.com/astral-sh/ruff/pull/18102)) - Support `typing.TypeAliasType` ([#18156](https://github.com/astral-sh/ruff/pull/18156)) - Infer function-call type variables in both directions ([#18155](https://github.com/astral-sh/ruff/pull/18155)) ### Improvements to modeling of runtime semantics - Integer indexing into `bytes` returns `int` ([#18218](https://github.com/astral-sh/ruff/pull/18218)) - Emit `invalid-exception-caught` diagnostics even when the caught exception is not bound to a variable ([#18202](https://github.com/astral-sh/ruff/pull/18202)) ### Usability improvements - Add hint to some diagnostics that [PEP 604](https://peps.python.org/pep-0604/) union syntax is only available on Python 3.10+ ([#18192](https://github.com/astral-sh/ruff/pull/18192)) - Add note to `unresolved-import` diagnostic hinting to users to configure their Python environment ([#18207](https://github.com/astral-sh/ruff/pull/18207)) - Make `division-by-zero` an opt-in diagnostic rather than opt-out ([#18220](https://github.com/astral-sh/ruff/pull/18220)) ### Import resolution improvements - Add support for PyPy virtual environments ([#18203](https://github.com/astral-sh/ruff/pull/18203)) ### Contributors - [@dhruvmanila](https://github.com/dhruvmanila) - [@InSyncWithFoo](https://github.com/InSyncWithFoo) - [@AlexWaygood](https://github.com/AlexWaygood) - [@MichaReiser](https://github.com/MichaReiser) - [@BradonZhang](https://github.com/BradonZhang) - [@dcreager](https://github.com/dcreager) - [@danielhollas](https://github.com/danielhollas) - [@esadek](https://github.com/esadek) - [@kiran-4444](https://github.com/kiran-4444) - [@Mathemmagician](https://github.com/Mathemmagician) - [@sharkdp](https://github.com/sharkdp) - [@felixscherz](https://github.com/felixscherz) - [@adamaaronson](https://github.com/adamaaronson) - [@carljm](https://github.com/carljm) ## 0.0.1-alpha.5 ### Bug fixes - Fix assignability checks for invariant generics parameterized by gradual types ([#18138](https://github.com/astral-sh/ruff/pull/18138)) - Revert boolean expression control flow change which caused a performance regression ([#18150](https://github.com/astral-sh/ruff/pull/18150)) - Remove pyvenv.cfg validation check for lines with multiple `=` ([#18144](https://github.com/astral-sh/ruff/pull/18144)) ### Contributors - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@AlexWaygood](https://github.com/AlexWaygood) ## 0.0.1-alpha.4 ### Enhancements - Allow unions including `Any`/`Unknown` as bases ([#18094](https://github.com/astral-sh/ruff/pull/18094)) - Better control flow for boolean expressions that are inside if ([#18010](https://github.com/astral-sh/ruff/pull/18010)) - Improve invalid method calls for unmatched overloads ([#18122](https://github.com/astral-sh/ruff/pull/18122)) - Add support for `NamedTuple` 'fallback' attributes ([#18127](https://github.com/astral-sh/ruff/pull/18127)) - `type[…]` is always assignable to `type` ([#18121](https://github.com/astral-sh/ruff/pull/18121)) - Support accessing `__builtins__` global ([#18118](https://github.com/astral-sh/ruff/pull/18118)) ### Bug fixes - Fix relative imports in stub packages ([#18132](https://github.com/astral-sh/ruff/pull/18132)) ### Contributors - [@MatthewMckee4](https://github.com/MatthewMckee4) - [@felixscherz](https://github.com/felixscherz) - [@BurntSushi](https://github.com/BurntSushi) - [@maxmynter](https://github.com/maxmynter) - [@sharkdp](https://github.com/sharkdp) - [@TomerBin](https://github.com/TomerBin) - [@MichaReiser](https://github.com/MichaReiser) ## 0.0.1-alpha.3 ### Enhancements - Include synthesized arguments in displayed counts for `too-many-positional-arguments` ([#18098](https://github.com/astral-sh/ruff/pull/18098)) ### Bug fixes - Fix `redundant-cast` false positives when casting to `Unknown` ([#18111](https://github.com/astral-sh/ruff/pull/18111)) - Fix normalization of unions containing instances parameterized with unions ([#18112](https://github.com/astral-sh/ruff/pull/18112)) - Make dataclass instances adhere to DataclassInstance ([#18115](https://github.com/astral-sh/ruff/pull/18115)) ### CLI - Change layout of extra verbose output and respect `--color` for verbose output ([#18089](https://github.com/astral-sh/ruff/pull/18089)) ### Documentation - Use Cargo-style versions in the changelog ([#397](https://github.com/astral-sh/ty/pull/397)) ### Contributors - [@zanieb](https://github.com/zanieb) - [@sharkdp](https://github.com/sharkdp) - [@AlexWaygood](https://github.com/AlexWaygood) - [@InSyncWithFoo](https://github.com/InSyncWithFoo) - [@MichaReiser](https://github.com/MichaReiser) ## 0.0.1-alpha.2 ### Enhancements - Improve diagnostics for failure to call overloaded function ([#18073](https://github.com/astral-sh/ruff/pull/18073)) - Fix inconsistent casing in `invalid-return-type` diagnostic ([#18084](https://github.com/astral-sh/ruff/pull/18084)) - Add type-expression syntax link to `invalid-type-expression` diagnostics ([#18104](https://github.com/astral-sh/ruff/pull/18104)) ### Bug fixes - Add cycle handling for unpacking targets ([#18078](https://github.com/astral-sh/ruff/pull/18078)) - Do not look up `__init__` on instances ([#18092](https://github.com/astral-sh/ruff/pull/18092)) ### Typing - Infer parameter specializations of explicitly implemented generic protocols ([#18054](https://github.com/astral-sh/ruff/pull/18054)) - Check assignments to implicit global symbols are assignable to the types declared on `types.ModuleType` ([#18077](https://github.com/astral-sh/ruff/pull/18077)) - Fix various generics-related TODOs ([#18062](https://github.com/astral-sh/ruff/pull/18062)) ### Documentation - Fix rule link in the configuration description ([#381](https://github.com/astral-sh/ty/pull/381)) - Use `https://ty.dev/rules` when linking to the rules table ([#18072](https://github.com/astral-sh/ruff/pull/18072)) - Use `ty server` instead of `ty lsp` ([#360](https://github.com/astral-sh/ty/pull/360)) - Fix missing `>` in HTML anchor tags in CLI reference ([#18096](https://github.com/astral-sh/ruff/pull/18096)) - Fix link to rules docs ([#378](https://github.com/astral-sh/ty/pull/378)) - Fix repository in README transform script ([#361](https://github.com/astral-sh/ty/pull/361)) ### Contributors - [@dhruvmanila](https://github.com/dhruvmanila) - [@Usul-Dev](https://github.com/Usul-Dev) - [@dcreager](https://github.com/dcreager) - [@AlexWaygood](https://github.com/AlexWaygood) - [@BurntSushi](https://github.com/BurntSushi) - [@MichaReiser](https://github.com/MichaReiser) - [@frgfm](https://github.com/frgfm) - [@kiran-4444](https://github.com/kiran-4444) - [@sharkdp](https://github.com/sharkdp) - [@eruditmorina](https://github.com/eruditmorina) ## 0.0.1-alpha.1 ### Enhancements - Add basic support for non-virtual Python environments ([#17991](https://github.com/astral-sh/ruff/pull/17991)) - Do not allow invalid virtual environments from discovered `.venv` or `VIRTUAL_ENV` ([#18003](https://github.com/astral-sh/ruff/pull/18003)) - Refine message for why a rule is enabled ([#18038](https://github.com/astral-sh/ruff/pull/18038)) - Update `--python` to accept paths to executables in environments ([#17954](https://github.com/astral-sh/ruff/pull/17954)) - Improve diagnostics for `assert_type` and `assert_never` ([#18050](https://github.com/astral-sh/ruff/pull/18050)) - Add a note to the diagnostic if a new builtin is used on an old Python version ([#18068](https://github.com/astral-sh/ruff/pull/18068)) ### Bug fixes - Fix infinite recursion bug in `is_disjoint_from` ([#18043](https://github.com/astral-sh/ruff/pull/18043)) - Recognize submodules in self-referential imports ([#18005](https://github.com/astral-sh/ruff/pull/18005)) ### Typing - Allow a class to inherit from an intersection if the intersection contains a dynamic type and the intersection is not disjoint from `type` ([#18055](https://github.com/astral-sh/ruff/pull/18055)) - Allow classes to inherit from `type[Any]` or `type[Unknown]` ([#18060](https://github.com/astral-sh/ruff/pull/18060)) - Apply function specialization to all overloads ([#18020](https://github.com/astral-sh/ruff/pull/18020)) - Implement `DataClassInstance` protocol for dataclasses ([#18018](https://github.com/astral-sh/ruff/pull/18018)) - Induct into instances and subclasses when finding and applying generics ([#18052](https://github.com/astral-sh/ruff/pull/18052)) - Infer parameter specializations of generic aliases ([#18021](https://github.com/astral-sh/ruff/pull/18021)) - Narrowing for `hasattr()` ([#18053](https://github.com/astral-sh/ruff/pull/18053)) - Silence false positives for PEP-695 ParamSpec annotations ([#18001](https://github.com/astral-sh/ruff/pull/18001)) - Understand homogeneous tuple annotations ([#17998](https://github.com/astral-sh/ruff/pull/17998)) - `__file__` is always a string inside a Python module ([#18071](https://github.com/astral-sh/ruff/pull/18071)) ### CLI - Avoid initializing progress bars early ([#18049](https://github.com/astral-sh/ruff/pull/18049)) ### Contributors - [@soof-golan](https://github.com/soof-golan) - [@ibraheemdev](https://github.com/ibraheemdev) - [@dhruvmanila](https://github.com/dhruvmanila) - [@charliermarsh](https://github.com/charliermarsh) - [@MichaReiser](https://github.com/MichaReiser) - [@carljm](https://github.com/carljm) - [@abhijeetbodas2001](https://github.com/abhijeetbodas2001) - [@zanieb](https://github.com/zanieb) - [@AlexWaygood](https://github.com/AlexWaygood) - [@dcreager](https://github.com/dcreager) - [@mtshiba](https://github.com/mtshiba) - [@sharkdp](https://github.com/sharkdp) ## 0.0.0-alpha.8 ### Changes - Add `--config` CLI arg ([#17697](https://github.com/astral-sh/ruff/pull/17697)) - Add CLI documentation and update README ([#284](https://github.com/astral-sh/ty/pull/284)) - Add a warning about pre-release status to the CLI ([#17983](https://github.com/astral-sh/ruff/pull/17983)) - Add missing bitwise-operator branches for boolean and integer arithmetic ([#17949](https://github.com/astral-sh/ruff/pull/17949)) - Add progress bar for `ty check` ([#17965](https://github.com/astral-sh/ruff/pull/17965)) - Add CLI reference ([#17978](https://github.com/astral-sh/ruff/pull/17978)) - Change default severity for `unbound-reference` to `error` ([#17936](https://github.com/astral-sh/ruff/pull/17936)) - Change range of `revealed-type` diagnostic to be the range of the argument passed in, not the whole call ([#17980](https://github.com/astral-sh/ruff/pull/17980)) - Default to latest supported Python version ([#17938](https://github.com/astral-sh/ruff/pull/17938)) - Display "All checks passed!" message in green ([#17982](https://github.com/astral-sh/ruff/pull/17982)) - Document configuration schema ([#17950](https://github.com/astral-sh/ruff/pull/17950)) - Generate and add rules table ([#17953](https://github.com/astral-sh/ruff/pull/17953)) - Handle type variables that have other type variables as a default ([#17956](https://github.com/astral-sh/ruff/pull/17956)) - Ignore `possibly-unresolved-reference` by default ([#17934](https://github.com/astral-sh/ruff/pull/17934)) - Implement `global` handling and `load-before-global-declaration` syntax error ([#17637](https://github.com/astral-sh/ruff/pull/17637)) - Make `unused-ignore-comment` disabled by default for now ([#17955](https://github.com/astral-sh/ruff/pull/17955)) - Recognise functions containing `yield from` expressions as being generator functions ([#17930](https://github.com/astral-sh/ruff/pull/17930)) - Fix stack overflow on recursive protocols ([#17929](https://github.com/astral-sh/ruff/pull/17929)) - Report duplicate `Protocol` or `Generic` base classes with `[duplicate-base]`, not `[inconsistent-mro]` ([#17971](https://github.com/astral-sh/ruff/pull/17971)) - Respect the gradual guarantee when reporting errors in resolving MROs ([#17962](https://github.com/astral-sh/ruff/pull/17962)) - Support `typing.Self` in methods ([#17689](https://github.com/astral-sh/ruff/pull/17689)) - Support extending `__all__` from an imported module even when the module is not an `ExprName` node ([#17947](https://github.com/astral-sh/ruff/pull/17947)) - Support extending `__all__` with a literal tuple or set as well as a literal list ([#17948](https://github.com/astral-sh/ruff/pull/17948)) - Understand classes that inherit from subscripted `Protocol[]` as generic ([#17832](https://github.com/astral-sh/ruff/pull/17832)) - Update ty metadata ([#17943](https://github.com/astral-sh/ruff/pull/17943)) - Add `py.typed` ([#276](https://github.com/astral-sh/ty/pull/276)) - Bottom-up improvement of diagnostic messages for union type function calls ([#17984](https://github.com/astral-sh/ruff/pull/17984)) - Fix more ecosystem/fuzzer panics with fixpoint ([#17758](https://github.com/astral-sh/ruff/pull/17758)) - Remove `lint:` prefix from top-level diagnostic preamble ([#17987](https://github.com/astral-sh/ruff/pull/17987)) ### Contributors - [@Glyphack](https://github.com/Glyphack) - [@BurntSushi](https://github.com/BurntSushi) - [@paul-nameless](https://github.com/paul-nameless) - [@MichaReiser](https://github.com/MichaReiser) - [@ntbre](https://github.com/ntBre) - [@ibraheemdev](https://github.com/ibraheemdev) - [@sharkdp](https://github.com/sharkdp) - [@thejchap](https://github.com/thejchap) - [@carljm](https://github.com/carljm) - [@jorenham](https://github.com/jorenham) - [@AlexWaygood](https://github.com/AlexWaygood) - [@dcreager](https://github.com/dcreager) ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing > [!IMPORTANT] > If you want to contribute changes to ty's core, please check out the > [dedicated `ty` contributing guide](https://github.com/astral-sh/ruff/blob/main/crates/ty/CONTRIBUTING.md). > Keep reading if you want to contribute to ty's documentation or release process instead. ## Repository structure This repository contains ty's documentation and release infrastructure. The core of ty's Rust codebase is located in the [Ruff](https://github.com/astral-sh/ruff) repository. While the relationship between these two projects will evolve over time, they currently share foundational crates and it's easiest to use a single repository for the Rust development. ty's command-line help text, and part of `docs/reference/`, are auto-generated from Rust code in the Ruff repository, using generation scripts that live in [`crates/ruff_dev/src/`](https://github.com/astral-sh/ruff/blob/main/crates/ruff_dev/src/): - [Configuration options](https://docs.astral.sh/ty/reference/configuration/), from [ruff/crates/ty_project/src/metadata/options.rs](https://github.com/astral-sh/ruff/blob/main/crates/ty_project/src/metadata/options.rs) - [Rules](https://docs.astral.sh/ty/reference/rules/), from [ruff/crates/ty_python_semantic/src/](https://github.com/astral-sh/ruff/blob/main/crates/ty_python_semantic/src/) - [Command-line interface reference](https://docs.astral.sh/ty/reference/cli/), from [ruff/crates/ty/src/args.rs](https://github.com/astral-sh/ruff/blob/main/crates/ty/src/args.rs) The Ruff repository is included as a submodule inside this repository to allow ty's release tags to reflect an exact snapshot of the Ruff project. The submodule is only updated on release. To see the latest development code, check out the `main` branch of the Ruff repository. ## Getting started with the ty repository Clone the repository: ```shell git clone https://github.com/astral-sh/ty.git ``` Then, ensure the submodule is initialized: ```shell git submodule update --init --recursive ``` ### Prerequisites You'll need [uv](https://docs.astral.sh/uv/getting-started/installation/) (or `pipx` and `pip`) to run Python utility commands. You can optionally install prek hooks to automatically run the validation checks when making a commit: ```shell uv tool install prek prek install ``` ## Building the Python package The Python package can be built with any Python build frontend (Maturin is used as a backend), e.g.: ```shell uv build ``` ## Updating the Ruff commit To update the Ruff submodule to the latest commit: ```shell git -C ruff pull origin main ``` Or, to update the Ruff submodule to a specific commit: ```shell git -C ruff checkout ``` To commit the changes: ```shell commit=$(git -C ruff rev-parse --short HEAD) git switch -c "sync/ruff-${commit}" git add ruff git commit -m "Update ruff submodule to https://github.com/astral-sh/ruff/commit/${commit}" ``` To restore the Ruff submodule to a clean-state, reset, then update the submodule: ```shell git -C ruff reset --hard git submodule update ``` To restore the Ruff submodule to the commit from `main`: ```shell git -C ruff reset --hard $(git ls-tree main -- ruff | awk '{print $3}') git add ruff ``` ## Documentation To preview any changes to the documentation locally run the development server with: ```shell uvx --with-requirements docs/requirements.txt -- mkdocs serve -f mkdocs.yml ``` The documentation should then be available locally at [http://127.0.0.1:8000/ty/](http://127.0.0.1:8000/ty/). To update the documentation dependencies, edit `docs/requirements.in`, then run: ```shell uv pip compile docs/requirements.in -o docs/requirements.txt --universal -p 3.12 ``` Documentation is deployed automatically on release by publishing to the [Astral documentation](https://github.com/astral-sh/docs) repository, which itself deploys via Cloudflare Pages. After making changes to the documentation, format the markdown files with: ```shell npx prettier --prose-wrap always --write "**/*.md" ``` ## Releasing ty Releases can only be performed by Astral team members. Preparation for the release is automated. 1. Install the prek hooks as described above, if you haven't already. 1. Checkout the `main` branch and run `git pull origin main --recurse-submodules --tags`. 1. Create and checkout a new branch for the release. 1. Run `./scripts/release.sh`. The release script will: - Update the Ruff submodule to the latest commit on `main` upstream - Generate changelog entries based on pull requests here, and in Ruff - Bump the versions in the `pyproject.toml` and `dist-workspace.toml` - Update the generated reference documentation in `docs/reference` 1. Editorialize the `CHANGELOG.md` file to ensure entries are consistently styled. This usually involves simple edits, like consistent capitalization and leading verbs like "Add ...". 1. Create a pull request with the changelog and version changes The pull requests are usually titled as: `Bump version to `. Binary builds will automatically be tested for the release. 1. Merge the pull request. 1. Run the [release workflow](https://github.com/astral-sh/ty/actions/workflows/release.yml) with the version tag. **Do not include a leading `v`**. When running the release workflow for pre-release versions, use the Cargo version format (not PEP 440), e.g. `0.0.1-alpha.5` (not `0.0.1a5`). For stable releases, these formats are identical. The release will automatically be created on GitHub after the distributions are published. 1. Run `uv run --no-project ./scripts/update_schemastore.py` This script will prepare a branch to update the `ty.json` schema in the `schemastore` repository. Follow the link in the script's output to submit the pull request. The script is a no-op if there are no schema changes. 1. Update and release `ty-vscode`. The instructions are [in the `ty-vscode` repository](https://github.com/astral-sh/ty-vscode/blob/main/CONTRIBUTING.md#release). ## Updating ty's conformance results upstream One way in which type checkers can be evaluated is by how well they do on the [typing conformance test suite](https://github.com/python/typing/tree/main/conformance), which contains a number of assertions to test whether type checkers adhere to the rules laid out in the typing spec. ty's results are uploaded to the upstream `python/typing` repo so that users can compare ty's conformance score with other type checkers. Updating these results is partially, but not fully, automated. To update ty's conformance results upstream after a release: 1. Clone and checkout a new branch 1. Run `cd conformance` 1. Run `uv sync --update-package=ty` 1. Run `uv run python src/main.py`. This step may update generated fields in TOML files (see below) and/or the `results.html` file. 1. Check to see if any manual changes are required and apply them as necessary (see below for details). 1. If you had to make any manual changes as part of the previous step, run `uv run python src/main.py` again. This second run should not update any further TOML files, but is necessary for regenerating `results.html`. 1. Make a PR to the upstream repo. ### Manual changes that may be required to `.toml` files The TOML files that contain the results for each type checker are partially generated. The `conformance_automated`, `output` and `errors_diff` fields are all generated; these should never be altered: - `output` is the raw output of the type checker on that file. - `errors_diff` provides one line of output for every Python line where a diagnostic was expected from ty but didn't occur, and one line of output for every Python line where a diagnostic was not expected from ty but did occur. - `conformance_automated`: this will always either be "Pass" (if `errors_diff` is an empty string) or "Fail" (if it is a non-empty string). The other fields are manually entered and may need to be updated if any of the generated fields in a TOML file are altered by running `uv run python src/main.py`: - `conformant`: This should be one of three values: - "Unsupported": none of the major features in the Python file are supported by ty - "Partial": some, but not all, of the features and assertions in the Python file are supported by ty. If this is given as the current status, a non-empty `notes` field should be provided that describes which features are currently supported and which are not. - "Pass": this should generally only be used if ty passes all assertions in the Python file and the generated `errors_diff` field is an empty string. In some rare occasions, it *may* be appropriate to label ty as conformant even if there is a non-empty `errors_diff` field, but you will generally need to provide a non-empty `ignore_errors` field if so. - `notes`: if the score is given as "Partial" in the `conformant` field, this field should describe which assertions fail and which features are currently unsupported. - `ignore_errors`: this is only required if the generated `errors_diff` field is nonempty but the score is nonetheless given as "Conformant". It should be a list of strings. Each string should be an error message that appears in the `errors_diff` field but should nonetheless be ignored when considering whether ty is conformant or not. If ty's score is given as "Conformant" with a non-empty `errors_diff` field, a verification check in CI in the `python/typing` repository will fail unless each error message listed in `errors_diff` is listed in that TOML file's `ignore_errors` field. ================================================ FILE: Dockerfile ================================================ FROM --platform=$BUILDPLATFORM ubuntu AS build ENV HOME="/root" WORKDIR $HOME RUN apt update && apt install -y build-essential curl python3-venv # Setup zig as cross compiling linker RUN python3 -m venv $HOME/.venv RUN .venv/bin/pip install cargo-zigbuild ENV PATH="$HOME/.venv/bin:$PATH" # Change to the ruff directory, which is the root for our build WORKDIR $HOME/ruff # Install rust ARG TARGETPLATFORM RUN case "$TARGETPLATFORM" in \ "linux/arm64") echo "aarch64-unknown-linux-musl" > rust_target.txt ;; \ "linux/amd64") echo "x86_64-unknown-linux-musl" > rust_target.txt ;; \ *) exit 1 ;; \ esac # Update rustup whenever we bump the rust version COPY ruff/rust-toolchain.toml rust-toolchain.toml RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --target $(cat rust_target.txt) --profile minimal --default-toolchain none ENV PATH="$HOME/.cargo/bin:$PATH" # Installs the correct toolchain version from rust-toolchain.toml and then the musl target RUN rustup target add $(cat rust_target.txt) # Build COPY ruff/crates crates COPY ruff/Cargo.toml Cargo.toml COPY ruff/Cargo.lock Cargo.lock COPY dist-workspace.toml ../dist-workspace.toml RUN cargo zigbuild --bin ty --target $(cat rust_target.txt) --release RUN cp target/$(cat rust_target.txt)/release/ty /ty # TODO: Optimize binary size, with a version that also works when cross compiling # RUN strip --strip-all /ty FROM scratch COPY --from=build /ty /ty WORKDIR /io ENTRYPOINT ["/ty"] ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2025 Astral Software Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # ty [![ty](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json)](https://github.com/astral-sh/ty) [![PyPI](https://img.shields.io/pypi/v/ty.svg)](https://pypi.python.org/pypi/ty) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?logo=discord&logoColor=white)](https://discord.com/invite/astral-sh) An extremely fast Python type checker and language server, written in Rust.

Shows a bar chart with benchmark results.

Type checking the home-assistant project without caching.


ty is backed by [Astral](https://astral.sh), the creators of [uv](https://github.com/astral-sh/uv) and [Ruff](https://github.com/astral-sh/ruff). ty is currently in [beta](#version-policy). ## Highlights - 10x - 100x faster than mypy and Pyright - Comprehensive [diagnostics](https://docs.astral.sh/ty/features/diagnostics/) with rich contextual information - Configurable [rule levels](https://docs.astral.sh/ty/rules/), [per-file overrides](https://docs.astral.sh/ty/reference/configuration/#overrides), [suppression comments](https://docs.astral.sh/ty/suppression/), and first-class project support - Designed for adoption, with support for [redeclarations](https://docs.astral.sh/ty/features/type-system/#redeclarations) and [partially typed code](https://docs.astral.sh/ty/features/type-system/#gradual-guarantee) - [Language server](https://docs.astral.sh/ty/features/language-server/) with code navigation, completions, code actions, auto-import, inlay hints, on-hover help, etc. - Fine-grained [incremental analysis](https://docs.astral.sh/ty/features/language-server/#fine-grained-incrementality) designed for fast updates when editing files in an IDE - Editor integrations for [VS Code](https://docs.astral.sh/ty/editors/#vs-code), [PyCharm](https://docs.astral.sh/ty/editors/#pycharm), [Neovim](https://docs.astral.sh/ty/editors/#neovim) and more - Advanced typing features like first-class [intersection types](https://docs.astral.sh/ty/features/type-system/#intersection-types), advanced [type narrowing](https://docs.astral.sh/ty/features/type-system/#top-and-bottom-materializations), and [sophisticated reachability analysis](https://docs.astral.sh/ty/features/type-system/#reachability-based-on-types) ## Getting started Run ty with [uvx](https://docs.astral.sh/uv/guides/tools/#running-tools) to get started quickly: ```shell uvx ty check ``` Or, check out the [ty playground](https://play.ty.dev) to try it out in your browser. To learn more about using ty, see the [documentation](https://docs.astral.sh/ty/). ## Installation To install ty, see the [installation](https://docs.astral.sh/ty/installation/) documentation. To add the ty language server to your editor, see the [editor integration](https://docs.astral.sh/ty/editors/) guide. ## Getting help If you have questions or want to report a bug, please open an [issue](https://github.com/astral-sh/ty/issues) in this repository. You may also join our [Discord server](https://discord.com/invite/astral-sh). ## Contributing Development of this project takes place in the [Ruff](https://github.com/astral-sh/ruff) repository at this time. Please [open pull requests](https://github.com/astral-sh/ruff/pulls) there for changes to anything in the `ruff` submodule (which includes all of the Rust source code). See the [contributing guide](./CONTRIBUTING.md) for more details. ## Version policy ty uses `0.0.x` versioning. ty does not yet have a stable API; breaking changes, including changes to diagnostics, may occur between any two versions. See the [type system support](https://github.com/astral-sh/ty/issues/1889) tracking issue for a detailed overview of currently supported features. ## FAQ #### Why is ty doing \_\_\_\_\_? See our [typing FAQ](https://docs.astral.sh/ty/reference/typing-faq). #### How do you pronounce ty? It's pronounced as "tee - why" ([`/tiː waɪ/`](https://en.wikipedia.org/wiki/Help:IPA/English#Key)) #### How should I stylize ty? Just "ty", please. ## License ty is licensed under the MIT license ([LICENSE](LICENSE) or ). Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in ty by you, as defined in the MIT license, shall be licensed as above, without any additional terms or conditions. ================================================ FILE: SECURITY.md ================================================ # Security policy ## Reporting a vulnerability If you have found a possible vulnerability, please email `security at astral dot sh`. ## Bug bounties While we sincerely appreciate and encourage reports of suspected security problems, please note that Astral does not currently run any bug bounty programs. ## Vulnerability disclosures Critical vulnerabilities will be disclosed via GitHub's [security advisory](https://github.com/astral-sh/ty/security) system. ================================================ FILE: _typos.toml ================================================ [files] extend-exclude = [] [default.extend-words] [default] extend-ignore-re = [ # Line ignore with trailing "spellchecker:disable-line" "(?Rm)^.*#\\s*spellchecker:disable-line$", "LICENSEs", "ntBre" ] ================================================ FILE: assets/badge/v0.json ================================================ { "label": "", "message": "ty", "logoSvg": "", "logoWidth": 10, "labelColor": "grey", "color": "#261230" } ================================================ FILE: dist-workspace.toml ================================================ [workspace] members = ["cargo:./ruff"] packages = ["ty"] version = "0.0.24" # Config for 'dist' [dist] # The preferred dist version to use in CI (Cargo.toml SemVer syntax) cargo-dist-version = "0.31.0" # Whether to consider the binaries in a package for distribution (defaults true) dist = false # CI backends to support ci = "github" # The installers to generate for each app installers = ["shell", "powershell"] # The archive format to use for windows builds (defaults .zip) windows-archive = ".zip" # The archive format to use for non-windows builds (defaults .tar.xz) unix-archive = ".tar.gz" # Target platforms to build apps for (Rust target-triple syntax) targets = [ "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", "aarch64-pc-windows-msvc", "arm-unknown-linux-musleabihf", "armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-musleabihf", "x86_64-apple-darwin", "powerpc64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu", "s390x-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc", "i686-unknown-linux-gnu", "i686-unknown-linux-musl", "i686-pc-windows-msvc", ] # Whether to auto-include files like READMEs, LICENSEs, and CHANGELOGs (default true) auto-includes = false # Whether dist should create a Github Release or use an existing draft create-release = true # Which actions to run on pull requests pr-run-mode = "plan" # Whether to publish prereleases to package managers publish-prereleases = true # Whether CI should trigger releases with dispatches instead of tag pushes dispatch-releases = true # Which phase dist should use to create the GitHub release github-release = "announce" # Whether to enable GitHub Attestations github-attestations = true # When to generate GitHub Attestations github-attestations-phase = "announce" # Patterns to attest when creating attestations for release artifacts github-attestations-filters = ["*.json", "*.sh", "*.ps1", "*.zip", "*.tar.gz"] # Whether CI should include auto-generated code to build local artifacts build-local-artifacts = false # Local artifacts jobs to run in CI local-artifacts-jobs = ["./build-binaries", "./build-docker"] # Publish jobs to run in CI publish-jobs = ["./publish-pypi"] # Post-announce jobs to run in CI post-announce-jobs = ["./publish-docs", "./publish-mirror"] # Custom permissions for GitHub Jobs github-custom-job-permissions = { "build-docker" = { packages = "write", contents = "read", id-token = "write", attestations = "write" }, "publish-mirror" = { contents = "read" } } # Whether to install an updater program install-updater = false # Path that installers should place binaries in install-path = ["$XDG_BIN_HOME/", "$XDG_DATA_HOME/../bin", "~/.local/bin"] # Whether source tarballs should include submodules recursive-tarball = true # Prefer simple hosting for downloads, falling back to GitHub releases hosting = ["simple", "github"] simple-download-url = "https://releases.astral.sh/github/ty/releases/download/{tag}" [dist.github-custom-runners] global = "depot-ubuntu-latest-4" [dist.github-action-commits] "actions/checkout" = "0c366fd6a839edf440554fa01a7085ccba70ac98" # v6.0.2 "actions/upload-artifact" = "bbbca2ddaa5d8feaa63e36b76fdaad77386f024f" # v7.0.0 "actions/download-artifact" = "70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3" # v8.0.0 "actions/attest-build-provenance" = "00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8" # v3.1.0 ================================================ FILE: docs/.overrides/main.html ================================================ {% extends "base.html" %} {% block htmltitle %} {% if page.meta and page.meta.title %} {{ page.meta.title }} | {{ config.site_name }} {% elif page.title and not page.is_homepage %} {{ page.title | striptags }} | {{ config.site_name }} {% else %} {{ config.site_name }} {% endif %} {% endblock %} {% block extrahead %} {% endblock %} ================================================ FILE: docs/.overrides/partials/integrations/analytics/fathom.html ================================================ ================================================ FILE: docs/configuration.md ================================================ # Configuration ## Configuration files ty supports persistent configuration files at both the project- and user-level. Specifically, ty will search for a `pyproject.toml` or `ty.toml` file in the current directory, or in the nearest parent directory. If a `pyproject.toml` file is found, ty will read configuration from the `[tool.ty]` table. For example, to ignore the `index-out-of-bounds` rule, add the following to a `pyproject.toml`: ```toml title="pyproject.toml" [tool.ty.rules] index-out-of-bounds = "ignore" ``` !!! note If there is no `tool.ty` table, the `pyproject.toml` file will be ignored, and ty will continue searching in the directory hierarchy. ty will also search for `ty.toml` files, which follow an identical structure, but omit the `[tool.ty]` prefix. For example: ```toml title="ty.toml" [rules] index-out-of-bounds = "ignore" ``` !!! important `ty.toml` files take precedence over `pyproject.toml` files, so if both `ty.toml` and `pyproject.toml` files are present in a directory, configuration will be read from `ty.toml`, and the `[tool.ty]` section in the accompanying `pyproject.toml` will be ignored. ty will also discover user-level configuration at `~/.config/ty/ty.toml` (or `$XDG_CONFIG_HOME/ty/ty.toml`) on macOS and Linux, or `%APPDATA%\ty\ty.toml` on Windows. User-level configuration must use the `ty.toml` format, rather than the `pyproject.toml` format, as a `pyproject.toml` is intended to define a Python project. If project- and user-level configuration files are found, the settings will be merged, with project-level configuration taking precedence over the user-level configuration. For example, if a string, number, or boolean is present in both the project- and user-level configuration tables, the project-level value will be used, and the user-level value will be ignored. If an array is present in both tables, the arrays will be merged, with the project-level settings appearing later in the merged array. Settings provided via command line take precedence over persistent configuration. See the [configuration](./reference/configuration.md) reference for an enumeration of the available settings. ================================================ FILE: docs/editors.md ================================================ # Editor integration ty can be integrated with various editors to provide a seamless development experience. Learn more about ty's editor features in the [language server](./features/language-server.md) documentation. ## VS Code The Astral team maintains an official VS Code extension. Install the [ty extension](https://marketplace.visualstudio.com/items?itemName=astral-sh.ty) from the VS Code Marketplace. The extension automatically disables the language server from the [Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python) to avoid running two Python language servers. This is done by setting [`python.languageServer`](https://code.visualstudio.com/docs/python/settings-reference#_intellisense-engine-settings) to `"None"` as a default configuration. If you prefer to use ty only for type checking and want to use another language server for capabilities like hover, auto-completions, etc., you can override this by explicitly setting [`python.languageServer`](https://code.visualstudio.com/docs/python/settings-reference#_intellisense-engine-settings) and [`ty.disableLanguageServices`](./reference/editor-settings.md#disablelanguageservices) in your [`settings.json`](https://code.visualstudio.com/docs/configure/settings#_settings-json-file): ```jsonc { "python.languageServer": "Pylance", "ty.disableLanguageServices": true, } ``` ## Neovim The [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) extension is the recommended way of using ty with Neovim (if you prefer not to install the extension, you can copy [the ty configuration](https://github.com/neovim/nvim-lspconfig/blob/master/lsp/ty.lua) manually instead). After installing the nvim-lspconfig extension, you need to enable the language server (and can optionally configure additional settings). For Neovim >=0.11, you can add the following snippet to your config file: ```lua -- Optional: Only required if you need to update the language server settings vim.lsp.config('ty', { settings = { ty = { -- ty language server settings go here } } }) -- Required: Enable the language server vim.lsp.enable('ty') ``` For Neovim \<0.11, you would use the configuration below instead (note that [you might need to install an older version of nvim-lspconfig](https://github.com/neovim/nvim-lspconfig?tab=readme-ov-file#important-%EF%B8%8F)): ```lua require('lspconfig').ty.setup({ settings = { ty = { -- ty language server settings go here } } }) ``` ## Zed ty is included with Zed out of the box (no extension required), although the default primary LSP for Python is basedpyright. You can enable ty and disable basedpyright by adding this to your `settings.json` file: ```json { "languages": { "Python": { "language_servers": [ // Enable ty and ruff, // Other built-in servers (basedpyright, pyright, pylsp) // are disabled by being omitted from this list. "ty", "ruff" ] } } } ``` You can override the `ty` executable Zed uses by setting `lsp.ty.binary`: ```json { "lsp": { "ty": { "binary": { "path": "/home/user/.local/bin/ty", "arguments": ["server"] } } } } ``` More information in [Zed's documentation](https://zed.dev/docs/languages/python#configure-python-language-servers-in-zed). ## PyCharm Starting with version 2025.3, PyCharm users can enable native ty support in the settings: 1. Go to **Python | Tools | ty** in the Settings dialog. 1. Select the **Enable** checkbox. 1. In the Execution mode setting, select how PyCharm should search for the executable: **Interpreter** mode: PyCharm searches for an executable installed in your interpreter. To install the ty package for the selected interpreter, click _Install ty_. **Path** mode: PyCharm searches for an executable in `$PATH`. If the executable is not found, you can specify the path by clicking the Browse... icon. 1. Select which options should be enabled. For more information, refer to [PyCharm documentation](https://www.jetbrains.com/help/pycharm/lsp-tools.html#ty). ## Other editors ty can be used with any editor that supports the [language server protocol](https://microsoft.github.io/language-server-protocol/). To start the language server, use the `server` subcommand: ```shell ty server ``` Refer to your editor's documentation to learn how to connect to an LSP server. ## Settings See the [editor settings reference](./reference/editor-settings.md) for more details on configuring the language server. ================================================ FILE: docs/exclusions.md ================================================ # Excluding files ty automatically discovers all Python files in your project. You can customize where ty searches by using the [`src.include`](./reference/configuration.md#include) and [`src.exclude`](./reference/configuration.md#exclude) settings. For example, with the following configuration, ty checks all Python files in the `src` and `tests` directories except those in the `src/generated` directory: ```toml title="pyproject.toml" [tool.ty.src] include = ["src", "tests"] exclude = ["src/generated"] ``` ## Default exclusions By default, ty excludes a [variety of commonly ignored directories](./reference/configuration.md#exclude_1). If you want to include one of these directories, you can do so by adding a negative `exclude` using a leading `!`: ```toml title="pyproject.toml" [tool.ty.src] # Remove `build` from the excluded directories. exclude = ["!**/build/"] ``` By default, ty ignores files listed in an `.ignore` or `.gitignore` file. To disable this functionality, set [`respect-ignore-files`](./reference/configuration.md#respect-ignore-files) to `false`. ## Explicit targets You may explicitly pass the paths that ty should check, e.g.: ```shell ty check src scripts/benchmark.py ``` Paths that are passed as positional arguments to `ty check` are included even if they would otherwise be ignored through `exclude` filters or an ignore-file. ## Include and exclude syntax Both `include` and `exclude` support gitignore like glob patterns: - `src/` matches a directory (including its contents) named `src`. - `src` matches a file or directory (including its contents) named `src`. - `*` matches any (possibly empty) sequence of characters (except `/`). - `**` matches zero or more path components. This sequence **must** form a single path component, so both `./**a` and `./b**/` are invalid and will result in an error. A sequence of more than two consecutive `*` characters is also invalid. - `?` matches any single character except `/` - `[abc]` matches any character inside the brackets. Character sequences can also specify ranges of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid. All patterns are anchored: The pattern `src` only includes `/src` but not something like `/test/src`. To include any directory named `src`, use the prefix match `**/src`. The same applies for exclude patterns where `src` only excludes `/src` but not something like `/test/src`. !!! warning A prefix include pattern like `**/src` can notably slow down the Python file discovery. All fields accepting patterns use the reduced portable glob syntax from [PEP 639](https://peps.python.org/pep-0639/#add-license-FILES-key), with the addition that characters can be escaped with a backslash. ## Excluding files from virtual environments In Python 3.13+, the `venv` module will add a `.gitignore` file to the virtual environment root and ty will not emit diagnostics for the contained files. However, when using an older version of Python, ty may include diagnostics for files in the virtual environment. You can resolve this by adding a `.gitignore` to the environment, e.g., for a virtual environment named `.venv`: ```shell echo "*" > .venv/.gitignore ``` Or by adding your virtual environment to your `.gitignore` or `.ignore` file. ================================================ FILE: docs/features/diagnostics.md ================================================ # Diagnostics ty provides diagnostics that include snippets of your source code, annotations and helpful explanations. It will sometimes also provide suggestions on how to fix the reported issues. ## Example: Typed dictionaries In this first example, ty detected an invalid assignment to a `TypedDict` key. Notice how the diagnostic includes both the context around the line with the error, as well as the reference to the `age` item in the `TypedDict` definition: ![TypedDict invalid-assignment diagnostic](screenshots/diagnostics1.dark.png#only-dark) ![TypedDict invalid-assignment diagnostic](screenshots/diagnostics1.light.png#only-light) When a `TypedDict` key is misspelled, ty will suggest the correct spelling. If you are using an editor with language server support, you can also apply this suggestion as a quick fix: ![TypedDict misspelled-key diagnostic](screenshots/diagnostics2.dark.png#only-dark) ![TypedDict misspelled-key diagnostic](screenshots/diagnostics2.light.png#only-light) ## Example: Invalid arguments Here, a file has been opened for writing in text mode, but we are trying to write bytes to it. The diagnostic points out the type mismatch and also includes the corresponding parameter in the function definition for `write`: ![Invalid arguments diagnostic](screenshots/diagnostics3.dark.png#only-dark) ![Invalid arguments diagnostic](screenshots/diagnostics3.light.png#only-light) ## Example: Backwards compatibility Instead of just telling you that `tomllib` can not be found, ty will tell you *why* it is not available for your project. In this case, your project is targeting Python 3.10, but `tomllib` was only added in Python 3.11: ![Missing import diagnostic](screenshots/diagnostics4.dark.png#only-dark) ![Missing import diagnostic](screenshots/diagnostics4.light.png#only-light) ================================================ FILE: docs/features/language-server.md ================================================ # Language server You can generally expect ty to be a fully-featured [language server] for Python. This page describes some of the key features provided by ty's IDE integration and includes a reference table of supported LSP features at the end. See the [editor integration](../editors.md) guide for instructions on how to set up ty with your editor. ## Diagnostics
![](screenshots/inline-diagnostics.png)
Example of an inline diagnostic with code-span annotations
ty reports type errors and other [diagnostics](./diagnostics.md) directly in your editor. Diagnostics are updated as you type. You can use the [`diagnosticMode`](../reference/editor-settings.md#diagnosticmode) setting to control if you want to see diagnostics for open files only, or for your entire workspace. !!! info ty supports both the "pull" and "push" diagnostic models. Most modern editors will use the "pull" model for better performance, where diagnostics are fetched on demand rather than pushed after every change. ## Code navigation
![](screenshots/find-references.png)
"Find references" shows usages across the entire workspace
ty powers several language server features that allow you to navigate a Python codebase: - **Go to Definition**: Jump to where a symbol is defined. ty resolves imports, function calls, class references, and more. - **Go to Declaration**: Navigate to the declaration site of a symbol, which can differ from its definition (could be in a stub file). - **Go to Type Definition**: Navigate to the type of a symbol. For example, this takes you to the class `Person` when invoked on a variable `user: Person`. - **Find all references**: Find every usage of a function, class, or variable across your entire workspace. - **Document and workspace symbols**: See an outline of symbols in the current file, or search through symbols across your entire workspace. ## Code completions
![Code completion example](screenshots/code-completion.png){ width="500" }
Accepting this completion will automatically add a subprocess import at the top of the file.
ty provides intelligent code completions as you type, offering suggestions for variables, functions, classes, and modules that are in scope. For symbols that are not yet imported, ty suggests auto-import actions to add the necessary `import` statements. ## Code actions and refactorings
![Code action example](screenshots/quick-fix.png){ width="700" }
ty offers to remove the unused suppression comment
ty offers quick fixes and other code actions to help you resolve issues: - **Add import**: Automatically add missing import statements - **Quick fixes**: Some diagnostics come with quick fix suggestions to resolve the issue - **Rename symbol**: Safely rename symbols across your entire codebase - **Selection range**: Expand or shrink the text selection in your editor based on ty's understanding of Python syntax ## Contextual information
![Inlay hints](screenshots/inlay-hints.png)
Gray inlay hints and on-hover information (signature, docstring)
ty surfaces useful contextual information as you code: - **Hover**: Hover over any symbol to see its type, documentation, function signatures, and other useful information like the variance of type parameters. - **Inlay hints**: Display inline type hints for variables and parameters without explicit annotations, as well as parameter names at call sites. These hints can also be double-clicked to insert the type annotations into your source code. You can also click on parts of the inlay hints for go-to-definition navigation. - **Signature help**: When calling a function, ty displays the function's parameters and their types. This appears automatically when you type `(` and updates as you navigate between arguments. - **Document highlight**: When the cursor is on a symbol, ty highlights all occurrences of that symbol in the current file. - **Semantic highlighting**: Syntax highlighting based on the underlying semantics and types. ## Code folding ty provides Python specific code folding ranges to LSP clients upon request. This includes tagging docstrings as comments, which supports editor actions like "fold all comment blocks." ## Notebook support ty supports Jupyter notebooks (`.ipynb` files) with language server features. Each cell is analyzed in context, with diagnostics, completions, and other features working across cells. ## Fine-grained incrementality ty's architecture is designed for low-latency updates of diagnostics and other language server features. When you make a change in your editor, ty incrementally updates only the affected parts of the codebase, rather than re-analyzing everything from scratch. This happens at a fine-grained level, down to individual definitions. This incrementality means that you get instant feedback as you type, i.e., within a few milliseconds, even on large projects. !!! info Fine-grained dependencies also allow ty to skip large parts of 3rd-party dependencies when they are not relevant to your codebase. ## Feature reference | Feature | Status | Notes | | ----------------------------------------------------- | ---------------- | ------------------------------------------------------------- | | [`callHierarchy/*`][callhierarchy] | ❌ Not supported | [#1976] | | [`notebookDocument/*`][notebookdocument] | ✅ Supported | | | [`textDocument/codeAction`][codeaction] | ✅ Supported | Quick fixes | | [`textDocument/codeLens`][codelens] | ❌ Not supported | | | [`textDocument/completion`][completion] | ✅ Supported | | | [`textDocument/declaration`][declaration] | ✅ Supported | | | [`textDocument/definition`][definition] | ✅ Supported | | | [`textDocument/diagnostic`][diagnostic] | ✅ Supported | | | [`textDocument/documentColor`][documentcolor] | ❌ Not supported | | | [`textDocument/documentHighlight`][documenthighlight] | ✅ Supported | | | [`textDocument/documentLink`][documentlink] | ❌ Not supported | | | [`textDocument/documentSymbol`][documentsymbol] | ✅ Supported | | | [`textDocument/foldingRange`][foldingrange] | ✅ Supported | | | [`textDocument/formatting`][formatting] | — | Use [Ruff] for formatting | | [`textDocument/hover`][hover] | ✅ Supported | | | [`textDocument/implementation`][implementation] | ❌ Not supported | | | [`textDocument/inlayHint`][inlayhint] | ✅ Supported | | | [`textDocument/onTypeFormatting`][ontypeformatting] | — | [Ruff #16829](https://github.com/astral-sh/ruff/issues/16829) | | [`textDocument/prepareRename`][preparerename] | ✅ Supported | | | [`textDocument/rangeFormatting`][rangeformatting] | — | Use [Ruff] for formatting | | [`textDocument/references`][references] | ✅ Supported | | | [`textDocument/rename`][rename] | ✅ Supported | | | [`textDocument/selectionRange`][selectionrange] | ✅ Supported | | | [`textDocument/semanticTokens`][semantictokens] | ✅ Supported | | | [`textDocument/signatureHelp`][signaturehelp] | ✅ Supported | | | [`textDocument/typeDefinition`][typedefinition] | ✅ Supported | | | [`typeHierarchy/*`][typehierarchy] | ❌ Not supported | [#534] | | [`workspace/diagnostic`][workspacediagnostic] | ✅ Supported | | | [`workspace/symbol`][workspacesymbol] | ✅ Supported | | | [`workspace/willRenameFiles`][willrenamefiles] | ❌ Not supported | [#1560] | [#1560]: https://github.com/astral-sh/ty/issues/1560 [#1976]: https://github.com/astral-sh/ty/issues/1976 [#534]: https://github.com/astral-sh/ty/issues/534 [callhierarchy]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_incomingCalls [codeaction]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeAction [codelens]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeLens [completion]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion [declaration]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_declaration [definition]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition [diagnostic]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_diagnostic [documentcolor]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentColor [documenthighlight]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentHighlight [documentlink]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentLink [documentsymbol]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol [foldingrange]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_foldingRange [formatting]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_formatting [hover]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover [implementation]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_implementation [inlayhint]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_inlayHint [language server]: https://microsoft.github.io/language-server-protocol/ [notebookdocument]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#notebookDocument_synchronization [ontypeformatting]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_onTypeFormatting [preparerename]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareRename [rangeformatting]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_rangeFormatting [references]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references [rename]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_rename [ruff]: https://docs.astral.sh/ruff/ [selectionrange]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_selectionRange [semantictokens]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens [signaturehelp]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_signatureHelp [typedefinition]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_typeDefinition [typehierarchy]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#typeHierarchy_supertypes [willrenamefiles]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_willRenameFiles [workspacediagnostic]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_diagnostic [workspacesymbol]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol ================================================ FILE: docs/features/type-system.md ================================================ # Type system You can generally expect ty to support all typing features that are described and specified in the [Python typing documentation] (for a detailed overview, please refer to the [type system features tracking issue](https://github.com/astral-sh/ty/issues/1889)). This page highlights some of the unique features that ty's type system provides. ## Redeclarations ty allows you to reuse the same symbol with a different type. The following example shows how the `paths` parameter is redeclared as a list of strings: ```py def split_paths(paths: str) -> list[Path]: paths: list[str] = paths.split(":") return [Path(p) for p in paths] ``` (Full example in the [playground](https://play.ty.dev/80a74c95-a43e-4a3d-8c26-f88e879d7dcb)) ## Intersection types ty has first-class support for intersection types. In contrast to a union type `A | B`, which means "*either* A *or* B", an intersection type `A & B` means "*both* A *and* B". Type narrowing in ty is based on intersections. For example, notice how we can call `obj.serialize_json()` *and* access the `.version` property in the following function: ```py def output_as_json(obj: Serializable) -> str: if isinstance(obj, Versioned): reveal_type(obj) # reveals: Serializable & Versioned return str({ "data": obj.serialize_json(), "version": obj.version }) else: return obj.serialize_json() ``` (Full example in the [playground](https://play.ty.dev/39241435-5e78-4ce9-817f-ce65be73a6ed)) Intersections can also be built using gradual types like `Any` or its implicit counterpart `Unknown`. For example, imagine you call into untyped (third party) code that returns an object of type `Unknown`. Narrowing the type of that object using `isinstance` will result in an intersection type `Unknown & Iterable`. This type allows you to use `obj` as an iterable. But more importantly, it still gives you access to attributes defined on the original unknown type (`.description`, in this example): ```py def print_content(data: bytes): obj = untyped_library.deserialize(data) if isinstance(obj, Iterable): print(obj.description) for part in obj: print("*", part.description) else: print(obj.description) ``` (Full example in the [playground](https://play.ty.dev/8f98820e-7306-4d69-b572-56d69a92b90f)) Intersection types are also used in `hasattr` narrowing. Take a look at the following example where we narrow a type of `Person | Animal | None` using `hasattr(…, "name")`. `Person` is preserved in the narrowed union type because it has a `name` attribute. `Animal` is intersected with a synthetic protocol, accounting for the possibility of subclasses of `Animal` that add a `name` member. `None` is excluded completely since it is a final type that has no `name` attribute: ```py class Person: name: str class Animal: species: str def greet(being: Person | Animal | None): if hasattr(being, "name"): # `being` is now of type `Person | (Animal & )` print(f"Hello, {being.name}!") else: print("Hello there!") ``` (Full example in the [playground](https://play.ty.dev/31f2c718-516a-4a85-80e0-2a4682b818f1)) !!! info If you run into a situation like this and would like `Animal` to be excluded from the narrowed type as well, you can make `Animal` a `@final` class. This also allows ty to infer a more precise type for `being.name` (`str` instead of `object`). If ty is the only type checker you use, you can also make direct use of intersection types in annotations by importing `Intersection` from the special `ty_extensions` module that is (currently) only available at type-checking time: ```py from typing import TYPE_CHECKING if TYPE_CHECKING: from ty_extensions import Intersection type SerializableVersioned = Intersection[Serializable, Versioned] def output_as_json(obj: SerializableVersioned) -> str: ... ``` (Full example in the [playground](https://play.ty.dev/f003e901-0e45-4f45-9759-d6db9d5e5f66)) ## Top and bottom materializations Gradual types generally have two special [materializations]. The top materialization represents the "largest" type that a gradual type can materialize to: the union of all possible materializations. For example, the top materialization of `Any` is `object`, and the top materialization of `Any & int` is `int`. For invariant generic classes, the top materialization cannot be expressed in Python's type system, but it is a useful type that ty intersects with when `isinstance` checks involve generic classes. For example, when checking `isinstance(…, list)`, ty intersects with the top materialization of `list[Unknown]`: ```py @final class Item: ... def process(items: Item | list[Item]): if isinstance(items, list): # reveals: list[Item] reveal_type(items) ``` (Full example in the [playground](https://play.ty.dev/f1306120-0b8d-4ed5-b832-1f2d379eae2b)) !!! info You might wonder why `Item` is declared `@final` here. If we remove the `@final` decorator, the inferred type in the `if` branch becomes `(Item & Top[list[Unknown]]) | list[Item]` instead. This accounts for the possibility of classes that inherit from both `Item` *and* `list`! If you run into this situation and want to rule out this case, you can also perform the `isinstance` check against `Item` instead. The `else` branch will then have a narrowed type of `list[Item] & ~Item`, which effectively acts like `list[Item]`. ## Reachability based on types Reachability analysis in ty is based on type inference. This allows ty to detect unreachable branches in many more situations compared to approaches which match on a few known patterns (e.g. `sys.version_info >= (3, 10)` checks). This has useful practical applications. Consider a case where you are writing code that needs to be compatible with two major versions of a dependency. The following code can be successfully type-checked with either pydantic 1.x installed, or pydantic 2.x installed. In both cases, ty will only consider the corresponding branch to be reachable, and will not emit any type errors for the other branch. This works because `pydantic.__version__.startswith("2.")` can be evaluated to `True` or `False` at type-checking time: ```py import pydantic from pydantic import BaseModel PYDANTIC_V2 = pydantic.__version__.startswith("2.") class Person(BaseModel): name: str def to_json(person: Person): if PYDANTIC_V2: return person.model_dump_json() # no error here when checking with 1.x else: return person.json() ``` (Full example in the [playground](https://play.ty.dev/34a227bb-93d5-405e-86c3-72f57ec5642e)) ## Gradual guarantee ty generally tries to avoid emitting false positive type errors in untyped code. The following snippet does not produce any type errors when checked with ty (whereas other type checkers make the assumption that `max_retries` is of type `None`, leading to an error in the attribute assignment): ```py class RetryPolicy: max_retries = None policy = RetryPolicy() policy.max_retries = 1 ``` (Full example in the [playground](https://play.ty.dev/a5286db1-cdfd-45e7-af54-29649ba5c423)) This is achieved by treating `max_retries` as being of type `Unknown | None`, which means that the type of the attribute is not fully known, but `None` is definitely a possible value. Users can always opt into stricter checking by adding type annotations (`int | None`, in this case). !!! info We are also planning to add a mode for users that prefer to have stricter types inferred by default in these situations. You can follow [this issue](https://github.com/astral-sh/ty/issues/1240) for updates. ## Fixpoint iteration In a situation where a symbol's type cyclically depends on itself, ty uses a mechanism called fixpoint iteration to be able to infer a type for that symbol. In the `tick` method below, note how the type of `self.value` depends on `self.value` itself. ty starts by assuming that `self.value` is just `Unknown | Literal[0]` (the type inferred from the `__init__` method), and then iterates until the type converges to `Unknown | Literal[0, 1, 2, 3, 4]`. Without the modulo operation, the union would grow indefinitely. In that case, we fall back to `int` after a certain number of iterations. ```py class LoopingCounter: def __init__(self): self.value = 0 def tick(self): self.value = (self.value + 1) % 5 # reveals: Unknown | Literal[0, 1, 2, 3, 4] reveal_type(LoopingCounter().value) ``` (Full example in the [playground](https://play.ty.dev/64400d96-ee1b-48f3-8361-b583dddddf82)) [materializations]: https://typing.python.org/en/latest/spec/concepts.html#materialization [python typing documentation]: https://typing.python.org/en/latest/spec/index.html ================================================ FILE: docs/index.md ================================================ # ty An extremely fast Python type checker and language server, written in Rust.

Shows a bar chart with benchmark results.

Shows a bar chart with benchmark results.

Type checking the home-assistant project without caching.

ty is backed by [Astral](https://astral.sh), the creators of [uv](https://github.com/astral-sh/uv) and [Ruff](https://github.com/astral-sh/ruff). ## Highlights - 10x - 100x faster than mypy and Pyright - Comprehensive [diagnostics](./features/diagnostics.md) with rich contextual information - Configurable [rule levels](./rules.md), [per-file overrides](./reference/configuration.md#overrides), [suppression comments](./suppression.md), and first-class project support - Designed for adoption, with support for [redeclarations](./features/type-system.md#redeclarations) and [partially typed code](./features/type-system.md#gradual-guarantee) - [Language server](./features/language-server.md) with code navigation, completions, code actions, auto-import, inlay hints, on-hover help, etc. - Fine-grained [incremental analysis](./features/language-server.md#fine-grained-incrementality) designed for fast updates when editing files in an IDE - Editor integrations for [VS Code](./editors.md#vs-code), [PyCharm](./editors.md#pycharm), [Neovim](./editors.md#neovim) and more - Advanced typing features like first-class [intersection types](./features/type-system.md#intersection-types), advanced [type narrowing](./features/type-system.md#top-and-bottom-materializations), and [sophisticated reachability analysis](./features/type-system.md#reachability-based-on-types) ## Getting started Run ty with [uvx](https://docs.astral.sh/uv/guides/tools/#running-tools) to get started quickly: ```shell uvx ty check ``` ty will check all Python files in the working directory or project by default. See the [type checking](./type-checking.md) documentation for more details. ## Installation To install ty, see the [installation](./installation.md) documentation. To add the ty language server to your editor, see the [editor integration](./editors.md) guide. ## Playground ty has an [online playground](https://play.ty.dev) you can use to try it out on snippets or small projects. !!! tip The playground is a great way to share snippets with other people, e.g., when sharing a bug report. ================================================ FILE: docs/installation.md ================================================ # Installing ty ## Running ty without installation Use [uvx](https://docs.astral.sh/uv/guides/tools/) to quickly get started with ty: ```shell uvx ty ``` ## Installation methods ### Adding ty to your project !!! tip Adding ty as a dependency ensures that all developers on the project are using the same version of ty. Use [uv](https://github.com/astral-sh/uv) (or your project manager of choice) to add ty as a development dependency: ```shell uv add --dev ty ``` Then, use `uv run` to invoke ty: ```shell uv run ty ``` To update ty, use `--upgrade-package`: ```shell uv lock --upgrade-package ty ``` ### Installing globally with uv Install ty globally with uv: ```shell uv tool install ty@latest ``` To update ty, use `uv tool upgrade`: ```shell uv tool upgrade ty ``` ### Installing with the standalone installer ty includes a standalone installer. === "macOS and Linux" Use `curl` to download the script and execute it with `sh`: ```console $ curl -LsSf https://astral.sh/ty/install.sh | sh ``` If your system doesn't have `curl`, you can use `wget`: ```console $ wget -qO- https://astral.sh/ty/install.sh | sh ``` Request a specific version by including it in the URL: ```console $ curl -LsSf https://astral.sh/ty/0.0.24/install.sh | sh ``` === "Windows" Use `irm` to download the script and execute it with `iex`: ```pwsh-session PS> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/ty/install.ps1 | iex" ``` Changing the [execution policy](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.4#powershell-execution-policies) allows running a script from the internet. Request a specific version by including it in the URL: ```pwsh-session PS> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/ty/0.0.24/install.ps1 | iex" ``` !!! tip The installation script may be inspected before use: === "macOS and Linux" ```console $ curl -LsSf https://astral.sh/ty/install.sh | less ``` === "Windows" ```pwsh-session PS> powershell -c "irm https://astral.sh/ty/install.ps1 | more" ``` Alternatively, the installer or binaries can be downloaded directly from [GitHub](#installing-from-github-releases). ### Installing from GitHub Releases ty release artifacts can be downloaded directly from [GitHub Releases](https://github.com/astral-sh/ty/releases). Each release page includes binaries for all supported platforms as well as instructions for using the standalone installer via `github.com` instead of `astral.sh`. ### Installing globally with pipx Install ty globally with pipx: ```shell pipx install ty ``` To update ty, use `pipx upgrade`: ```shell pipx upgrade ty ``` ### Installing with pip Install ty into your current Python environment with pip: ```shell pip install ty ``` ### Installing globally with mise Install ty globally with with [mise](https://github.com/jdx/mise): ```shell mise install ty ``` To set it globally: ```shell mise use --global ty ``` ### Installing in Docker Install ty in Docker by copying the binary from the official image: ```dockerfile title="Dockerfile" COPY --from=ghcr.io/astral-sh/ty:latest /ty /bin/ ``` The following tags are available: - `ghcr.io/astral-sh/ty:latest` - `ghcr.io/astral-sh/ty:{major}.{minor}.{patch}`, e.g., `ghcr.io/astral-sh/ty:0.0.24` - `ghcr.io/astral-sh/ty:{major}.{minor}`, e.g., `ghcr.io/astral-sh/ty:0.0` (the latest patch version) ### Using ty with Bazel [`aspect_rules_lint`](https://registry.bazel.build/docs/aspect_rules_lint#function-lint_ty_aspect) provides a Bazel lint aspect that runs ty. See its documentation for setup instructions. ## Adding ty to your editor See the [editor integration](./editors.md) guide to add ty to your editor. ## Shell autocompletion !!! tip You can run `echo $SHELL` to help you determine your shell. To enable shell autocompletion for ty commands, run one of the following: === "Bash" ```bash echo 'eval "$(ty generate-shell-completion bash)"' >> ~/.bashrc ``` === "Zsh" ```bash echo 'eval "$(ty generate-shell-completion zsh)"' >> ~/.zshrc ``` === "fish" ```bash echo 'ty generate-shell-completion fish | source' > ~/.config/fish/completions/ty.fish ``` === "Elvish" ```bash echo 'eval (ty generate-shell-completion elvish | slurp)' >> ~/.elvish/rc.elv ``` === "PowerShell / pwsh" ```powershell if (!(Test-Path -Path $PROFILE)) { New-Item -ItemType File -Path $PROFILE -Force } Add-Content -Path $PROFILE -Value '(& ty generate-shell-completion powershell) | Out-String | Invoke-Expression' ``` Then restart the shell or source the shell config file. ================================================ FILE: docs/js/extra.js ================================================ function cleanupClipboardText(targetSelector) { const targetElement = document.querySelector(targetSelector); // exclude "Generic Prompt" and "Generic Output" spans from copy const excludedClasses = ["gp", "go"]; const clipboardText = Array.from(targetElement.childNodes) .filter( (node) => !excludedClasses.some((className) => node?.classList?.contains(className) ) ) .map((node) => node.textContent) .filter((s) => s != ""); return clipboardText.join("").trim(); } // Sets copy text to attributes lazily using an Intersection Observer. function setCopyText() { // The `data-clipboard-text` attribute allows for customized content in the copy // See: https://www.npmjs.com/package/clipboard#copy-text-from-attribute const attr = "clipboardText"; // all "copy" buttons whose target selector is a element const elements = document.querySelectorAll( 'button[data-clipboard-target$="code"]' ); const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { // target in the viewport that have not been patched if ( entry.intersectionRatio > 0 && entry.target.dataset[attr] === undefined ) { entry.target.dataset[attr] = cleanupClipboardText( entry.target.dataset.clipboardTarget ); } }); }); elements.forEach((elt) => { observer.observe(elt); }); } // Using the document$ observable is particularly important if you are using instant loading since // it will not result in a page refresh in the browser // See `How to integrate with third-party JavaScript libraries` guideline: // https://squidfunk.github.io/mkdocs-material/customization/?h=javascript#additional-javascript document$.subscribe(function () { setCopyText(); }); // Use client-side redirects for anchors that have moved. // Other redirects should use `redirect_maps` in the `mkdocs.yml` file instead. (function () { // (there are no redirects yet) let redirect_maps = {}; // The prefix for the site, see `site_dir` in `mkdocs.yml` let site_dir = "ty"; function get_path() { var path = window.location.pathname; // Trim the site prefix if (path.startsWith("/" + site_dir + "/")) { path = path.slice(site_dir.length + 2); } // Always include a trailing `/` if (!path.endsWith("/")) { path = path + "/"; } // Check for an anchor var anchor = window.location.hash.substring(1); if (!anchor) { return path; } return path + "#" + anchor; } let path = get_path(); if (path && redirect_maps.hasOwnProperty(path)) { window.location.replace("/" + site_dir + "/" + redirect_maps[path]); } })(); ================================================ FILE: docs/modules.md ================================================ # Module discovery ## First-party modules First-party modules are Python files that are part of your project source code. By default, ty searches for first-party modules in the project's root directory or the `src` directory, if present. If your project uses a different layout, configure the project's [`environment.root`](./reference/configuration.md#root) in your `pyproject.toml` or `ty.toml`. For example, if your project's code is in an `app/` directory: ```text example-pkg ├── README.md ├── pyproject.toml └── app └── example_pkg └── __init__.py ``` then set [`environment.root`](./reference/configuration.md#root) in your `pyproject.toml` to `["./app"]`: ```toml title="pyproject.toml" [tool.ty.environment] root = ["./app"] ``` Note that a `./python` folder is automatically added to the project `root` if it exists, and is not itself a package (i.e. does not contain an `__init__.py` file or an `__init__.pyi` file). ## Third-party modules Third-party modules are Python packages that are not part of your project or the standard library. These are usually declared as dependencies in a `pyproject.toml` or `requirements.txt` file and installed using a package manager like uv or pip. Examples of popular third-party modules are `requests`, `numpy` and `django`. ty searches for third-party modules in the configured [Python environment](#python-environment). ## Python environment The Python environment is used for discovery of third-party modules. By default, ty will attempt to discover a virtual environment. First, ty checks for an active virtual environment using the `VIRTUAL_ENV` environment variable. If not set, ty will search for a `.venv` directory in the project root or working directory. ty only supports discovery of virtual environments at this time. !!! note When using project management tools, such as uv or Poetry, the `run` command usually automatically activates the virtual environment and will be detected by ty. The Python environment may be explicitly configured using the [`environment.python`](./reference/configuration.md#python) setting or [`--python`](./reference/cli.md#ty-check--python) flag. When setting the environment explicitly, non-virtual environments can be provided. ================================================ FILE: docs/python-version.md ================================================ # Python version The Python version affects allowed syntax, type definitions of the standard library, and type definitions of first- and third-party modules that are conditional on the Python version. For example, Python 3.10 introduced support for `match` statements and added the `sys.stdlib_module_names` symbol to the standard library. Syntactic features always need to be available in the lowest supported Python version, but symbols may be used in a `sys.version_info` conditional branch: ```python import sys # `invalid-syntax` error if `python-version` is set to 3.9 or lower: match "echo hello".split(): case ["echo", message]: print(message) case _: print("unknown command") # `unresolved-attribute` error if `python-version` is set to 3.9 or lower: print(sys.stdlib_module_names) if sys.version_info >= (3, 10): # ok, because the usage is guarded by a version check: print(sys.stdlib_module_names) ``` By default, the lower bound of the project's [`requires-python`](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#python-requires) field (from the `pyproject.toml`) is used as the target Python version, ensuring that features and symbols only available in newer Python versions are not used. If the `requires-python` field is not available but a virtual environment *has* been configured or detected, ty will try to infer the Python version being used from the virtual environment's metadata. If no virtual environment is present or inferring the Python version from the metadata fails, ty will fall back to the latest stable Python version supported by ty (currently 3.14). The Python version may also be explicitly specified using the [`python-version`](./reference/configuration.md#python-version) setting or the [`--python-version`](./reference/cli.md#ty-check--python-version) flag. ================================================ FILE: docs/reference/cli.md ================================================ # CLI Reference ## ty An extremely fast Python type checker.

Usage

``` ty ```

Commands

ty check

Check a project for type errors

ty server

Start the language server

ty version

Display ty's version

ty explain

Explain rules and other parts of ty

ty help

Print this message or the help of the given subcommand(s)

## ty check Check a project for type errors

Usage

``` ty check [OPTIONS] [PATH]... ```

Arguments

PATHS

List of files or directories to check [default: the project root]

Options

--add-ignore

Adds ty: ignore comments to suppress all rule diagnostics

--color when

Control when colored output is used

Possible values:

  • auto: Display colors if the output goes to an interactive terminal
  • always: Always display colors
  • never: Never display colors
--config, -c config-option

A TOML <KEY> = <VALUE> pair (such as you might find in a ty.toml configuration file) overriding a specific configuration option.

Overrides of individual settings using this option always take precedence over all configuration files.

--config-file path

The path to a ty.toml file to use for configuration.

While ty configuration can be included in a pyproject.toml file, it is not allowed in this context.

May also be set with the TY_CONFIG_FILE environment variable.

--error rule

Treat the given rule as having severity 'error'. Can be specified multiple times. Use 'all' to apply to all rules.

--error-on-warning

Use exit code 1 if there are any warning-level diagnostics

--exclude exclude

Glob patterns for files to exclude from type checking.

Uses gitignore-style syntax to exclude files and directories from type checking. Supports patterns like tests/, *.tmp, **/__pycache__/**.

--exit-zero

Always use exit code 0, even when there are error-level diagnostics

--extra-search-path path

Additional path to use as a module-resolution source (can be passed multiple times).

This is an advanced option that should usually only be used for first-party or third-party modules that are not installed into your Python environment in a conventional way. Use --python to point ty to your Python environment if it is in an unusual location.

--force-exclude

Enforce exclusions, even for paths passed to ty directly on the command-line. Use --no-force-exclude to disable

--help, -h

Print help (see a summary with '-h')

--ignore rule

Disables the rule. Can be specified multiple times. Use 'all' to apply to all rules.

--no-progress

Hide all progress outputs.

For example, spinners or progress bars.

--output-format output-format

The format to use for printing diagnostic messages

May also be set with the TY_OUTPUT_FORMAT environment variable.

Possible values:

  • full: Print diagnostics verbosely, with context and helpful hints (default)
  • concise: Print diagnostics concisely, one per line
  • gitlab: Print diagnostics in the JSON format expected by GitLab Code Quality reports
  • github: Print diagnostics in the format used by GitHub Actions workflow error annotations
  • junit: Print diagnostics as a JUnit-style XML report
--project project

Run the command within the given project directory.

All pyproject.toml files will be discovered by walking up the directory tree from the given project directory, as will the project's virtual environment (.venv) unless the venv-path option is set.

Other command-line arguments (such as relative paths) will be resolved relative to the current working directory.

--python, --venv path

Path to your project's Python environment or interpreter.

ty uses your Python environment to resolve third-party imports in your code.

This can be a path to:

  • A Python interpreter, e.g. .venv/bin/python3 - A virtual environment directory, e.g. .venv - A system Python sys.prefix directory, e.g. /usr

If you're using a project management tool such as uv or you have an activated Conda or virtual environment, you should not generally need to specify this option.

--python-platform, --platform platform

Target platform to assume when resolving types.

This is used to specialize the type of sys.platform and will affect the visibility of platform-specific functions and attributes. If the value is set to all, no assumptions are made about the target platform. If unspecified, the current system's platform will be used.

--python-version, --target-version version

Python version to assume when resolving types.

The Python version affects allowed syntax, type definitions of the standard library, and type definitions of first- and third-party modules that are conditional on the Python version.

If a version is not specified on the command line or in a configuration file, ty will try the following techniques in order of preference to determine a value: 1. Check for the project.requires-python setting in a pyproject.toml file and use the minimum version from the specified range 2. Check for an activated or configured Python environment and attempt to infer the Python version of that environment 3. Fall back to the latest stable Python version supported by ty (see ty check --help output)

Possible values:

  • 3.7
  • 3.8
  • 3.9
  • 3.10
  • 3.11
  • 3.12
  • 3.13
  • 3.14
  • 3.15
--quiet, -q

Use quiet output (or -qq for silent output)

--respect-ignore-files

Respect file exclusions via .gitignore and other standard ignore files. Use --no-respect-ignore-files to disable

--typeshed, --custom-typeshed-dir path

Custom directory to use for stdlib typeshed stubs

--verbose, -v

Use verbose output (or -vv and -vvv for more verbose output)

--warn rule

Treat the given rule as having severity 'warn'. Can be specified multiple times. Use 'all' to apply to all rules.

--watch, -W

Watch files for changes and recheck files related to the changed files

## ty server Start the language server

Usage

``` ty server ```

Options

--help, -h

Print help

## ty version Display ty's version

Usage

``` ty version [OPTIONS] ```

Options

--help, -h

Print help

--output-format output-format

The format in which to display the version information

[default: text]

Possible values:

  • text
  • json
## ty generate-shell-completion Generate shell completion

Usage

``` ty generate-shell-completion ```

Arguments

SHELL

Options

--help, -h

Print help

## ty explain Explain rules and other parts of ty

Usage

``` ty explain ```

Commands

ty explain rule

Explain a rule (or all rules)

ty explain help

Print this message or the help of the given subcommand(s)

### ty explain rule Explain a rule (or all rules)

Usage

``` ty explain rule [OPTIONS] [RULE] ```

Arguments

RULE

Rule to explain

Defaults to all rules if omitted.

Options

--help, -h

Print help (see a summary with '-h')

--output-format output-format

Output format

[default: text]

Possible values:

  • text
  • json
### ty explain help Print this message or the help of the given subcommand(s)

Usage

``` ty explain help [COMMAND] ``` ## ty help Print this message or the help of the given subcommand(s)

Usage

``` ty help [COMMAND] ``` ================================================ FILE: docs/reference/configuration.md ================================================ # Configuration ## `rules` Configures the enabled rules and their severity. The keys are either rule names or `all` to set a default severity for all rules. See [the rules documentation](https://ty.dev/rules) for a list of all available rules. Valid severities are: * `ignore`: Disable the rule. * `warn`: Enable the rule and create a warning diagnostic. * `error`: Enable the rule and create an error diagnostic. ty will exit with a non-zero code if any error diagnostics are emitted. **Default value**: `{...}` **Type**: `dict[RuleName | "all", "ignore" | "warn" | "error"]` **Example usage**: === "pyproject.toml" ```toml [tool.ty.rules] possibly-unresolved-reference = "warn" division-by-zero = "ignore" ``` === "ty.toml" ```toml [rules] possibly-unresolved-reference = "warn" division-by-zero = "ignore" ``` --- ## `analysis` ### `allowed-unresolved-imports` A list of module glob patterns for which `unresolved-import` diagnostics should be suppressed. Details on supported glob patterns: - `*` matches zero or more characters except `.`. For example, `foo.*` matches `foo.bar` but not `foo.bar.baz`; `foo*` matches `foo` and `foobar` but not `foo.bar` or `barfoo`; and `*foo` matches `foo` and `barfoo` but not `foo.bar` or `foobar`. - `**` matches any number of module components (e.g., `foo.**` matches `foo`, `foo.bar`, etc.) - Prefix a pattern with `!` to exclude matching modules When multiple patterns match, later entries take precedence. Glob patterns can be used in combinations with each other. For example, to suppress errors for any module where the first component contains the substring `test`, use `*test*.**`. **Default value**: `[]` **Type**: `list[str]` **Example usage**: === "pyproject.toml" ```toml [tool.ty.analysis] # Suppress errors for all `test` modules except `test.foo` allowed-unresolved-imports = ["test.**", "!test.foo"] ``` === "ty.toml" ```toml [analysis] # Suppress errors for all `test` modules except `test.foo` allowed-unresolved-imports = ["test.**", "!test.foo"] ``` --- ### `replace-imports-with-any` A list of module glob patterns whose imports should be replaced with `typing.Any`. Unlike `allowed-unresolved-imports`, this setting replaces the module's type information with `typing.Any` even if the module can be resolved. Import diagnostics are unconditionally suppressed for matching modules. - Prefix a pattern with `!` to exclude matching modules When multiple patterns match, later entries take precedence. Glob patterns can be used in combinations with each other. For example, to suppress errors for any module where the first component contains the substring `test`, use `*test*.**`. When multiple patterns match, later entries take precedence. **Default value**: `[]` **Type**: `list[str]` **Example usage**: === "pyproject.toml" ```toml [tool.ty.analysis] # Replace all pandas and numpy imports with Any replace-imports-with-any = ["pandas.**", "numpy.**"] ``` === "ty.toml" ```toml [analysis] # Replace all pandas and numpy imports with Any replace-imports-with-any = ["pandas.**", "numpy.**"] ``` --- ### `respect-type-ignore-comments` Whether ty should respect `type: ignore` comments. When set to `false`, `type: ignore` comments are treated like any other normal comment and can't be used to suppress ty errors (you have to use `ty: ignore` instead). Setting this option can be useful when using ty alongside other type checkers or when you prefer using `ty: ignore` over `type: ignore`. Defaults to `true`. **Default value**: `true` **Type**: `bool` **Example usage**: === "pyproject.toml" ```toml [tool.ty.analysis] # Disable support for `type: ignore` comments respect-type-ignore-comments = false ``` === "ty.toml" ```toml [analysis] # Disable support for `type: ignore` comments respect-type-ignore-comments = false ``` --- ## `environment` ### `extra-paths` User-provided paths that should take first priority in module resolution. This is an advanced option that should usually only be used for first-party or third-party modules that are not installed into your Python environment in a conventional way. Use the `python` option to specify the location of your Python environment. This option is similar to mypy's `MYPYPATH` environment variable and pyright's `stubPath` configuration setting. **Default value**: `[]` **Type**: `list[str]` **Example usage**: === "pyproject.toml" ```toml [tool.ty.environment] extra-paths = ["./shared/my-search-path"] ``` === "ty.toml" ```toml [environment] extra-paths = ["./shared/my-search-path"] ``` --- ### `python` Path to your project's Python environment or interpreter. ty uses the `site-packages` directory of your project's Python environment to resolve third-party (and, in some cases, first-party) imports in your code. This can be a path to: - A Python interpreter, e.g. `.venv/bin/python3` - A virtual environment directory, e.g. `.venv` - A system Python [`sys.prefix`] directory, e.g. `/usr` If you're using a project management tool such as uv, you should not generally need to specify this option, as commands such as `uv run` will set the `VIRTUAL_ENV` environment variable to point to your project's virtual environment. ty can also infer the location of your environment from an activated Conda environment, and will look for a `.venv` directory in the project root if none of the above apply. Failing that, ty will look for a `python3` or `python` binary available in `PATH`. [`sys.prefix`]: https://docs.python.org/3/library/sys.html#sys.prefix **Default value**: `null` **Type**: `str` **Example usage**: === "pyproject.toml" ```toml [tool.ty.environment] python = "./custom-venv-location/.venv" ``` === "ty.toml" ```toml [environment] python = "./custom-venv-location/.venv" ``` --- ### `python-platform` Specifies the target platform that will be used to analyze the source code. If specified, ty will understand conditions based on comparisons with `sys.platform`, such as are commonly found in typeshed to reflect the differing contents of the standard library across platforms. If `all` is specified, ty will assume that the source code can run on any platform. If no platform is specified, ty will use the current platform: - `win32` for Windows - `darwin` for macOS - `android` for Android - `ios` for iOS - `linux` for everything else **Default value**: `` **Type**: `"win32" | "darwin" | "android" | "ios" | "linux" | "all" | str` **Example usage**: === "pyproject.toml" ```toml [tool.ty.environment] # Tailor type stubs and conditionalized type definitions to windows. python-platform = "win32" ``` === "ty.toml" ```toml [environment] # Tailor type stubs and conditionalized type definitions to windows. python-platform = "win32" ``` --- ### `python-version` Specifies the version of Python that will be used to analyze 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, ty will generate errors if the source code makes use of language features that are not supported in that version. If a version is not specified, ty will try the following techniques in order of preference to determine a value: 1. Check for the `project.requires-python` setting in a `pyproject.toml` file and use the minimum version from the specified range 2. Check for an activated or configured Python environment and attempt to infer the Python version of that environment 3. Fall back to the default value (see below) For some language features, ty can also understand conditionals based on comparisons with `sys.version_info`. These are commonly found in typeshed, for example, to reflect the differing contents of the standard library across Python versions. **Default value**: `"3.14"` **Type**: `"3.7" | "3.8" | "3.9" | "3.10" | "3.11" | "3.12" | "3.13" | "3.14" | .` **Example usage**: === "pyproject.toml" ```toml [tool.ty.environment] python-version = "3.12" ``` === "ty.toml" ```toml [environment] python-version = "3.12" ``` --- ### `root` The root paths of the project, used for finding first-party modules. Accepts a list of directory paths searched in priority order (first has highest priority). If left unspecified, ty will try to detect common project layouts and initialize `root` accordingly. The project root (`.`) is always included. Additionally, the following directories are included if they exist and are not packages (i.e. they do not contain `__init__.py` or `__init__.pyi` files): * `./src` * `./` (if a `.//` directory exists) * `./python` **Default value**: `null` **Type**: `list[str]` **Example usage**: === "pyproject.toml" ```toml [tool.ty.environment] # Multiple directories (priority order) root = ["./src", "./lib", "./vendor"] ``` === "ty.toml" ```toml [environment] # Multiple directories (priority order) root = ["./src", "./lib", "./vendor"] ``` --- ### `typeshed` Optional path to a "typeshed" directory on disk for us to use for standard-library types. If this is not provided, we will fallback to our vendored typeshed stubs for the stdlib, bundled as a zip file in the binary **Default value**: `null` **Type**: `str` **Example usage**: === "pyproject.toml" ```toml [tool.ty.environment] typeshed = "/path/to/custom/typeshed" ``` === "ty.toml" ```toml [environment] typeshed = "/path/to/custom/typeshed" ``` --- ## `overrides` Configuration override that applies to specific files based on glob patterns. An override allows you to apply different rule configurations to specific files or directories. Multiple overrides can match the same file, with later overrides take precedence. Override rules take precedence over global rules for matching files. For example, to relax enforcement of rules in test files: ```toml [[tool.ty.overrides]] include = ["tests/**", "**/test_*.py"] [tool.ty.overrides.rules] possibly-unresolved-reference = "warn" ``` Or, to ignore a rule in generated files but retain enforcement in an important file: ```toml [[tool.ty.overrides]] include = ["generated/**"] exclude = ["generated/important.py"] [tool.ty.overrides.rules] possibly-unresolved-reference = "ignore" ``` ### `exclude` A list of file and directory patterns to exclude from this override. Patterns follow a syntax similar to `.gitignore`. Exclude patterns take precedence over include patterns within the same override. If not specified, defaults to `[]` (excludes no files). **Default value**: `null` **Type**: `list[str]` **Example usage**: === "pyproject.toml" ```toml [[tool.ty.overrides]] exclude = [ "generated", "*.proto", "tests/fixtures/**", "!tests/fixtures/important.py" # Include this one file ] ``` === "ty.toml" ```toml [[overrides]] exclude = [ "generated", "*.proto", "tests/fixtures/**", "!tests/fixtures/important.py" # Include this one file ] ``` --- ### `include` A list of file and directory patterns to include for this override. The `include` option follows a similar syntax to `.gitignore` but reversed: Including a file or directory will make it so that it (and its contents) are affected by this override. If not specified, defaults to `["**"]` (matches all files). **Default value**: `null` **Type**: `list[str]` **Example usage**: === "pyproject.toml" ```toml [[tool.ty.overrides]] include = [ "src", "tests", ] ``` === "ty.toml" ```toml [[overrides]] include = [ "src", "tests", ] ``` --- ### `rules` Rule overrides for files matching the include/exclude patterns. These rules will be merged with the global rules, with override rules taking precedence for matching files. You can set rules to different severity levels or disable them entirely. **Default value**: `{...}` **Type**: `dict[RuleName | "all", "ignore" | "warn" | "error"]` **Example usage**: === "pyproject.toml" ```toml [[tool.ty.overrides]] include = ["src"] [tool.ty.overrides.rules] possibly-unresolved-reference = "ignore" ``` === "ty.toml" ```toml [[overrides]] include = ["src"] [overrides.rules] possibly-unresolved-reference = "ignore" ``` --- ## `overrides.analysis` #### `allowed-unresolved-imports` A list of module glob patterns for which `unresolved-import` diagnostics should be suppressed. Details on supported glob patterns: - `*` matches zero or more characters except `.`. For example, `foo.*` matches `foo.bar` but not `foo.bar.baz`; `foo*` matches `foo` and `foobar` but not `foo.bar` or `barfoo`; and `*foo` matches `foo` and `barfoo` but not `foo.bar` or `foobar`. - `**` matches any number of module components (e.g., `foo.**` matches `foo`, `foo.bar`, etc.) - Prefix a pattern with `!` to exclude matching modules When multiple patterns match, later entries take precedence. Glob patterns can be used in combinations with each other. For example, to suppress errors for any module where the first component contains the substring `test`, use `*test*.**`. **Default value**: `[]` **Type**: `list[str]` **Example usage**: === "pyproject.toml" ```toml [tool.ty.overrides.analysis] # Suppress errors for all `test` modules except `test.foo` allowed-unresolved-imports = ["test.**", "!test.foo"] ``` === "ty.toml" ```toml [overrides.analysis] # Suppress errors for all `test` modules except `test.foo` allowed-unresolved-imports = ["test.**", "!test.foo"] ``` --- #### `replace-imports-with-any` A list of module glob patterns whose imports should be replaced with `typing.Any`. Unlike `allowed-unresolved-imports`, this setting replaces the module's type information with `typing.Any` even if the module can be resolved. Import diagnostics are unconditionally suppressed for matching modules. - Prefix a pattern with `!` to exclude matching modules When multiple patterns match, later entries take precedence. Glob patterns can be used in combinations with each other. For example, to suppress errors for any module where the first component contains the substring `test`, use `*test*.**`. When multiple patterns match, later entries take precedence. **Default value**: `[]` **Type**: `list[str]` **Example usage**: === "pyproject.toml" ```toml [tool.ty.overrides.analysis] # Replace all pandas and numpy imports with Any replace-imports-with-any = ["pandas.**", "numpy.**"] ``` === "ty.toml" ```toml [overrides.analysis] # Replace all pandas and numpy imports with Any replace-imports-with-any = ["pandas.**", "numpy.**"] ``` --- #### `respect-type-ignore-comments` Whether ty should respect `type: ignore` comments. When set to `false`, `type: ignore` comments are treated like any other normal comment and can't be used to suppress ty errors (you have to use `ty: ignore` instead). Setting this option can be useful when using ty alongside other type checkers or when you prefer using `ty: ignore` over `type: ignore`. Defaults to `true`. **Default value**: `true` **Type**: `bool` **Example usage**: === "pyproject.toml" ```toml [tool.ty.overrides.analysis] # Disable support for `type: ignore` comments respect-type-ignore-comments = false ``` === "ty.toml" ```toml [overrides.analysis] # Disable support for `type: ignore` comments respect-type-ignore-comments = false ``` --- ## `src` ### `exclude` A list of file and directory patterns to exclude from type checking. Patterns follow a syntax similar to `.gitignore`: - `./src/` matches only a directory - `./src` matches both files and directories - `src` matches files or directories named `src` - `*` matches any (possibly empty) sequence of characters (except `/`). - `**` matches zero or more path components. This sequence **must** form a single path component, so both `**a` and `b**` are invalid and will result in an error. A sequence of more than two consecutive `*` characters is also invalid. - `?` matches any single character except `/` - `[abc]` matches any character inside the brackets. Character sequences can also specify ranges of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid. - `!pattern` negates a pattern (undoes the exclusion of files that would otherwise be excluded) All paths are anchored relative to the project root (`src` only matches `/src` and not `/test/src`). To exclude any directory or file named `src`, use `**/src` instead. By default, ty excludes commonly ignored directories: - `**/.bzr/` - `**/.direnv/` - `**/.eggs/` - `**/.git/` - `**/.git-rewrite/` - `**/.hg/` - `**/.mypy_cache/` - `**/.nox/` - `**/.pants.d/` - `**/.pytype/` - `**/.ruff_cache/` - `**/.svn/` - `**/.tox/` - `**/.venv/` - `**/__pypackages__/` - `**/_build/` - `**/buck-out/` - `**/dist/` - `**/node_modules/` - `**/venv/` You can override any default exclude by using a negated pattern. For example, to re-include `dist` use `exclude = ["!dist"]` **Default value**: `null` **Type**: `list[str]` **Example usage**: === "pyproject.toml" ```toml [tool.ty.src] exclude = [ "generated", "*.proto", "tests/fixtures/**", "!tests/fixtures/important.py" # Include this one file ] ``` === "ty.toml" ```toml [src] exclude = [ "generated", "*.proto", "tests/fixtures/**", "!tests/fixtures/important.py" # Include this one file ] ``` --- ### `include` A list of files and directories to check. The `include` option follows a similar syntax to `.gitignore` but reversed: Including a file or directory will make it so that it (and its contents) are type checked. - `./src/` matches only a directory - `./src` matches both files and directories - `src` matches a file or directory named `src` - `*` matches any (possibly empty) sequence of characters (except `/`). - `**` matches zero or more path components. This sequence **must** form a single path component, so both `**a` and `b**` are invalid and will result in an error. A sequence of more than two consecutive `*` characters is also invalid. - `?` matches any single character except `/` - `[abc]` matches any character inside the brackets. Character sequences can also specify ranges of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any character between `0` and `9` inclusive. An unclosed bracket is invalid. All paths are anchored relative to the project root (`src` only matches `/src` and not `/test/src`). `exclude` takes precedence over `include`. **Default value**: `null` **Type**: `list[str]` **Example usage**: === "pyproject.toml" ```toml [tool.ty.src] include = [ "src", "tests", ] ``` === "ty.toml" ```toml [src] include = [ "src", "tests", ] ``` --- ### `respect-ignore-files` Whether to automatically exclude files that are ignored by `.ignore`, `.gitignore`, `.git/info/exclude`, and global `gitignore` files. Enabled by default. **Default value**: `true` **Type**: `bool` **Example usage**: === "pyproject.toml" ```toml [tool.ty.src] respect-ignore-files = false ``` === "ty.toml" ```toml [src] respect-ignore-files = false ``` --- ### `root` !!! warning "Deprecated" This option has been deprecated. Use `environment.root` instead. The root of the project, used for finding first-party modules. If left unspecified, ty will try to detect common project layouts and initialize `src.root` accordingly. The project root (`.`) is always included. Additionally, the following directories are included if they exist and are not packages (i.e. they do not contain `__init__.py` or `__init__.pyi` files): * `./src` * `./` (if a `.//` directory exists) * `./python` **Default value**: `null` **Type**: `str` **Example usage**: === "pyproject.toml" ```toml [tool.ty.src] root = "./app" ``` === "ty.toml" ```toml [src] root = "./app" ``` --- ## `terminal` ### `error-on-warning` Use exit code 1 if there are any warning-level diagnostics. Defaults to `false`. **Default value**: `false` **Type**: `bool` **Example usage**: === "pyproject.toml" ```toml [tool.ty.terminal] # Error if ty emits any warning-level diagnostics. error-on-warning = true ``` === "ty.toml" ```toml [terminal] # Error if ty emits any warning-level diagnostics. error-on-warning = true ``` --- ### `output-format` The format to use for printing diagnostic messages. Defaults to `full`. **Default value**: `full` **Type**: `full | concise | github | gitlab | junit` **Example usage**: === "pyproject.toml" ```toml [tool.ty.terminal] output-format = "concise" ``` === "ty.toml" ```toml [terminal] output-format = "concise" ``` --- ================================================ FILE: docs/reference/editor-settings.md ================================================ # Editor settings The editor settings supported by ty's language server, as well as the settings specific to [ty's VS Code extension][ty-vscode]. ## `configuration` In-editor configuration of ty's settings. The inline settings always take precedence over the settings from configuration files, including the configuration specified with [`configurationFile`](#configurationfile). Consult [the configuration reference](../configuration.md) for a list of all supported configuration options. **Default value**: `null` **Type**: `object` **Example usage**: === "VS Code" ```json { "ty.configuration": { "rules": { "unresolved-reference": "warn" } } } ``` === "Neovim" ```lua -- Neovim >=0.11: vim.lsp.config('ty', { settings = { ty = { configuration = { rules = { ["unresolved-reference"] = "warn" } } }, }, }) -- Neovim <0.11: require('lspconfig').ty.setup({ settings = { ty = { configuration = { rules = { ["unresolved-reference"] = "warn" } } }, }, }) ``` === "Zed" ```json { "lsp": { "ty": { "settings": { "configuration": { "rules": { "unresolved-reference": "warn" } } } } } } ``` ## `configurationFile` The path to a `ty.toml` configuration file. ty will use the specified configuration over any automatically discovered configuration. ty will expand a tilde `~` at the start of a string to the user's home directory, as well as variables like `$A` or `${A}`. !!! info While ty configuration can be included in a `pyproject.toml` file, it is not allowed in this context. **Default value**: `null` **Type**: `string` **Example usage**: === "VS Code" ```json { "ty.configurationFile": "./.config/ty.toml" } ``` === "Neovim" ```lua -- Neovim >=0.11: vim.lsp.config('ty', { settings = { ty = { configurationFile = "./.config/ty.toml" }, }, }) -- Neovim <0.11: require('lspconfig').ty.setup({ settings = { ty = { configurationFile = "./.config/ty.toml" }, }, }) ``` === "Zed" ```json { "lsp": { "ty": { "settings": { "configurationFile": "./.config/ty.toml" } } } } ``` ______________________________________________________________________ ## `disableLanguageServices` Whether to disable the language services for the ty language server like code completion, hover, go to definition, etc. This is useful if you want to use ty exclusively for type checking and want to use another language server for features like code completion, hover, go to definition, etc. **Default value**: `false` **Type**: `boolean` **Example usage**: === "VS Code" ```json { "ty.disableLanguageServices": true } ``` === "Neovim" ```lua -- Neovim >=0.11: vim.lsp.config('ty', { settings = { ty = { disableLanguageServices = true, }, }, }) -- Neovim <0.11: require('lspconfig').ty.setup({ settings = { ty = { disableLanguageServices = true, }, }, }) ``` === "Zed" ```json { "lsp": { "ty": { "settings": { "disableLanguageServices": true } } } } ``` ______________________________________________________________________ ## `diagnosticMode` Determines the scope of the diagnostics reported by the language server. Setting this to `off` is useful if you want to use ty exclusively for the language server features like code completion, hover, go to definition, etc. - `off`: Diagnostics are disabled. - `openFilesOnly`: Diagnostics are reported only for files that are currently open in the editor. - `workspace`: Diagnostics are reported for all files in the workspace. **Default value**: `"openFilesOnly"` **Type**: `"off" | "workspace" | "openFilesOnly"` **Example usage**: === "VS Code" ```json { "ty.diagnosticMode": "workspace" } ``` === "Neovim" ```lua -- Neovim >=0.11: vim.lsp.config('ty', { settings = { ty = { diagnosticMode = 'workspace', }, }, }) -- Neovim <0.11: require('lspconfig').ty.setup({ settings = { ty = { diagnosticMode = 'workspace', }, }, }) ``` === "Zed" ```json { "lsp": { "ty": { "settings": { "diagnosticMode": "workspace" } } } } ``` ______________________________________________________________________ ## `showSyntaxErrors` Whether to show syntax error diagnostics. This is useful when using ty with other language servers, allowing the user to refer to syntax errors from only one source. **Default value**: `true` **Type**: `bool` **Example usage**: === "VS Code" ```json { "ty.showSyntaxErrors": false } ``` === "Neovim" ```lua -- Neovim >=0.11: vim.lsp.config('ty', { settings = { ty = { showSyntaxErrors = false, }, }, }) -- Neovim <0.11: require('lspconfig').ty.setup({ settings = { ty = { showSyntaxErrors = false, }, }, }) ``` === "Zed" ```json { "lsp": { "ty": { "settings": { "showSyntaxErrors": false } } } } ``` ______________________________________________________________________ ## `inlayHints` These settings control the inline hints that ty provides in an editor. ### `variableTypes` Whether to show the types of variables as inline hints. **Default value**: `true` **Type**: `boolean` **Example usage**: === "VS Code" ```json { "ty.inlayHints.variableTypes": false } ``` === "Neovim" ```lua -- Neovim >=0.11: vim.lsp.config('ty', { settings = { ty = { inlayHints = { variableTypes = false, }, }, }, }) -- Neovim <0.11: require('lspconfig').ty.setup({ settings = { ty = { inlayHints = { variableTypes = false, }, }, }, }) ``` === "Zed" ```json { "lsp": { "ty": { "settings": { "inlayHints": { "variableTypes": false } } } } } ``` ### `callArgumentNames` Whether to show argument names in call expressions as inline hints. **Default value**: `true` **Type**: `boolean` **Example usage**: === "VS Code" ```json { "ty.inlayHints.callArgumentNames": false } ``` === "Neovim" ```lua -- Neovim >=0.11: vim.lsp.config('ty', { settings = { ty = { inlayHints = { callArgumentNames = false, }, }, }, }) -- Neovim <0.11: require('lspconfig').ty.setup({ settings = { ty = { inlayHints = { callArgumentNames = false, }, }, }, }) ``` === "Zed" ```json { "lsp": { "ty": { "settings": { "inlayHints": { "callArgumentNames": false } } } } } ``` ______________________________________________________________________ ## `completions` These settings control how code completions offered by ty work. ### `autoImport` Whether to include auto-import suggestions in code completions. That is, code completions will include symbols not currently in scope but available in your environment. **Default value**: `true` **Type**: `boolean` **Example usage**: === "VS Code" ```json { "ty.completions.autoImport": true } ``` === "Neovim" ```lua -- Neovim >=0.11: vim.lsp.config('ty', { settings = { ty = { completions = { autoImport = true, }, }, }, }) -- Neovim <0.11: require('lspconfig').ty.setup({ settings = { ty = { completions = { autoImport = true, }, }, }, }) ``` === "Zed" ```json { "lsp": { "ty": { "settings": { "completions": { "autoImport": true } } } } } ``` ______________________________________________________________________ ## VS Code specific The following settings are specific to [ty's VS Code extension][ty-vscode]. ### `importStrategy` Strategy for loading the `ty` executable. - `fromEnvironment` finds ty in the environment, falling back to the bundled version - `useBundled` uses the version bundled with the extension **Default value**: `"fromEnvironment"` **Type**: `"fromEnvironment" | "useBundled"` **Example usage**: ```json { "ty.importStrategy": "useBundled" } ``` ______________________________________________________________________ ### `interpreter` A list of paths to Python interpreters. Even though this is a list, only the first interpreter is used. The interpreter path is used to find the `ty` executable when [`ty.importStrategy`](#importstrategy) is set to `fromEnvironment`. **Default value**: `[]` **Type**: `string[]` **Example usage**: ```json { "ty.interpreter": ["/home/user/.local/bin/python"] } ``` ______________________________________________________________________ ### `path` A list of path to `ty` executables. The extension uses the first executable that exists. This setting takes precedence over the [`ty.importStrategy`](#importstrategy) setting. **Default value**: `[]` **Type**: `string[]` **Example usage**: ```json { "ty.path": ["/home/user/.local/bin/ty"] } ``` ______________________________________________________________________ ### `trace.server` The detail level at which messages between the language server and the editor (client) are logged. This setting is useful for debugging issues with the language server. Refer to the [troubleshooting guide](https://github.com/astral-sh/ty-vscode/blob/6cf16b4e87342a49f2bec1310a730cde8229e1d9/TROUBLESHOOTING.md) in [ty's VS Code extension][ty-vscode] for more information. **Default value**: `"off"` **Type**: `"off" | "messages" | "verbose"` **Example usage**: ```json { "ty.trace.server": "messages" } ``` ______________________________________________________________________ ## Initialization options The following settings are required when ty is initialized in an editor. These settings are static so changing them requires restarting the editor to take effect. For VS Code users, these settings are defined in the `ty.*` namespace as usual, but for other editors, they would need to be provided in a separate field of the configuration that corresponds to the initialization options. Refer to the examples below for how to set these options in different editors. ### `logFile` Path to the file to which the language server writes its log messages. By default, ty writes log messages to stderr. **Default value**: `null` **Type**: `string` **Example usage**: === "VS Code" ```json { "ty.logFile": "/path/to/ty.log" } ``` === "Neovim" ```lua -- Neovim >=0.11: vim.lsp.config('ty', { init_options = { logFile = '/path/to/ty.log', }, }) -- Neovim <0.11: require('lspconfig').ty.setup({ init_options = { logFile = '/path/to/ty.log', }, }) ``` === "Zed" ```json { "lsp": { "ty": { "initialization_options": { "logFile": "/path/to/ty.log" } } } } ``` ______________________________________________________________________ ### `logLevel` The log level to use for the language server. **Default value**: `"info"` **Type**: `"trace" | "debug" | "info" | "warn" | "error"` **Example usage**: === "VS Code" ```json { "ty.logLevel": "debug" } ``` === "Neovim" ```lua -- Neovim >=0.11: vim.lsp.config('ty', { init_options = { logLevel = 'debug', }, }) -- Neovim <0.11: require('lspconfig').ty.setup({ init_options = { logLevel = 'debug', }, }) ``` === "Zed" ```json { "lsp": { "ty": { "initialization_options": { "logLevel": "debug" } } } } ``` [ty-vscode]: https://marketplace.visualstudio.com/items?itemName=astral-sh.ty ================================================ FILE: docs/reference/environment.md ================================================ # Environment variables ty defines and respects the following environment variables: ### `TY_CONFIG_FILE` Path to a `ty.toml` configuration file to use. When set, ty will use this file for configuration instead of discovering configuration files automatically. Equivalent to the `--config-file` command-line argument. ### `TY_LOG` If set, ty will use this value as the log level for its `--verbose` output. Accepts any filter compatible with the `tracing_subscriber` crate. For example: - `TY_LOG=ty=debug` is the equivalent of `-vv` to the command line - `TY_LOG=trace` will enable all trace-level logging. See the [tracing documentation](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax) for more. ### `TY_LOG_PROFILE` If set to `"1"` or `"true"`, ty will enable flamegraph profiling. This creates a `tracing.folded` file that can be used to generate flame graphs for performance analysis. ### `TY_MAX_PARALLELISM` Specifies an upper limit for the number of tasks ty is allowed to run in parallel. For example, how many files should be checked in parallel. This isn't the same as a thread limit. ty may spawn additional threads when necessary, e.g. to watch for file system changes or a dedicated UI thread. ### `TY_OUTPUT_FORMAT` The format to use for printing diagnostic messages. When set, ty will use this format for output instead of the default. Accepts the same values as the `--output-format` command-line argument. ## Externally-defined variables ty also reads the following externally defined environment variables: ### `CONDA_DEFAULT_ENV` Used to determine the name of the active Conda environment. ### `CONDA_PREFIX` Used to detect the path of an active Conda environment. If both `VIRTUAL_ENV` and `CONDA_PREFIX` are present, `VIRTUAL_ENV` will be preferred. ### `PYTHONPATH` Adds additional directories to ty's search paths. The format is the same as the shell’s PATH: one or more directory pathnames separated by os appropriate pathsep (e.g. colons on Unix or semicolons on Windows). ### `RAYON_NUM_THREADS` Specifies an upper limit for the number of threads ty uses when performing work in parallel. Equivalent to `TY_MAX_PARALLELISM`. This is a standard Rayon environment variable. ### `VIRTUAL_ENV` Used to detect an activated virtual environment. ### `XDG_CONFIG_HOME` Path to user-level configuration directory on Unix systems. ### `_CONDA_ROOT` Used to determine the root install path of Conda. ================================================ FILE: docs/reference/exit-codes.md ================================================ # Exit codes The ty command line interface uses the following exit codes: | Exit code | Description | | :-------- | :------------------------------------------------------- | | `0` | no violations with severity `error` or higher were found | | `1` | violations with severity `error` or higher were found | | `2` | invalid CLI options, invalid configuration, or IO errors | | `101` | internal error | ty supports two command line arguments that change how exit codes work: - `--exit-zero`: ty will exit with `0` even if violations were found. - `--error-on-warning`: ty will exit with `1` if it finds any violations with severity `warning` or higher. ================================================ FILE: docs/reference/rules.md ================================================ # Rules ## `abstract-method-in-final-class` Default level: error · Added in 0.0.13 · Related issues · View source **What it does** Checks for `@final` classes that have unimplemented abstract methods. **Why is this bad?** A class decorated with `@final` cannot be subclassed. If such a class has abstract methods that are not implemented, the class can never be properly instantiated, as the abstract methods can never be implemented (since subclassing is prohibited). At runtime, instantiation of classes with unimplemented abstract methods is only prevented for classes that have `ABCMeta` (or a subclass of it) as their metaclass. However, type checkers also enforce this for classes that do not use `ABCMeta`, since the intent for the class to be abstract is clear from the use of `@abstractmethod`. **Example** ```python from abc import ABC, abstractmethod from typing import final class Base(ABC): @abstractmethod def method(self) -> int: ... @final class Derived(Base): # Error: `Derived` does not implement `method` pass ``` ## `ambiguous-protocol-member` Default level: warn · Added in 0.0.1-alpha.20 · Related issues · View source **What it does** Checks for protocol classes with members that will lead to ambiguous interfaces. **Why is this bad?** Assigning to an undeclared variable in a protocol class leads to an ambiguous interface which may lead to the type checker inferring unexpected things. It's recommended to ensure that all members of a protocol class are explicitly declared. **Examples** ```py from typing import Protocol class BaseProto(Protocol): a: int # fine (explicitly declared as `int`) def method_member(self) -> int: ... # fine: a method definition using `def` is considered a declaration c = "some variable" # error: no explicit declaration, leading to ambiguity b = method_member # error: no explicit declaration, leading to ambiguity # error: this creates implicit assignments of `d` and `e` in the protocol class body. # Were they really meant to be considered protocol members? for d, e in enumerate(range(42)): pass class SubProto(BaseProto, Protocol): a = 42 # fine (declared in superclass) ``` ## `assert-type-unspellable-subtype` Default level: error · Added in 0.0.14 · Related issues · View source **What it does** Checks for `assert_type()` calls where the actual type is an unspellable subtype of the asserted type. **Why is this bad?** `assert_type()` is intended to ensure that the inferred type of a value is exactly the same as the asserted type. But in some situations, ty has nonstandard extensions to the type system that allow it to infer more precise types than can be expressed in user annotations. ty emits a different error code to [`type-assertion-failure`](#type-assertion-failure) in these situations so that users can easily differentiate between the two cases. **Example** ```python def _(x: int): assert_type(x, int) # fine if x: assert_type(x, int) # error: [assert-type-unspellable-subtype] # the actual type is `int & ~AlwaysFalsy`, # which excludes types like `Literal[0]` ``` ## `byte-string-type-annotation` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for byte-strings in type annotation positions. **Why is this bad?** Static analysis tools like ty can't analyze type annotations that use byte-string notation. **Examples** ```python def test(): -> b"int": ... ``` Use instead: ```python def test(): -> "int": ... ``` ## `call-abstract-method` Default level: error · Preview (since 0.0.16) · Related issues · View source **What it does** Checks for calls to abstract `@classmethod`s or `@staticmethod`s with "trivial bodies" when accessed on the class object itself. "Trivial bodies" are bodies that solely consist of `...`, `pass`, a docstring, and/or `raise NotImplementedError`. **Why is this bad?** An abstract method with a trivial body has no concrete implementation to execute, so calling such a method directly on the class will probably not have the desired effect. It is also unsound to call these methods directly on the class. Unlike other methods, ty permits abstract methods with trivial bodies to have non-`None` return types even though they always return `None` at runtime. This is because it is expected that these methods will always be overridden rather than being called directly. As a result of this exception to the normal rule, ty may infer an incorrect type if one of these methods is called directly, which may then mean that type errors elsewhere in your code go undetected by ty. Calling abstract classmethods or staticmethods via `type[X]` is allowed, since the actual runtime type could be a concrete subclass with an implementation. **Example** ```python from abc import ABC, abstractmethod class Foo(ABC): @classmethod @abstractmethod def method(cls) -> int: ... Foo.method() # Error: cannot call abstract classmethod ``` ## `call-non-callable` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for calls to non-callable objects. **Why is this bad?** Calling a non-callable object will raise a `TypeError` at runtime. **Examples** ```python 4() # TypeError: 'int' object is not callable ``` ## `call-top-callable` Default level: error · Added in 0.0.7 · Related issues · View source **What it does** Checks for calls to objects typed as `Top[Callable[..., T]]` (the infinite union of all callable types with return type `T`). **Why is this bad?** When an object is narrowed to `Top[Callable[..., object]]` (e.g., via `callable(x)` or `isinstance(x, Callable)`), we know the object is callable, but we don't know its precise signature. This type represents the set of all possible callable types (including, e.g., functions that take no arguments and functions that require arguments), so no specific set of arguments can be guaranteed to be valid. **Examples** ```python def f(x: object): if callable(x): x() # error: We know `x` is callable, but not what arguments it accepts ``` ## `conflicting-argument-forms` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks whether an argument is used as both a value and a type form in a call. **Why is this bad?** Such calls have confusing semantics and often indicate a logic error. **Examples** ```python from typing import reveal_type from ty_extensions import is_singleton if flag: f = repr # Expects a value else: f = is_singleton # Expects a type form f(int) # error ``` ## `conflicting-declarations` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks whether a variable has been declared as two conflicting types. **Why is this bad** A variable with two conflicting declarations likely indicates a mistake. Moreover, it could lead to incorrect or ill-defined type inference for other code that relies on these variables. **Examples** ```python if b: a: int else: a: str a = 1 ``` ## `conflicting-metaclass` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for class definitions where the metaclass of the class being created would not be a subclass of the metaclasses of all the class's bases. **Why is it bad?** Such a class definition raises a `TypeError` at runtime. **Examples** ```python class M1(type): ... class M2(type): ... class A(metaclass=M1): ... class B(metaclass=M2): ... # TypeError: metaclass conflict class C(A, B): ... ``` ## `cyclic-class-definition` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for class definitions in stub files that inherit (directly or indirectly) from themselves. **Why is it bad?** Although forward references are natively supported in stub files, inheritance cycles are still disallowed, as it is impossible to resolve a consistent [method resolution order] for a class that inherits from itself. **Examples** ```python # foo.pyi class A(B): ... class B(A): ... ``` [method resolution order]: https://docs.python.org/3/glossary.html#term-method-resolution-order ## `cyclic-type-alias-definition` Default level: error · Added in 0.0.1-alpha.29 · Related issues · View source **What it does** Checks for type alias definitions that (directly or mutually) refer to themselves. **Why is it bad?** Although it is permitted to define a recursive type alias, it is not meaningful to have a type alias whose expansion can only result in itself, and is therefore not allowed. **Examples** ```python type Itself = Itself type A = B type B = A ``` ## `dataclass-field-order` Default level: error · Added in 0.0.15 · Related issues · View source **What it does** Checks for dataclass definitions where required fields are defined after fields with default values. **Why is this bad?** In dataclasses, all required fields (fields without default values) must be defined before fields with default values. This is a Python requirement that will raise a `TypeError` at runtime if violated. **Example** ```python from dataclasses import dataclass @dataclass class Example: x: int = 1 # Field with default value y: str # Error: Required field after field with default ``` ## `deprecated` Default level: warn · Added in 0.0.1-alpha.16 · Related issues · View source **What it does** Checks for uses of deprecated items **Why is this bad?** Deprecated items should no longer be used. **Examples** ```python @warnings.deprecated("use new_func instead") def old_func(): ... old_func() # emits [deprecated] diagnostic ``` ## `division-by-zero` Default level: ignore · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** It detects division by zero. **Why is this bad?** Dividing by zero raises a `ZeroDivisionError` at runtime. **Rule status** This rule is currently disabled by default because of the number of false positives it can produce. **Examples** ```python 5 / 0 ``` ## `duplicate-base` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for class definitions with duplicate bases. **Why is this bad?** Class definitions with duplicate bases raise `TypeError` at runtime. **Examples** ```python class A: ... # TypeError: duplicate base class class B(A, A): ... ``` ## `duplicate-kw-only` Default level: error · Added in 0.0.1-alpha.12 · Related issues · View source **What it does** Checks for dataclass definitions with more than one field annotated with `KW_ONLY`. **Why is this bad?** `dataclasses.KW_ONLY` is a special marker used to emulate the `*` syntax in normal signatures. It can only be used once per dataclass. Attempting to annotate two different fields with it will lead to a runtime error. **Examples** ```python from dataclasses import dataclass, KW_ONLY @dataclass class A: # Crash at runtime b: int _1: KW_ONLY c: str _2: KW_ONLY d: bytes ``` ## `empty-body` Default level: error · Added in 0.0.14 · Related issues · View source **What it does** Detects functions with empty bodies that have a non-`None` return type annotation. The errors reported by this rule have the same motivation as the [`invalid-return-type`](#invalid-return-type) rule. The diagnostic exists as a separate error code to allow users to disable this rule while prototyping code. While we strongly recommend enabling this rule if possible, users migrating from other type checkers may also find it useful to temporarily disable this rule on some or all of their codebase if they find it results in a large number of diagnostics. **Why is this bad?** A function with an empty body (containing only `...`, `pass`, or a docstring) will implicitly return `None` at runtime. Returning `None` when the return type is non-`None` is unsound, and will lead to ty inferring incorrect types elsewhere. Functions with empty bodies are permitted in certain contexts where they serve as declarations rather than implementations: - Functions in stub files (`.pyi`) - Methods in Protocol classes - Abstract methods decorated with `@abstractmethod` - Overload declarations decorated with `@overload` - Functions in `if TYPE_CHECKING` blocks **Examples** ```python def foo() -> int: ... # error: [empty-body] def bar() -> str: """A function that does nothing.""" pass # error: [empty-body] ``` ## `escape-character-in-forward-annotation` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for forward annotations that contain escape characters. **Why is this bad?** Static analysis tools like ty can't analyze type annotations that contain escape characters. **Example** ```python def foo() -> "intt\b": ... ``` ## `final-on-non-method` Default level: error · Added in 0.0.20 · Related issues · View source **What it does** Checks for `@final` decorators applied to non-method functions. **Why is this bad?** The `@final` decorator is only meaningful on methods and classes. Applying it to a module-level function or a nested function has no effect and is likely a mistake. **Example** ```python from typing import final # Error: @final is not allowed on non-method functions @final def my_function() -> int: return 0 ``` ## `final-without-value` Default level: error · Added in 0.0.15 · Related issues · View source **What it does** Checks for `Final` symbols that are declared without a value and are never assigned a value in their scope. **Why is this bad?** A `Final` symbol must be initialized with a value at the time of declaration or in a subsequent assignment. At module or function scope, the assignment must occur in the same scope. In a class body, the assignment may occur in `__init__`. **Examples** ```python from typing import Final # Error: `Final` symbol without a value MY_CONSTANT: Final[int] # OK: `Final` symbol with a value MY_CONSTANT: Final[int] = 1 ``` ## `fstring-type-annotation` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for f-strings in type annotation positions. **Why is this bad?** Static analysis tools like ty can't analyze type annotations that use f-string notation. **Examples** ```python def test(): -> f"int": ... ``` Use instead: ```python def test(): -> "int": ... ``` ## `ignore-comment-unknown-rule` Default level: warn · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for `ty: ignore[code]` where `code` isn't a known lint rule. **Why is this bad?** A `ty: ignore[code]` directive with a `code` that doesn't match any known rule will not suppress any type errors, and is probably a mistake. **Examples** ```py a = 20 / 0 # ty: ignore[division-by-zer] ``` Use instead: ```py a = 20 / 0 # ty: ignore[division-by-zero] ``` ## `implicit-concatenated-string-type-annotation` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for implicit concatenated strings in type annotation positions. **Why is this bad?** Static analysis tools like ty can't analyze type annotations that use implicit concatenated strings. **Examples** ```python def test(): -> "Literal[" "5" "]": ... ``` Use instead: ```python def test(): -> "Literal[5]": ... ``` ## `inconsistent-mro` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for classes with an inconsistent [method resolution order] (MRO). **Why is this bad?** Classes with an inconsistent MRO will raise a `TypeError` at runtime. **Examples** ```python class A: ... class B(A): ... # TypeError: Cannot create a consistent method resolution order class C(A, B): ... ``` [method resolution order]: https://docs.python.org/3/glossary.html#term-method-resolution-order ## `index-out-of-bounds` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for attempts to use an out of bounds index to get an item from a container. **Why is this bad?** Using an out of bounds index will raise an `IndexError` at runtime. **Examples** ```python t = (0, 1, 2) t[3] # IndexError: tuple index out of range ``` ## `ineffective-final` Default level: warn · Added in 0.0.1-alpha.33 · Related issues · View source **What it does** Checks for calls to `final()` that type checkers cannot interpret. **Why is this bad?** The `final()` function is designed to be used as a decorator. When called directly as a function (e.g., `final(type(...))`), type checkers will not understand the application of `final` and will not prevent subclassing. **Example** ```python from typing import final # Incorrect: type checkers will not prevent subclassing MyClass = final(type("MyClass", (), {})) # Correct: use `final` as a decorator @final class MyClass: ... ``` ## `instance-layout-conflict` Default level: error · Added in 0.0.1-alpha.12 · Related issues · View source **What it does** Checks for classes definitions which will fail at runtime due to "instance memory layout conflicts". This error is usually caused by attempting to combine multiple classes that define non-empty `__slots__` in a class's [Method Resolution Order] (MRO), or by attempting to combine multiple builtin classes in a class's MRO. **Why is this bad?** Inheriting from bases with conflicting instance memory layouts will lead to a `TypeError` at runtime. An instance memory layout conflict occurs when CPython cannot determine the memory layout instances of a class should have, because the instance memory layout of one of its bases conflicts with the instance memory layout of one or more of its other bases. For example, if a Python class defines non-empty `__slots__`, this will impact the memory layout of instances of that class. Multiple inheritance from more than one different class defining non-empty `__slots__` is not allowed: ```python class A: __slots__ = ("a", "b") class B: __slots__ = ("a", "b") # Even if the values are the same # TypeError: multiple bases have instance lay-out conflict class C(A, B): ... ``` An instance layout conflict can also be caused by attempting to use multiple inheritance with two builtin classes, due to the way that these classes are implemented in a CPython C extension: ```python class A(int, float): ... # TypeError: multiple bases have instance lay-out conflict ``` Note that pure-Python classes with no `__slots__`, or pure-Python classes with empty `__slots__`, are always compatible: ```python class A: ... class B: __slots__ = () class C: __slots__ = ("a", "b") # fine class D(A, B, C): ... ``` **Known problems** Classes that have "dynamic" definitions of `__slots__` (definitions do not consist of string literals, or tuples of string literals) are not currently considered disjoint bases by ty. Additionally, this check is not exhaustive: many C extensions (including several in the standard library) define classes that use extended memory layouts and thus cannot coexist in a single MRO. Since it is currently not possible to represent this fact in stub files, having a full knowledge of these classes is also impossible. When it comes to classes that do not define `__slots__` at the Python level, therefore, ty, currently only hard-codes a number of cases where it knows that a class will produce instances with an atypical memory layout. **Further reading** - [CPython documentation: `__slots__`](https://docs.python.org/3/reference/datamodel.html#slots) - [CPython documentation: Method Resolution Order](https://docs.python.org/3/glossary.html#term-method-resolution-order) [Method Resolution Order]: https://docs.python.org/3/glossary.html#term-method-resolution-order ## `invalid-argument-type` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Detects call arguments whose type is not assignable to the corresponding typed parameter. **Why is this bad?** Passing an argument of a type the function (or callable object) does not accept violates the expectations of the function author and may cause unexpected runtime errors within the body of the function. **Examples** ```python def func(x: int): ... func("foo") # error: [invalid-argument-type] ``` ## `invalid-assignment` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for assignments where the type of the value is not [assignable to] the type of the assignee. **Why is this bad?** Such assignments break the rules of the type system and weaken a type checker's ability to accurately reason about your code. **Examples** ```python a: int = '' ``` [assignable to]: https://typing.python.org/en/latest/spec/glossary.html#term-assignable ## `invalid-attribute-access` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for assignments to class variables from instances and assignments to instance variables from its class. **Why is this bad?** Incorrect assignments break the rules of the type system and weaken a type checker's ability to accurately reason about your code. **Examples** ```python class C: class_var: ClassVar[int] = 1 instance_var: int C.class_var = 3 # okay C().class_var = 3 # error: Cannot assign to class variable C().instance_var = 3 # okay C.instance_var = 3 # error: Cannot assign to instance variable ``` ## `invalid-await` Default level: error · Added in 0.0.1-alpha.19 · Related issues · View source **What it does** Checks for `await` being used with types that are not [Awaitable]. **Why is this bad?** Such expressions will lead to `TypeError` being raised at runtime. **Examples** ```python import asyncio class InvalidAwait: def __await__(self) -> int: return 5 async def main() -> None: await InvalidAwait() # error: [invalid-await] await 42 # error: [invalid-await] asyncio.run(main()) ``` [Awaitable]: https://docs.python.org/3/library/collections.abc.html#collections.abc.Awaitable ## `invalid-base` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for class definitions that have bases which are not instances of `type`. **Why is this bad?** Class definitions with bases like this will lead to `TypeError` being raised at runtime. **Examples** ```python class A(42): ... # error: [invalid-base] ``` ## `invalid-context-manager` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for expressions used in `with` statements that do not implement the context manager protocol. **Why is this bad?** Such a statement will raise `TypeError` at runtime. **Examples** ```python # TypeError: 'int' object does not support the context manager protocol with 1: print(2) ``` ## `invalid-dataclass` Default level: error · Added in 0.0.12 · Related issues · View source **What it does** Checks for invalid applications of the `@dataclass` decorator. **Why is this bad?** Applying `@dataclass` to a class that inherits from `NamedTuple`, `TypedDict`, `Enum`, or `Protocol` is invalid: - `NamedTuple` and `TypedDict` classes will raise an exception at runtime when instantiating the class. - `Enum` classes with `@dataclass` are [explicitly not supported]. - `Protocol` classes define interfaces and cannot be instantiated. **Examples** ```python from dataclasses import dataclass from typing import NamedTuple @dataclass # error: [invalid-dataclass] class Foo(NamedTuple): x: int ``` [explicitly not supported]: https://docs.python.org/3/howto/enum.html#dataclass-support ## `invalid-dataclass-override` Default level: error · Added in 0.0.13 · Related issues · View source **What it does** Checks for dataclass definitions that have both `frozen=True` and a custom `__setattr__` or `__delattr__` method defined. **Why is this bad?** Frozen dataclasses synthesize `__setattr__` and `__delattr__` methods which raise a `FrozenInstanceError` to emulate immutability. Overriding either of these methods raises a runtime error. **Examples** ```python from dataclasses import dataclass @dataclass(frozen=True) class A: def __setattr__(self, name: str, value: object) -> None: ... ``` ## `invalid-declaration` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for declarations where the inferred type of an existing symbol is not [assignable to] its post-hoc declared type. **Why is this bad?** Such declarations break the rules of the type system and weaken a type checker's ability to accurately reason about your code. **Examples** ```python a = 1 a: str ``` [assignable to]: https://typing.python.org/en/latest/spec/glossary.html#term-assignable ## `invalid-enum-member-annotation` Default level: warn · Added in 0.0.20 · Related issues · View source **What it does** Checks for enum members that have explicit type annotations. **Why is this bad?** The [typing spec] states that type checkers should infer a literal type for all enum members. An explicit type annotation on an enum member is misleading because the annotated type will be incorrect — the actual runtime type is the enum class itself, not the annotated type. In CPython's `enum` module, annotated assignments with values are still treated as members at runtime, but the annotation will confuse readers of the code. **Examples** ```python from enum import Enum class Pet(Enum): CAT = 1 # OK DOG: int = 2 # Error: enum members should not be annotated ``` Use instead: ```python from enum import Enum class Pet(Enum): CAT = 1 DOG = 2 ``` **References** - [Typing spec: Enum members](https://typing.python.org/en/latest/spec/enums.html#enum-members) [typing spec]: https://typing.python.org/en/latest/spec/enums.html#enum-members ## `invalid-exception-caught` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for exception handlers that catch non-exception classes. **Why is this bad?** Catching classes that do not inherit from `BaseException` will raise a `TypeError` at runtime. **Example** ```python try: 1 / 0 except 1: ... ``` Use instead: ```python try: 1 / 0 except ZeroDivisionError: ... ``` **References** - [Python documentation: except clause](https://docs.python.org/3/reference/compound_stmts.html#except-clause) - [Python documentation: Built-in Exceptions](https://docs.python.org/3/library/exceptions.html#built-in-exceptions) **Ruff rule** This rule corresponds to Ruff's [`except-with-non-exception-classes` (`B030`)](https://docs.astral.sh/ruff/rules/except-with-non-exception-classes) ## `invalid-explicit-override` Default level: error · Added in 0.0.1-alpha.28 · Related issues · View source **What it does** Checks for methods that are decorated with `@override` but do not override any method in a superclass. **Why is this bad?** Decorating a method with `@override` declares to the type checker that the intention is that it should override a method from a superclass. **Example** ```python from typing import override class A: @override def foo(self): ... # Error raised here class B(A): @override def ffooo(self): ... # Error raised here class C: @override def __repr__(self): ... # fine: overrides `object.__repr__` class D(A): @override def foo(self): ... # fine: overrides `A.foo` ``` ## `invalid-frozen-dataclass-subclass` Default level: error · Added in 0.0.1-alpha.35 · Related issues · View source **What it does** Checks for dataclasses with invalid frozen inheritance: - A frozen dataclass cannot inherit from a non-frozen dataclass. - A non-frozen dataclass cannot inherit from a frozen dataclass. **Why is this bad?** Python raises a `TypeError` at runtime when either of these inheritance patterns occurs. **Example** ```python from dataclasses import dataclass @dataclass class Base: x: int @dataclass(frozen=True) class Child(Base): # Error raised here y: int @dataclass(frozen=True) class FrozenBase: x: int @dataclass class NonFrozenChild(FrozenBase): # Error raised here y: int ``` ## `invalid-generic-class` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for the creation of invalid generic classes **Why is this bad?** There are several requirements that you must follow when defining a generic class. Many of these result in `TypeError` being raised at runtime if they are violated. **Examples** ```python from typing_extensions import Generic, TypeVar T = TypeVar("T") U = TypeVar("U", default=int) # error: class uses both PEP-695 syntax and legacy syntax class C[U](Generic[T]): ... # error: type parameter with default comes before type parameter without default class D(Generic[U, T]): ... ``` **References** - [Typing spec: Generics](https://typing.python.org/en/latest/spec/generics.html#introduction) ## `invalid-generic-enum` Default level: error · Added in 0.0.12 · Related issues · View source **What it does** Checks for enum classes that are also generic. **Why is this bad?** Enum classes cannot be generic. Python does not support generic enums: attempting to create one will either result in an immediate `TypeError` at runtime, or will create a class that cannot be specialized in the way that a normal generic class can. **Examples** ```python from enum import Enum from typing import Generic, TypeVar T = TypeVar("T") # error: enum class cannot be generic (class creation fails with `TypeError`) class E[T](Enum): A = 1 # error: enum class cannot be generic (class creation fails with `TypeError`) class F(Enum, Generic[T]): A = 1 # error: enum class cannot be generic -- the class creation does not immediately fail... class G(Generic[T], Enum): A = 1 # ...but this raises `KeyError`: x: G[int] ``` **References** - [Python documentation: Enum](https://docs.python.org/3/library/enum.html) ## `invalid-ignore-comment` Default level: warn · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for `type: ignore` and `ty: ignore` comments that are syntactically incorrect. **Why is this bad?** A syntactically incorrect ignore comment is probably a mistake and is useless. **Examples** ```py a = 20 / 0 # type: ignoree ``` Use instead: ```py a = 20 / 0 # type: ignore ``` ## `invalid-key` Default level: error · Added in 0.0.1-alpha.17 · Related issues · View source **What it does** Checks for subscript accesses with invalid keys and `TypedDict` construction with an unknown key. **Why is this bad?** Subscripting with an invalid key will raise a `KeyError` at runtime. Creating a `TypedDict` with an unknown key is likely a mistake; if the `TypedDict` is `closed=true` it also violates the expectations of the type. **Examples** ```python from typing import TypedDict class Person(TypedDict): name: str age: int alice = Person(name="Alice", age=30) alice["height"] # KeyError: 'height' bob: Person = { "namee": "Bob", "age": 30 } # typo! carol = Person(name="Carol", aeg=25) # typo! ``` ## `invalid-legacy-positional-parameter` Default level: warn · Added in 0.0.15 · Related issues · View source **What it does** Checks for parameters that appear to be attempting to use the legacy convention to specify that a parameter is positional-only, but do so incorrectly. The "legacy convention" for specifying positional-only parameters was specified in [PEP 484]. It states that parameters with names starting with `__` should be considered positional-only by type checkers. [PEP 570], introduced in Python 3.8, added dedicated syntax for specifying positional-only parameters, rendering the legacy convention obsolete. However, some codebases may still use the legacy convention for compatibility with older Python versions. **Why is this bad?** In most cases, a type checker will not consider a parameter to be positional-only if it comes after a positional-or-keyword parameter, even if its name starts with `__`. This may be unexpected to the author of the code. **Example** ```python def f(x, __y): # Error: `__y` is not considered positional-only pass ``` Use instead: ```python def f(__x, __y): # If you need compatibility with Python <=3.7 pass ``` or: ```python def f(x, y, /): # Python 3.8+ syntax pass ``` **References** - [Typing spec: positional-only parameters (legacy syntax)](https://typing.python.org/en/latest/spec/historical.html#pos-only-double-underscore) - [Python glossary: parameters](https://docs.python.org/3/glossary.html#term-parameter) [PEP 484]: https://peps.python.org/pep-0484/#positional-only-arguments [PEP 570]: https://peps.python.org/pep-0570/ ## `invalid-legacy-type-variable` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for the creation of invalid legacy `TypeVar`s **Why is this bad?** There are several requirements that you must follow when creating a legacy `TypeVar`. **Examples** ```python from typing import TypeVar T = TypeVar("T") # okay Q = TypeVar("S") # error: TypeVar name must match the variable it's assigned to T = TypeVar("T") # error: TypeVars should not be redefined # error: TypeVar must be immediately assigned to a variable def f(t: TypeVar("U")): ... ``` **References** - [Typing spec: Generics](https://typing.python.org/en/latest/spec/generics.html#introduction) ## `invalid-match-pattern` Default level: error · Added in 0.0.18 · Related issues · View source **What it does** Checks for invalid match patterns. **Why is this bad?** Matching on invalid patterns will lead to a runtime error. **Examples** ```python NotAClass = 42 match x: case NotAClass(): # TypeError at runtime: must be a class ... ``` ## `invalid-metaclass` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for arguments to `metaclass=` that are invalid. **Why is this bad?** Python allows arbitrary expressions to be used as the argument to `metaclass=`. These expressions, however, need to be callable and accept the same arguments as `type.__new__`. **Example** ```python def f(): ... # TypeError: f() takes 0 positional arguments but 3 were given class B(metaclass=f): ... ``` **References** - [Python documentation: Metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses) ## `invalid-method-override` Default level: error · Added in 0.0.1-alpha.20 · Related issues · View source **What it does** Detects method overrides that violate the [Liskov Substitution Principle] ("LSP"). The LSP states that an instance of a subtype should be substitutable for an instance of its supertype. Applied to Python, this means: 1. All argument combinations a superclass method accepts must also be accepted by an overriding subclass method. 2. The return type of an overriding subclass method must be a subtype of the return type of the superclass method. **Why is this bad?** Violating the Liskov Substitution Principle will lead to many of ty's assumptions and inferences being incorrect, which will mean that it will fail to catch many possible type errors in your code. **Example** ```python class Super: def method(self, x) -> int: return 42 class Sub(Super): # Liskov violation: `str` is not a subtype of `int`, # but the supertype method promises to return an `int`. def method(self, x) -> str: # error: [invalid-override] return "foo" def accepts_super(s: Super) -> int: return s.method(x=42) accepts_super(Sub()) # The result of this call is a string, but ty will infer # it to be an `int` due to the violation of the Liskov Substitution Principle. class Sub2(Super): # Liskov violation: the superclass method can be called with a `x=` # keyword argument, but the subclass method does not accept it. def method(self, y) -> int: # error: [invalid-override] return 42 accepts_super(Sub2()) # TypeError at runtime: method() got an unexpected keyword argument 'x' # ty cannot catch this error due to the violation of the Liskov Substitution Principle. ``` **Common issues** **Why does ty complain about my `__eq__` method?** `__eq__` and `__ne__` methods in Python are generally expected to accept arbitrary objects as their second argument, for example: ```python class A: x: int def __eq__(self, other: object) -> bool: # gracefully handle an object of an unexpected type # without raising an exception if not isinstance(other, A): return False return self.x == other.x ``` If `A.__eq__` here were annotated as only accepting `A` instances for its second argument, it would imply that you wouldn't be able to use `==` between instances of `A` and instances of unrelated classes without an exception possibly being raised. While some classes in Python do indeed behave this way, the strongly held convention is that it should be avoided wherever possible. As part of this check, therefore, ty enforces that `__eq__` and `__ne__` methods accept `object` as their second argument. **Why does ty disagree with Ruff about how to write my method?** Ruff has several rules that will encourage you to rename a parameter, or change its type signature, if it thinks you're falling into a certain anti-pattern. For example, Ruff's [ARG002](https://docs.astral.sh/ruff/rules/unused-method-argument/) rule recommends that an unused parameter should either be removed or renamed to start with `_`. Applying either of these suggestions can cause ty to start reporting an [`invalid-method-override`](#invalid-method-override) error if the function in question is a method on a subclass that overrides a method on a superclass, and the change would cause the subclass method to no longer accept all argument combinations that the superclass method accepts. This can usually be resolved by adding [`@typing.override`][override] to your method definition. Ruff knows that a method decorated with `@typing.override` is intended to override a method by the same name on a superclass, and avoids reporting rules like ARG002 for such methods; it knows that the changes recommended by ARG002 would violate the Liskov Substitution Principle. Correct use of `@override` is enforced by ty's [`invalid-explicit-override`](#invalid-explicit-override) rule. [Liskov Substitution Principle]: https://en.wikipedia.org/wiki/Liskov_substitution_principle [override]: https://docs.python.org/3/library/typing.html#typing.override ## `invalid-named-tuple` Default level: error · Added in 0.0.1-alpha.19 · Related issues · View source **What it does** Checks for invalidly defined `NamedTuple` classes. **Why is this bad?** An invalidly defined `NamedTuple` class may lead to the type checker drawing incorrect conclusions. It may also lead to `TypeError`s or `AttributeError`s at runtime. **Examples** A class definition cannot combine `NamedTuple` with other base classes in multiple inheritance; doing so raises a `TypeError` at runtime. The sole exception to this rule is `Generic[]`, which can be used alongside `NamedTuple` in a class's bases list. ```pycon >>> from typing import NamedTuple >>> class Foo(NamedTuple, object): ... TypeError: can only inherit from a NamedTuple type and Generic ``` Further, `NamedTuple` field names cannot start with an underscore: ```pycon >>> from typing import NamedTuple >>> class Foo(NamedTuple): ... _bar: int ValueError: Field names cannot start with an underscore: '_bar' ``` `NamedTuple` classes also have certain synthesized attributes (like `_asdict`, `_make`, `_replace`, etc.) that cannot be overwritten. Attempting to assign to these attributes without a type annotation will raise an `AttributeError` at runtime. ```pycon >>> from typing import NamedTuple >>> class Foo(NamedTuple): ... x: int ... _asdict = 42 AttributeError: Cannot overwrite NamedTuple attribute _asdict ``` ## `invalid-newtype` Default level: error · Added in 0.0.1-alpha.27 · Related issues · View source **What it does** Checks for the creation of invalid `NewType`s **Why is this bad?** There are several requirements that you must follow when creating a `NewType`. **Examples** ```python from typing import NewType def get_name() -> str: ... Foo = NewType("Foo", int) # okay Bar = NewType(get_name(), int) # error: The first argument to `NewType` must be a string literal Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType` ``` ## `invalid-overload` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for various invalid `@overload` usages. **Why is this bad?** The `@overload` decorator is used to define functions and methods that accepts different combinations of arguments and return different types based on the arguments passed. This is mainly beneficial for type checkers. But, if the `@overload` usage is invalid, the type checker may not be able to provide correct type information. **Example** Defining only one overload: ```py from typing import overload @overload def foo(x: int) -> int: ... def foo(x: int | None) -> int | None: return x ``` Or, not providing an implementation for the overloaded definition: ```py from typing import overload @overload def foo() -> None: ... @overload def foo(x: int) -> int: ... ``` **References** - [Python documentation: `@overload`](https://docs.python.org/3/library/typing.html#typing.overload) ## `invalid-parameter-default` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for default values that can't be assigned to the parameter's annotated type. **Why is this bad?** This breaks the rules of the type system and weakens a type checker's ability to accurately reason about your code. **Examples** ```python def f(a: int = ''): ... ``` ## `invalid-paramspec` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for the creation of invalid `ParamSpec`s **Why is this bad?** There are several requirements that you must follow when creating a `ParamSpec`. **Examples** ```python from typing import ParamSpec P1 = ParamSpec("P1") # okay P2 = ParamSpec("S2") # error: ParamSpec name must match the variable it's assigned to ``` **References** - [Typing spec: ParamSpec](https://typing.python.org/en/latest/spec/generics.html#paramspec) ## `invalid-protocol` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for protocol classes that will raise `TypeError` at runtime. **Why is this bad?** An invalidly defined protocol class may lead to the type checker inferring unexpected things. It may also lead to `TypeError`s at runtime. **Examples** A `Protocol` class cannot inherit from a non-`Protocol` class; this raises a `TypeError` at runtime: ```pycon >>> from typing import Protocol >>> class Foo(int, Protocol): ... ... Traceback (most recent call last): File "", line 1, in class Foo(int, Protocol): ... TypeError: Protocols can only inherit from other protocols, got ``` ## `invalid-raise` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source Checks for `raise` statements that raise non-exceptions or use invalid causes for their raised exceptions. **Why is this bad?** Only subclasses or instances of `BaseException` can be raised. For an exception's cause, the same rules apply, except that `None` is also permitted. Violating these rules results in a `TypeError` at runtime. **Examples** ```python def f(): try: something() except NameError: raise "oops!" from f def g(): raise NotImplemented from 42 ``` Use instead: ```python def f(): try: something() except NameError as e: raise RuntimeError("oops!") from e def g(): raise NotImplementedError from None ``` **References** - [Python documentation: The `raise` statement](https://docs.python.org/3/reference/simple_stmts.html#raise) - [Python documentation: Built-in Exceptions](https://docs.python.org/3/library/exceptions.html#built-in-exceptions) ## `invalid-return-type` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Detects returned values that can't be assigned to the function's annotated return type. Note that the special case of a function with a non-`None` return type and an empty body is handled by the separate [`empty-body`](#empty-body) error code. **Why is this bad?** Returning an object of a type incompatible with the annotated return type is unsound, and will lead to ty inferring incorrect types elsewhere. **Examples** ```python def func() -> int: return "a" # error: [invalid-return-type] ``` ## `invalid-super-argument` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Detects `super()` calls where: - the first argument is not a valid class literal, or - the second argument is not an instance or subclass of the first argument. **Why is this bad?** `super(type, obj)` expects: - the first argument to be a class, - and the second argument to satisfy one of the following: - `isinstance(obj, type)` is `True` - `issubclass(obj, type)` is `True` Violating this relationship will raise a `TypeError` at runtime. **Examples** ```python class A: ... class B(A): ... super(A, B()) # it's okay! `A` satisfies `isinstance(B(), A)` super(A(), B()) # error: `A()` is not a class super(B, A()) # error: `A()` does not satisfy `isinstance(A(), B)` super(B, A) # error: `A` does not satisfy `issubclass(A, B)` ``` **References** - [Python documentation: super()](https://docs.python.org/3/library/functions.html#super) ## `invalid-syntax-in-forward-annotation` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for string-literal annotations where the string cannot be parsed as a Python expression. **Why is this bad?** Type annotations are expected to be Python expressions that describe the expected type of a variable, parameter, attribute or `return` statement. Type annotations are permitted to be string-literal expressions, in order to enable forward references to names not yet defined. However, it must be possible to parse the contents of that string literal as a normal Python expression. **Example** ```python def foo() -> "intstance of C": return 42 class C: ... ``` Use instead: ```python def foo() -> "C": return 42 class C: ... ``` **References** - [Typing spec: The meaning of annotations](https://typing.python.org/en/latest/spec/annotations.html#the-meaning-of-annotations) - [Typing spec: String annotations](https://typing.python.org/en/latest/spec/annotations.html#string-annotations) ## `invalid-total-ordering` Default level: error · Added in 0.0.10 · Related issues · View source **What it does** Checks for classes decorated with `@functools.total_ordering` that don't define any ordering method (`__lt__`, `__le__`, `__gt__`, or `__ge__`). **Why is this bad?** The `@total_ordering` decorator requires the class to define at least one ordering method. If none is defined, Python raises a `ValueError` at runtime. **Example** ```python from functools import total_ordering @total_ordering class MyClass: # Error: no ordering method defined def __eq__(self, other: object) -> bool: return True ``` Use instead: ```python from functools import total_ordering @total_ordering class MyClass: def __eq__(self, other: object) -> bool: return True def __lt__(self, other: "MyClass") -> bool: return True ``` ## `invalid-type-alias-type` Default level: error · Added in 0.0.1-alpha.6 · Related issues · View source **What it does** Checks for the creation of invalid `TypeAliasType`s **Why is this bad?** There are several requirements that you must follow when creating a `TypeAliasType`. **Examples** ```python from typing import TypeAliasType IntOrStr = TypeAliasType("IntOrStr", int | str) # okay NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name must be a string literal ``` ## `invalid-type-arguments` Default level: error · Added in 0.0.1-alpha.29 · Related issues · View source **What it does** Checks for invalid type arguments in explicit type specialization. **Why is this bad?** Providing the wrong number of type arguments or type arguments that don't satisfy the type variable's bounds or constraints will lead to incorrect type inference and may indicate a misunderstanding of the generic type's interface. **Examples** Using legacy type variables: ```python from typing import Generic, TypeVar T1 = TypeVar('T1', int, str) T2 = TypeVar('T2', bound=int) class Foo1(Generic[T1]): ... class Foo2(Generic[T2]): ... Foo1[bytes] # error: bytes does not satisfy T1's constraints Foo2[str] # error: str does not satisfy T2's bound ``` Using PEP 695 type variables: ```python class Foo[T]: ... class Bar[T, U]: ... Foo[int, str] # error: too many arguments Bar[int] # error: too few arguments ``` ## `invalid-type-checking-constant` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for a value other than `False` assigned to the `TYPE_CHECKING` variable, or an annotation not assignable from `bool`. **Why is this bad?** The name `TYPE_CHECKING` is reserved for a flag that can be used to provide conditional code seen only by the type checker, and not at runtime. Normally this flag is imported from `typing` or `typing_extensions`, but it can also be defined locally. If defined locally, it must be assigned the value `False` at runtime; the type checker will consider its value to be `True`. If annotated, it must be annotated as a type that can accept `bool` values. **Examples** ```python TYPE_CHECKING: str TYPE_CHECKING = '' ``` ## `invalid-type-form` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for expressions that are used as [type expressions] but cannot validly be interpreted as such. **Why is this bad?** Such expressions cannot be understood by ty. In some cases, they might raise errors at runtime. **Examples** ```python from typing import Annotated a: type[1] # `1` is not a type b: Annotated[int] # `Annotated` expects at least two arguments ``` [type expressions]: https://typing.python.org/en/latest/spec/annotations.html#type-and-annotation-expressions ## `invalid-type-guard-call` Default level: error · Added in 0.0.1-alpha.11 · Related issues · View source **What it does** Checks for type guard function calls without a valid target. **Why is this bad?** The first non-keyword non-variadic argument to a type guard function is its target and must map to a symbol. Starred (`is_str(*a)`), literal (`is_str(42)`) and other non-symbol-like expressions are invalid as narrowing targets. **Examples** ```python from typing import TypeIs def f(v: object) -> TypeIs[int]: ... f() # Error f(*a) # Error f(10) # Error ``` ## `invalid-type-guard-definition` Default level: error · Added in 0.0.1-alpha.11 · Related issues · View source **What it does** Checks for type guard functions without a first non-self-like non-keyword-only non-variadic parameter. **Why is this bad?** Type narrowing functions must accept at least one positional argument (non-static methods must accept another in addition to `self`/`cls`). Extra parameters/arguments are allowed but do not affect narrowing. **Examples** ```python from typing import TypeIs def f() -> TypeIs[int]: ... # Error, no parameter def f(*, v: object) -> TypeIs[int]: ... # Error, no positional arguments allowed def f(*args: object) -> TypeIs[int]: ... # Error, expect variadic arguments class C: def f(self) -> TypeIs[int]: ... # Error, only positional argument expected is `self` ``` ## `invalid-type-variable-bound` Default level: error · Added in 0.0.15 · Related issues · View source **What it does** Checks for [type variables] whose bounds reference type variables. **Why is this bad?** The bound of a type variable must be a concrete type. **Examples** ```python T = TypeVar('T', bound=list['T']) # error: [invalid-type-variable-bound] U = TypeVar('U') T = TypeVar('T', bound=U) # error: [invalid-type-variable-bound] def f[T: list[T]](): ... # error: [invalid-type-variable-bound] def g[U, T: U](): ... # error: [invalid-type-variable-bound] ``` [type variable]: https://docs.python.org/3/library/typing.html#typing.TypeVar ## `invalid-type-variable-constraints` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for constrained [type variables] with only one constraint, or that those constraints reference type variables. **Why is this bad?** A constrained type variable must have at least two constraints. **Examples** ```python from typing import TypeVar T = TypeVar('T', str) # invalid constrained TypeVar I = TypeVar('I', bound=int) U = TypeVar('U', list[I], int) # invalid constrained TypeVar ``` Use instead: ```python T = TypeVar('T', str, int) # valid constrained TypeVar # or T = TypeVar('T', bound=str) # valid bound TypeVar U = TypeVar('U', list[int], int) # valid constrained Type ``` [type variables]: https://docs.python.org/3/library/typing.html#typing.TypeVar ## `invalid-type-variable-default` Default level: error · Added in 0.0.16 · Related issues · View source **What it does** Checks for [type variables] whose default type is not compatible with the type variable's bound or constraints. **Why is this bad?** If a type variable has a bound, the default must be assignable to that bound (see: [bound rules]). If a type variable has constraints, the default must be one of the constraints (see: [constraint rules]). **Examples** ```python T = TypeVar("T", bound=str, default=int) # error: [invalid-type-variable-default] U = TypeVar("U", int, str, default=bytes) # error: [invalid-type-variable-default] ``` [type variables]: https://docs.python.org/3/library/typing.html#typing.TypeVar [bound rules]: https://typing.python.org/en/latest/spec/generics.html#bound-rules [constraint rules]: https://typing.python.org/en/latest/spec/generics.html#constraint-rules ## `invalid-typed-dict-header` Default level: error · Added in 0.0.14 · Related issues · View source **What it does** Detects errors in `TypedDict` class headers, such as unexpected arguments or invalid base classes. **Why is this bad?** The typing spec states that `TypedDict`s are not permitted to have custom metaclasses. Using `**` unpacking in a `TypedDict` header is also prohibited by ty, as it means that ty cannot statically determine whether keys in the `TypedDict` are intended to be required or optional. **Example** ```python from typing import TypedDict class Foo(TypedDict, metaclass=whatever): # error: [invalid-typed-dict-header] ... def f(x: dict): class Bar(TypedDict, **x): # error: [invalid-typed-dict-header] ... ``` ## `invalid-typed-dict-statement` Default level: error · Added in 0.0.9 · Related issues · View source **What it does** Detects statements other than annotated declarations in `TypedDict` class bodies. **Why is this bad?** `TypedDict` class bodies aren't allowed to contain any other types of statements. For example, method definitions and field values aren't allowed. None of these will be available on "instances of the `TypedDict`" at runtime (as `dict` is the runtime class of all "`TypedDict` instances"). **Example** ```python from typing import TypedDict class Foo(TypedDict): def bar(self): # error: [invalid-typed-dict-statement] pass ``` ## `isinstance-against-protocol` Default level: error · Added in 0.0.14 · Related issues · View source **What it does** Reports invalid runtime checks against `Protocol` classes. This includes explicit calls `isinstance()`/`issubclass()` against non-runtime-checkable protocols, `issubclass()` calls against protocols that have non-method members, and implicit `isinstance()` checks against non-runtime-checkable protocols via pattern matching. **Why is this bad?** These calls (implicit or explicit) raise `TypeError` at runtime. **Examples** ```python from typing_extensions import Protocol, runtime_checkable class HasX(Protocol): x: int @runtime_checkable class HasY(Protocol): y: int def f(arg: object, arg2: type): isinstance(arg, HasX) # error: [isinstance-against-protocol] (not runtime-checkable) issubclass(arg2, HasX) # error: [isinstance-against-protocol] (not runtime-checkable) def g(arg: object): match arg: case HasX(): # error: [isinstance-against-protocol] (not runtime-checkable) pass def h(arg2: type): isinstance(arg2, HasY) # fine (runtime-checkable) # `HasY` is runtime-checkable, but has non-method members, # so it still can't be used in `issubclass` checks) issubclass(arg2, HasY) # error: [isinstance-against-protocol] ``` **References** - [Typing documentation: `@runtime_checkable`](https://docs.python.org/3/library/typing.html#typing.runtime_checkable) ## `isinstance-against-typed-dict` Default level: error · Added in 0.0.15 · Related issues · View source **What it does** Reports runtime checks against `TypedDict` classes. This includes explicit calls to `isinstance()`/`issubclass()` and implicit checks performed by `match` class patterns. **Why is this bad?** Using a `TypedDict` class in these contexts raises `TypeError` at runtime. **Examples** ```python from typing_extensions import TypedDict class Movie(TypedDict): name: str director: str def f(arg: object, arg2: type): isinstance(arg, Movie) # error: [isinstance-against-typed-dict] issubclass(arg2, Movie) # error: [isinstance-against-typed-dict] def g(arg: object): match arg: case Movie(): # error: [isinstance-against-typed-dict] pass ``` **References** - [Typing specification: `TypedDict`](https://typing.python.org/en/latest/spec/typeddict.html) ## `missing-argument` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for missing required arguments in a call. **Why is this bad?** Failing to provide a required argument will raise a `TypeError` at runtime. **Examples** ```python def func(x: int): ... func() # TypeError: func() missing 1 required positional argument: 'x' ``` ## `missing-typed-dict-key` Default level: error · Added in 0.0.1-alpha.20 · Related issues · View source **What it does** Detects missing required keys in `TypedDict` constructor calls. **Why is this bad?** `TypedDict` requires all non-optional keys to be provided during construction. Missing items can lead to a `KeyError` at runtime. **Example** ```python from typing import TypedDict class Person(TypedDict): name: str age: int alice: Person = {"name": "Alice"} # missing required key 'age' alice["age"] # KeyError ``` ## `no-matching-overload` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for calls to an overloaded function that do not match any of the overloads. **Why is this bad?** Failing to provide the correct arguments to one of the overloads will raise a `TypeError` at runtime. **Examples** ```python @overload def func(x: int): ... @overload def func(x: bool): ... func("string") # error: [no-matching-overload] ``` ## `not-iterable` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for objects that are not iterable but are used in a context that requires them to be. **Why is this bad?** Iterating over an object that is not iterable will raise a `TypeError` at runtime. **Examples** ```python for i in 34: # TypeError: 'int' object is not iterable pass ``` ## `not-subscriptable` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for subscripting objects that do not support subscripting. **Why is this bad?** Subscripting an object that does not support it will raise a `TypeError` at runtime. **Examples** ```python 4[1] # TypeError: 'int' object is not subscriptable ``` ## `override-of-final-method` Default level: error · Added in 0.0.1-alpha.29 · Related issues · View source **What it does** Checks for methods on subclasses that override superclass methods decorated with `@final`. **Why is this bad?** Decorating a method with `@final` declares to the type checker that it should not be overridden on any subclass. **Example** ```python from typing import final class A: @final def foo(self): ... class B(A): def foo(self): ... # Error raised here ``` ## `override-of-final-variable` Default level: error · Added in 0.0.16 · Related issues · View source **What it does** Checks for class variables on subclasses that override a superclass variable that has been declared as `Final`. **Why is this bad?** Declaring a variable as `Final` indicates to the type checker that it should not be overridden on any subclass. **Example** ```python from typing import Final class A: X: Final[int] = 1 class B(A): X = 2 # Error raised here ``` ## `parameter-already-assigned` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for calls which provide more than one argument for a single parameter. **Why is this bad?** Providing multiple values for a single parameter will raise a `TypeError` at runtime. **Examples** ```python def f(x: int) -> int: ... f(1, x=2) # Error raised here ``` ## `positional-only-parameter-as-kwarg` Default level: error · Added in 0.0.1-alpha.22 · Related issues · View source **What it does** Checks for keyword arguments in calls that match positional-only parameters of the callable. **Why is this bad?** Providing a positional-only parameter as a keyword argument will raise `TypeError` at runtime. **Example** ```python def f(x: int, /) -> int: ... f(x=1) # Error raised here ``` ## `possibly-missing-attribute` Default level: ignore · Added in 0.0.1-alpha.22 · Related issues · View source **What it does** Checks for possibly missing attributes. **Why is this bad?** Attempting to access a missing attribute will raise an `AttributeError` at runtime. **Rule status** This rule is currently disabled by default because of the number of false positives it can produce. **Examples** ```python class A: if b: c = 0 A.c # AttributeError: type object 'A' has no attribute 'c' ``` ## `possibly-missing-implicit-call` Default level: warn · Added in 0.0.1-alpha.22 · Related issues · View source **What it does** Checks for implicit calls to possibly missing methods. **Why is this bad?** Expressions such as `x[y]` and `x * y` call methods under the hood (`__getitem__` and `__mul__` respectively). Calling a missing method will raise an `AttributeError` at runtime. **Examples** ```python import datetime class A: if datetime.date.today().weekday() != 6: def __getitem__(self, v): ... A()[0] # TypeError: 'A' object is not subscriptable ``` ## `possibly-missing-import` Default level: ignore · Added in 0.0.1-alpha.22 · Related issues · View source **What it does** Checks for imports of symbols that may be missing. **Why is this bad?** Importing a missing module or name will raise a `ModuleNotFoundError` or `ImportError` at runtime. **Rule status** This rule is currently disabled by default because of the number of false positives it can produce. **Examples** ```python # module.py import datetime if datetime.date.today().weekday() != 6: a = 1 # main.py from module import a # ImportError: cannot import name 'a' from 'module' ``` ## `possibly-missing-submodule` Default level: warn · Added in 0.0.23 · Related issues · View source **What it does** Checks for accesses of submodules that might not've been imported. **Why is this bad?** When module `a` has a submodule `b`, `import a` isn't generally enough to let you access `a.b.` You either need to explicitly `import a.b`, or else you need the `__init__.py` file of `a` to include `from . import b`. Without one of those, `a.b` is an `AttributeError`. **Examples** ```python import html html.parser # AttributeError: module 'html' has no attribute 'parser' ``` ## `possibly-unresolved-reference` Default level: ignore · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for references to names that are possibly not defined. **Why is this bad?** Using an undefined variable will raise a `NameError` at runtime. **Rule status** This rule is currently disabled by default because of the number of false positives it can produce. **Example** ```python for i in range(0): x = i print(x) # NameError: name 'x' is not defined ``` ## `raw-string-type-annotation` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for raw-strings in type annotation positions. **Why is this bad?** Static analysis tools like ty can't analyze type annotations that use raw-string notation. **Examples** ```python def test(): -> r"int": ... ``` Use instead: ```python def test(): -> "int": ... ``` ## `redundant-cast` Default level: warn · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Detects redundant `cast` calls where the value already has the target type. **Why is this bad?** These casts have no effect and can be removed. **Example** ```python def f() -> int: return 10 cast(int, f()) # Redundant ``` ## `redundant-final-classvar` Default level: warn · Added in 0.0.18 · Related issues · View source **What it does** Checks for redundant combinations of the `ClassVar` and `Final` type qualifiers. **Why is this bad?** An attribute that is marked `Final` in a class body is implicitly a class variable. Marking it as `ClassVar` is therefore redundant. Note that this diagnostic is not emitted for dataclass fields, where `ClassVar[Final[int]]` has a distinct meaning from `Final[int]`. **Examples** ```python from typing import ClassVar, Final class C: x: ClassVar[Final[int]] = 1 # redundant y: Final[ClassVar[int]] = 1 # redundant ``` ## `shadowed-type-variable` Default level: error · Added in 0.0.20 · Related issues · View source **What it does** Checks for type variables in nested generic classes or functions that shadow type variables from an enclosing scope. **Why is this bad?** Shadowing type variables makes the code confusing and is disallowed by the typing spec. **Examples** ```python class Outer[T]: # Error: `T` is already used by `Outer` class Inner[T]: ... # Error: `T` is already used by `Outer` def method[T](self, x: T) -> T: ... ``` **References** - [Typing spec: Generics](https://typing.python.org/en/latest/spec/generics.html#introduction) ## `static-assert-error` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Makes sure that the argument of `static_assert` is statically known to be true. **Why is this bad?** A `static_assert` call represents an explicit request from the user for the type checker to emit an error if the argument cannot be verified to evaluate to `True` in a boolean context. **Examples** ```python from ty_extensions import static_assert static_assert(1 + 1 == 3) # error: evaluates to `False` static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known truthiness ``` ## `subclass-of-final-class` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for classes that subclass final classes. **Why is this bad?** Decorating a class with `@final` declares to the type checker that it should not be subclassed. **Example** ```python from typing import final @final class A: ... class B(A): ... # Error raised here ``` ## `super-call-in-named-tuple-method` Default level: error · Added in 0.0.1-alpha.30 · Related issues · View source **What it does** Checks for calls to `super()` inside methods of `NamedTuple` classes. **Why is this bad?** Using `super()` in a method of a `NamedTuple` class will raise an exception at runtime. **Examples** ```python from typing import NamedTuple class F(NamedTuple): x: int def method(self): super() # error: super() is not supported in methods of NamedTuple classes ``` **References** - [Python documentation: super()](https://docs.python.org/3/library/functions.html#super) ## `too-many-positional-arguments` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for calls that pass more positional arguments than the callable can accept. **Why is this bad?** Passing too many positional arguments will raise `TypeError` at runtime. **Example** ```python def f(): ... f("foo") # Error raised here ``` ## `type-assertion-failure` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for `assert_type()` and `assert_never()` calls where the actual type is not the same as the asserted type. **Why is this bad?** `assert_type()` allows confirming the inferred type of a certain value. **Example** ```python def _(x: int): assert_type(x, int) # fine assert_type(x, str) # error: Actual type does not match asserted type ``` ## `unavailable-implicit-super-arguments` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Detects invalid `super()` calls where implicit arguments like the enclosing class or first method argument are unavailable. **Why is this bad?** When `super()` is used without arguments, Python tries to find two things: the nearest enclosing class and the first argument of the immediately enclosing function (typically self or cls). If either of these is missing, the call will fail at runtime with a `RuntimeError`. **Examples** ```python super() # error: no enclosing class or function found def func(): super() # error: no enclosing class or first argument exists class A: f = super() # error: no enclosing function to provide the first argument def method(self): def nested(): super() # error: first argument does not exist in this nested function lambda: super() # error: first argument does not exist in this lambda (super() for _ in range(10)) # error: argument is not available in generator expression super() # okay! both enclosing class and first argument are available ``` **References** - [Python documentation: super()](https://docs.python.org/3/library/functions.html#super) ## `unbound-type-variable` Default level: error · Added in 0.0.20 · Related issues · View source **What it does** Checks for type variables that are used in a scope where they are not bound to any enclosing generic context. **Why is this bad?** Using a type variable outside of a scope that binds it has no well-defined meaning. **Examples** ```python from typing import TypeVar, Generic T = TypeVar("T") S = TypeVar("S") x: T # error: unbound type variable in module scope class C(Generic[T]): x: list[S] = [] # error: S is not in this class's generic context ``` **References** - [Typing spec: Scoping rules for type variables](https://typing.python.org/en/latest/spec/generics.html#scoping-rules-for-type-variables) ## `undefined-reveal` Default level: warn · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for calls to `reveal_type` without importing it. **Why is this bad?** Using `reveal_type` without importing it will raise a `NameError` at runtime. **Examples** ```python reveal_type(1) # NameError: name 'reveal_type' is not defined ``` ## `unknown-argument` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for keyword arguments in calls that don't match any parameter of the callable. **Why is this bad?** Providing an unknown keyword argument will raise `TypeError` at runtime. **Example** ```python def f(x: int) -> int: ... f(x=1, y=2) # Error raised here ``` ## `unresolved-attribute` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for unresolved attributes. **Why is this bad?** Accessing an unbound attribute will raise an `AttributeError` at runtime. An unresolved attribute is not guaranteed to exist from the type alone, so this could also indicate that the object is not of the type that the user expects. **Examples** ```python class A: ... A().foo # AttributeError: 'A' object has no attribute 'foo' ``` ## `unresolved-global` Default level: warn · Added in 0.0.1-alpha.15 · Related issues · View source **What it does** Detects variables declared as `global` in an inner scope that have no explicit bindings or declarations in the global scope. **Why is this bad?** Function bodies with `global` statements can run in any order (or not at all), which makes it hard for static analysis tools to infer the types of globals without explicit definitions or declarations. **Example** ```python def f(): global x # unresolved global x = 42 def g(): print(x) # unresolved reference ``` Use instead: ```python x: int def f(): global x x = 42 def g(): print(x) ``` Or: ```python x: int | None = None def f(): global x x = 42 def g(): print(x) ``` ## `unresolved-import` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for import statements for which the module cannot be resolved. **Why is this bad?** Importing a module that cannot be resolved will raise a `ModuleNotFoundError` at runtime. **Examples** ```python import foo # ModuleNotFoundError: No module named 'foo' ``` ## `unresolved-reference` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for references to names that are not defined. **Why is this bad?** Using an undefined variable will raise a `NameError` at runtime. **Example** ```python print(x) # NameError: name 'x' is not defined ``` ## `unsupported-base` Default level: warn · Added in 0.0.1-alpha.7 · Related issues · View source **What it does** Checks for class definitions that have bases which are unsupported by ty. **Why is this bad?** If a class has a base that is an instance of a complex type such as a union type, ty will not be able to resolve the [method resolution order] (MRO) for the class. This will lead to an inferior understanding of your codebase and unpredictable type-checking behavior. **Examples** ```python import datetime class A: ... class B: ... if datetime.date.today().weekday() != 6: C = A else: C = B class D(C): ... # error: [unsupported-base] ``` [method resolution order]: https://docs.python.org/3/glossary.html#term-method-resolution-order ## `unsupported-bool-conversion` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for bool conversions where the object doesn't correctly implement `__bool__`. **Why is this bad?** If an exception is raised when you attempt to evaluate the truthiness of an object, using the object in a boolean context will fail at runtime. **Examples** ```python class NotBoolable: __bool__ = None b1 = NotBoolable() b2 = NotBoolable() if b1: # exception raised here pass b1 and b2 # exception raised here not b1 # exception raised here b1 < b2 < b1 # exception raised here ``` ## `unsupported-dynamic-base` Default level: ignore · Added in 0.0.12 · Related issues · View source **What it does** Checks for dynamic class definitions (using `type()`) that have bases which are unsupported by ty. This is equivalent to [`unsupported-base`](#unsupported-base) but applies to classes created via `type()` rather than `class` statements. **Why is this bad?** If a dynamically created class has a base that is an unsupported type such as `type[T]`, ty will not be able to resolve the [method resolution order] (MRO) for the class. This may lead to an inferior understanding of your codebase and unpredictable type-checking behavior. **Default level** This rule is disabled by default because it will not cause a runtime error, and may be noisy on codebases that use `type()` in highly dynamic ways. **Examples** ```python def factory(base: type[Base]) -> type: # `base` has type `type[Base]`, not `type[Base]` itself return type("Dynamic", (base,), {}) # error: [unsupported-dynamic-base] ``` [method resolution order]: https://docs.python.org/3/glossary.html#term-method-resolution-order ## `unsupported-operator` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for binary expressions, comparisons, and unary expressions where the operands don't support the operator. **Why is this bad?** Attempting to use an unsupported operator will raise a `TypeError` at runtime. **Examples** ```python class A: ... A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A' ``` ## `unused-awaitable` Default level: warn · Preview (since 0.0.21) · Related issues · View source **What it does** Checks for awaitable objects (such as coroutines) used as expression statements without being awaited. **Why is this bad?** Calling an `async def` function returns a coroutine object. If the coroutine is never awaited, the body of the async function will never execute, which is almost always a bug. Python emits a `RuntimeWarning: coroutine was never awaited` at runtime in this case. **Examples** ```python async def fetch_data() -> str: return "data" async def main() -> None: fetch_data() # Warning: coroutine is not awaited await fetch_data() # OK ``` ## `unused-ignore-comment` Default level: warn · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for `ty: ignore` directives that are no longer applicable. **Why is this bad?** A `ty: ignore` directive that no longer matches any diagnostic violations is likely included by mistake, and should be removed to avoid confusion. **Examples** ```py a = 20 / 2 # ty: ignore[division-by-zero] ``` Use instead: ```py a = 20 / 2 ``` **Options** Set [`analysis.respect-type-ignore-comments`](https://docs.astral.sh/ty/reference/configuration/#respect-type-ignore-comments) to `false` to prevent this rule from reporting unused `type: ignore` comments. ## `unused-type-ignore-comment` Default level: warn · Added in 0.0.14 · Related issues · View source **What it does** Checks for `type: ignore` directives that are no longer applicable. **Why is this bad?** A `type: ignore` directive that no longer matches any diagnostic violations is likely included by mistake, and should be removed to avoid confusion. **Examples** ```py a = 20 / 2 # type: ignore ``` Use instead: ```py a = 20 / 2 ``` **Options** This rule is skipped if [`analysis.respect-type-ignore-comments`](https://docs.astral.sh/ty/reference/configuration/#respect-type-ignore-comments) to `false`. ## `useless-overload-body` Default level: warn · Added in 0.0.1-alpha.22 · Related issues · View source **What it does** Checks for various `@overload`-decorated functions that have non-stub bodies. **Why is this bad?** Functions decorated with `@overload` are ignored at runtime; they are overridden by the implementation function that follows the series of overloads. While it is not illegal to provide a body for an `@overload`-decorated function, it may indicate a misunderstanding of how the `@overload` decorator works. **Example** ```py from typing import overload @overload def foo(x: int) -> int: return x + 1 # will never be executed @overload def foo(x: str) -> str: return "Oh no, got a string" # will never be executed def foo(x: int | str) -> int | str: raise Exception("unexpected type encountered") ``` Use instead: ```py from typing import assert_never, overload @overload def foo(x: int) -> int: ... @overload def foo(x: str) -> str: ... def foo(x: int | str) -> int | str: if isinstance(x, int): return x + 1 elif isinstance(x, str): return "Oh no, got a string" else: assert_never(x) ``` **References** - [Python documentation: `@overload`](https://docs.python.org/3/library/typing.html#typing.overload) ## `zero-stepsize-in-slice` Default level: error · Added in 0.0.1-alpha.1 · Related issues · View source **What it does** Checks for step size 0 in slices. **Why is this bad?** A slice with a step size of zero will raise a `ValueError` at runtime. **Examples** ```python l = list(range(10)) l[1:10:0] # ValueError: slice step cannot be zero ``` ================================================ FILE: docs/reference/typing-faq.md ================================================ # Typing FAQ This page answers some commonly asked questions about ty and Python's type system. ## Why does ty report an error on my code? Check the [documentation](https://docs.astral.sh/ty/reference/rules/) for the specific error code you are seeing; it may explain the problem. ## What is the `Unknown` type and when does it appear? `Unknown` is ty's way of representing a type that could not be fully inferred. It behaves the same way as `Any`, but appears implicitly, rather than through an explicit `Any` annotation: ```py from missing_module import MissingClass # error: unresolved-import reveal_type(MissingClass) # Unknown ``` ty also uses unions with `Unknown` to maintain the [gradual guarantee](../features/type-system.md#gradual-guarantee), which helps avoid false positive errors in untyped code while still providing useful type information where possible. For example, consider the following untyped `Message` class (which could come from a third-party dependency that you have no control over). ty treats the `data` attribute as having type `Unknown | None`, since there is no type annotation that restricts it further. The `Unknown` in the union allows ty to avoid raising errors on the `msg.data = …` assignment. On the other hand, the `None` in the union reflects the fact that `data` *could* possibly be `None`, and requires code that uses `msg.data` to handle that case explicitly. ```py class Message: data = None def __init__(self, title): self.title = title def receive(msg: Message): reveal_type(msg.data) # Unknown | None msg = Message("Favorite color") msg.data = {"color": "blue"} ``` ([Full example in the playground](https://play.ty.dev/862941a8-a3f6-4818-9ea1-d9d59b0bd2fa)) ## Why does ty show `int | float` when I annotate something as `float`? The [Python typing specification](https://typing.python.org/en/latest/spec/special-types.html) includes a special rule for numeric types where an `int` can be used wherever a `float` is expected: ```py def circle_area(radius: float) -> float: return 3.14 * radius * radius circle_area(2) # OK: int is allowed where float is expected ``` This rule is a special case, since `int` is not actually a subclass of `float`. To support this, ty treats `float` annotations as meaning `int | float`. Unlike some other type checkers, ty makes this behavior explicit in type hints and error messages. For example, if you [hover over the `radius` parameter](https://play.ty.dev/fdc144c6-031c-4af9-b520-a4c6ccde9261), ty will show `int | float`. A similar rule applies to `complex`, which is treated as `int | float | complex`. !!! info These special rules for `float` and `complex` exist for a reason. In almost all cases, you probably want to accept both `int` and `float` when you annotate something as `float`. If you really need to accept *only* `float` and not `int`, you can use ty's `JustFloat` type. At the time of writing, this import needs to be guarded by a `TYPE_CHECKING` block: ```py from typing import TYPE_CHECKING if TYPE_CHECKING: from ty_extensions import JustFloat else: JustFloat = float def only_actual_floats_allowed(f: JustFloat) -> None: ... only_actual_floats_allowed(1.0) # OK only_actual_floats_allowed(1) # error: invalid-argument-type ``` ([Full example in the playground](https://play.ty.dev/fb034780-3ba7-4c6a-9449-5b0f44128bab)) If you need this for `complex`, you can use `ty_extensions.JustComplex` in a similar way. ## Why can't I use `list[Subtype]` when a `list[Supertype]` is expected? { #invariant-generics } Let's say you have a class hierarchy with an `Entry` base class as well as `Directory` and `File` subclasses. Since a `Directory` *is* an `Entry`, you can use it everywhere an `Entry` is expected. You might therefore expect a `list[Directory]` to be usable in any context where a `list[Entry]` is expected, but this is not the case. The reason for this is mutability: ```py # Setup of `Entry`, `Directory`, and `File` classes (1) def modify(entries: list[Entry]): entries.append(File("README.txt")) # mutation directories: list[Directory] = [Directory("Downloads"), Directory("Documents")] modify(directories) # ty emits an error on this call ``` 1. The full example might look like this: ```py from dataclasses import dataclass @dataclass class Entry: path: str def size_bytes(self) -> int: ... @dataclass class Directory(Entry): def children(self) -> list[Entry]: ... @dataclass class File(Entry): def content(self) -> bytes: ... def modify(entries: list[Entry]): entries.append(File("README.txt")) # mutation directories: list[Directory] = [Directory("Downloads"), Directory("Documents")] modify(directories) # ty emits an error on this call ``` You can try it out in [this playground example](https://play.ty.dev/01013e73-da54-40c4-a9c5-2af269abda9d). The `modify` call mutates the contents of the `directories` list. After this call, it contains two directories *and one `File`*, which clearly violates the `list[Directory]` type annotation. If this call *were* allowed, subsequent code that relies on the fact that `directories` only contains `Directory` instances might break at runtime: ```py for directory in directories: directory.children() # runtime: 'File' object has no attribute 'children' ``` !!! info In type system terminology, we say `list` is *invariant*, which means that just because `A` is a subtype of `B` does not mean that `list[A]` will be a subtype of `list[B]`. The same is true for other builtin collections such as `set` or `dict`. In contrast, read-only collections like `tuple` or `frozenset` are *covariant* in their type parameter. It is safe to assign a `frozenset[bool]` to a `frozenset[int]` because the contents cannot be mutated. You might run into problems with invariance in situations where mutability isn't required: ```py def total_size_bytes(entries: list[Entry]) -> int: return sum(entry.size_bytes() for entry in entries) # inferred as `list[Directory]` media_entries = [Directory("Pictures"), Directory("Videos")] # still a type-check error, but should be fine in principle (no mutation occurs) size = total_size_bytes(media_entries) ``` To prevent this, you can adapt the signature of `total_size_bytes` to take an argument of type [`Sequence[Entry]`](https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes) instead. This type describes read-only sequences (that contain values of type `Entry`). `Sequence` is therefore covariant in its type parameter. If you cannot adapt the signature of the function you are calling, you can also widen the type of the argument by annotating `media_entries` as `list[Entry]`. In some cases it's also a reasonable solution to create a copy of the list (`total_size_bytes(list(media_entries))`). !!! note If you are looking for a covariant alternative to `dict[str, V]`, you can use [`Mapping[str, V]`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping). ## Why does ty say `Callable` has no attribute `__name__`? When you access `__name__`, `__qualname__`, `__module__`, or `__doc__` on a value typed as `Callable`, ty reports an `unresolved-attribute` error. This is because not all callables have these attributes. Functions do (including lambdas), but other callable objects do not. The `FileUpload` class below, for example, is callable, but instances of `FileUpload` do not have a `__name__` attribute. Passing a `FileUpload` instance to `retry` would lead to an `AttributeError` at runtime. ```py from typing import Callable def retry(times: int, operation: Callable[[], bool]) -> bool: for i in range(times): # WRONG: `operation` does not necessarily have a `__name__` attribute print(f"Calling {operation.__name__}, attempt {i + 1} of {times}") if operation(): return True return False class FileUpload: def __init__(self, name: str) -> None: # … def __call__(self) -> bool: # … retry(3, FileUpload("image.png")) ``` To fix this, you could use `getattr` with a fall back to a default name when the attribute is not present (or use a `hasattr(…, "__name__")` check if you access it multiple times): ```py name = getattr(operation, "__name__", "operation") ``` Alternatively, you could use an `isinstance(…, types.FunctionType)` check to narrow the type of `operation` to something that definitely has a `__name__` attribute: ```py if isinstance(operation, FunctionType): print(f"Calling {operation.__name__}, attempt {i + 1} of {times}") else: print(f"Calling operation, attempt {i + 1} of {times}") ``` You can try various approaches in [this playground example](https://play.ty.dev/f6f7f35a-47c3-423d-be8d-33d03c61d40c). See also [this discussion](https://github.com/astral-sh/ty/issues/1495) for some plans to improve the developer experience around this in the future. !!! info ty has first-class support for intersection types. If you only want to accept function-like callables, you could define `FunctionLikeCallable` as an intersection of `Callable` and `types.FunctionType`: ```py from typing import Callable, TYPE_CHECKING from types import FunctionType if TYPE_CHECKING: from ty_extensions import Intersection type FunctionLikeCallable[**P, R] = Intersection[Callable[P, R], FunctionType] else: FunctionLikeCallable = Callable def retry(times: int, operation: FunctionLikeCallable[[], bool]) -> bool: ... ``` You can check out the full example [here](https://play.ty.dev/7a1ea4ab-04e1-4271-adf5-ddc3a5d2fcfd), which demonstrates that `FileUpload` instances are no longer accepted by `retry`. ## Does ty have a strict mode? Not yet. A stricter inference mode is tracked in [this issue](https://github.com/astral-sh/ty/issues/1240). In the meantime, you can consider using Ruff's [`flake8-annotations` rules](https://docs.astral.sh/ruff/rules/#flake8-annotations-ann) to enforce more explicit type annotations in your code. ## Why doesn't ty warn about missing type annotations? ty does not report an error for unannotated function parameters, return types, or variables. When ty encounters an unannotated symbol, it infers the type as [`Unknown`](#what-is-the-unknown-type-and-when-does-it-appear) while still providing useful diagnostics where possible. If you are looking for the equivalent of mypy's [`disallow_untyped_defs`](https://mypy.readthedocs.io/en/stable/config_file.html#confval-disallow_untyped_defs) (error code: `no-untyped-def`), Ruff provides this as a set of opt-in lint rules via its [`flake8-annotations` (`ANN`)](https://docs.astral.sh/ruff/rules/#flake8-annotations-ann) rule group. Some rules you might find useful include: - [`ANN001`](https://docs.astral.sh/ruff/rules/missing-type-function-argument/): Missing type annotation for function argument - [`ANN002`](https://docs.astral.sh/ruff/rules/missing-type-args/): Missing type annotation for `*args` - [`ANN003`](https://docs.astral.sh/ruff/rules/missing-type-kwargs/): Missing type annotation for `**kwargs` - [`ANN201`](https://docs.astral.sh/ruff/rules/missing-return-type-undocumented-public-function/): Missing return type annotation for public function - [`ANN202`](https://docs.astral.sh/ruff/rules/missing-return-type-private-function/): Missing return type annotation for private function - [`RUF045`](https://docs.astral.sh/ruff/rules/implicit-class-var-in-dataclass/): Implicit class variable in dataclass ## Why can't ty resolve my imports? Import resolution issues are often caused by a missing or incorrect environment configuration. When ty reports *"Cannot resolve imported module …"*, check the following: 1. **Virtual environment**: Make sure your virtual environment is discoverable. ty looks for an active virtual environment via `VIRTUAL_ENV` or a `.venv` directory in your project root. See the [module discovery](../modules.md#python-environment) documentation for more details. 1. **Project structure**: If your source code is not in the project root or `src/` directory, configure [`environment.root`](./configuration.md#root) in your `pyproject.toml`: ```toml [tool.ty.environment] root = ["./app"] ``` 1. **Third-party packages**: Ensure dependencies are installed in your virtual environment. Run ty with `-v` to see the search paths being used. 1. **Compiled extensions**: ty requires `.py` or `.pyi` files for type information. If a package contains only compiled extensions (`.so` or `.pyd` files), you'll need stub files (`.pyi`) for ty to understand the types. See also [this issue](https://github.com/astral-sh/ty/issues/487) which tracks improvements in this area. ## Does ty support monorepos? ty can work with monorepos, but automatic discovery of nested projects is limited. By default, ty uses the current working directory or the `--project` option to determine the project root. For monorepos with multiple Python packages, you have a few options: 1. **Run ty per-package**: Run `ty check` from each package directory, or use `--project` to specify the package: ```bash ty check --project packages/package-a ty check --project packages/package-b ``` 1. **Configure multiple source roots**: Use [`environment.root`](./configuration.md#root) to specify multiple source directories: ```toml [tool.ty.environment] root = ["packages/package-a", "packages/package-b"] ``` This has the disadvantage of treating all packages as a single project, which may lead to cases in which ty thinks something is importable when it wouldn't be at runtime. You can follow [this issue](https://github.com/astral-sh/ty/issues/819) to get updates on this topic. ## Does ty support PEP 723 inline-metadata scripts? It depends on what you want to do. If you have a single inline-metadata script, you can type check it with ty by using uv's `--with-requirements` flag to install the dependencies specified in the script header: ```bash uvx --with-requirements script.py ty check script.py ``` If you have multiple scripts in your workspace, ty does not yet recognize that they have different dependencies based on their inline metadata. You can follow [this issue](https://github.com/astral-sh/ty/issues/691) for updates. ## Is there a pre-commit hook for ty? Not yet. You can track progress in [this issue](https://github.com/astral-sh/ty/issues/269), which also includes some suggested manual hooks you can use in the meantime. ## Does ty support (mypy) plugins? No. ty does not have a plugin system and there is currently no plan to add one. We prefer extending the type system with well-specified features rather than relying on type-checker-specific plugins. That said, we are considering adding support for popular third-party libraries like pydantic, SQLAlchemy, attrs, or django directly into ty. ## What is `Top[list[Unknown]]`, and why does it appear? This type represents "all possible lists of any element type" (as opposed to `list[Unknown]`, which represents "a list of some unknown element type"). It usually arises from a check such as `if isinstance(x, list):`. If `x` was previously of type `Item | list[Item]`, you might expect this check to narrow the type to `list[Item]`, but ty respects the possibility that there could be a common subclass of both `Item` and `list` (which may not be a list of `Item`!), and so the narrowed type is instead `(Item & Top[list[Unknown]]) | list[Item]`. This code can be made more robust by instead checking `if instance(x, Item)`, or by declaring the `Item` type as `@typing.final`. See also the [discussion here](https://docs.astral.sh/ty/features/type-system/#top-and-bottom-materializations) and [in this issue](https://github.com/astral-sh/ty/issues/1578). ================================================ FILE: docs/requirements.in ================================================ black>=23.10.0 mkdocs>=1.5.0 mkdocs-material>=9.7.0 mkdocs-redirects>=1.2.1 mdformat>=0.7.17 mdformat-mkdocs>=2.0.4 mdformat-admon>=2.0.2 mkdocs-redirects>=1.2.2 mkdocs-git-revision-date-localized-plugin>=1.3.0 mkdocs-llmstxt>=0.2.0 ================================================ FILE: docs/requirements.txt ================================================ # This file was autogenerated by uv via the following command: # uv pip compile docs/requirements.in -o docs/requirements.txt --universal -p 3.12 babel==2.15.0 # via # mkdocs-git-revision-date-localized-plugin # mkdocs-material backrefs==6.1 # via mkdocs-material beautifulsoup4==4.13.4 # via # markdownify # mkdocs-llmstxt black==24.4.2 # via -r docs/requirements.in certifi==2024.7.4 # via requests charset-normalizer==3.3.2 # via requests click==8.1.7 # via # black # mkdocs colorama==0.4.6 # via # click # mkdocs # mkdocs-material ghp-import==2.1.0 # via mkdocs gitdb==4.0.12 # via gitpython gitpython==3.1.44 # via mkdocs-git-revision-date-localized-plugin idna==3.7 # via requests jinja2==3.1.4 # via # mkdocs # mkdocs-material linkify-it-py==2.0.3 # via markdown-it-py markdown==3.6 # via # mkdocs # mkdocs-material # pymdown-extensions markdown-it-py==3.0.0 # via # mdformat # mdformat-gfm # mdit-py-plugins markdownify==1.1.0 # via mkdocs-llmstxt markupsafe==2.1.5 # via # jinja2 # mkdocs mdformat==0.7.22 # via # -r docs/requirements.in # mdformat-admon # mdformat-gfm # mdformat-mkdocs # mdformat-tables # mkdocs-llmstxt mdformat-admon==2.0.6 # via # -r docs/requirements.in # mdformat-mkdocs mdformat-gfm==0.3.6 # via mdformat-mkdocs mdformat-mkdocs==3.0.0 # via -r docs/requirements.in mdformat-tables==0.4.1 # via mdformat-gfm mdit-py-plugins==0.4.1 # via # mdformat-admon # mdformat-gfm # mdformat-mkdocs mdurl==0.1.2 # via markdown-it-py mergedeep==1.3.4 # via # mkdocs # mkdocs-get-deps mkdocs==1.6.0 # via # -r docs/requirements.in # mkdocs-git-revision-date-localized-plugin # mkdocs-material # mkdocs-redirects mkdocs-get-deps==0.2.0 # via mkdocs mkdocs-git-revision-date-localized-plugin==1.3.0 # via -r docs/requirements.in mkdocs-llmstxt==0.2.0 # via -r docs/requirements.in mkdocs-material==9.7.0 # via -r docs/requirements.in mkdocs-material-extensions==1.3.1 # via mkdocs-material mkdocs-redirects==1.2.2 # via -r docs/requirements.in more-itertools==10.3.0 # via mdformat-mkdocs mypy-extensions==1.0.0 # via black packaging==24.1 # via # black # mkdocs paginate==0.5.6 # via mkdocs-material pathspec==0.12.1 # via # black # mkdocs platformdirs==4.2.2 # via # black # mkdocs-get-deps pygments==2.18.0 # via mkdocs-material pymdown-extensions==10.8.1 # via mkdocs-material python-dateutil==2.9.0.post0 # via ghp-import pytz==2025.1 # via mkdocs-git-revision-date-localized-plugin pyyaml==6.0.1 # via # mkdocs # mkdocs-get-deps # pymdown-extensions # pyyaml-env-tag pyyaml-env-tag==0.1 # via mkdocs requests==2.32.3 # via mkdocs-material six==1.16.0 # via # markdownify # python-dateutil smmap==5.0.2 # via gitdb soupsieve==2.7 # via beautifulsoup4 typing-extensions==4.14.0 # via beautifulsoup4 uc-micro-py==1.0.3 # via linkify-it-py urllib3==2.2.2 # via requests watchdog==4.0.1 # via mkdocs ================================================ FILE: docs/rules.md ================================================ # Rules Rules are individual checks that ty performs to detect common issues in your code, such as incompatible assignments, missing imports, or invalid type annotations. Each rule focuses on a specific pattern and can be turned on or off depending on your project’s needs. !!! tip See the [rules reference](./reference/rules.md) for an enumeration of all supported rules. ## Rule levels Each rule has a configurable level: - `error`: violations are reported as errors and ty exits with an exit code of 1 if there's any. - `warn`: violations are reported as warnings. Depending on your configuration, ty exits with an exit code of 0 if there are only warning violations (default) or 1 when using `--error-on-warning`. - `ignore`: the rule is turned off You can configure the level for each rule on the command line using the `--warn`, `--error`, and `--ignore` flags. For example: ```shell ty check \ --warn unused-ignore-comment \ # Make `unused-ignore-comment` a warning --ignore redundant-cast \ # Disable `redundant-cast` --error possibly-missing-attribute \ # Error on `possibly-missing-attribute` --error possibly-missing-import # Error on `possibly-missing-import` ``` The options can be repeated. Subsequent options override earlier options. Rule levels can also be changed in the [`rules`](./reference/configuration.md#rules) section of a [configuration file](./configuration.md). For example, the following is equivalent to the command above: ```toml title="pyproject.toml" [tool.ty.rules] unused-ignore-comment = "warn" redundant-cast = "ignore" possibly-missing-attribute = "error" possibly-missing-import = "error" ``` You can also configure the level for all rules at once. On the command line you can use `--error all`, `--warn all`, or `--ignore all`. For example: ```shell ty check --error all ``` You can also configure this setting in the [`rules`](./reference/configuration.md#rules) section of a [configuration file](./configuration.md). For example, the following is equivalent to the command above: ```toml title="pyproject.toml" [tool.ty.rules] all = "error" ``` ================================================ FILE: docs/stylesheets/extra.css ================================================ :root { --black: #261230; --white: #ffffff; --radiate: #d7ff64; --flare: #6340ac; --rock: #78876e; --galaxy: #261230; --space: #30173d; --comet: #6f5d6f; --cosmic: #de5fe9; --sun: #ffac2f; --electron: #46ebe1; --aurora: #46eb74; --constellation: #5f6de9; --neutron: #cff3cf; --proton: #f6afbc; --nebula: #cdcbfb; --supernova: #f1aff6; --starlight: #f4f4f1; --lunar: #fbf2fc; --asteroid: #e3cee3; --crater: #f0dfdf; } [data-md-color-scheme="astral-light"] { --md-default-bg-color--dark: var(--black); --md-primary-fg-color: var(--galaxy); --md-typeset-a-color: var(--flare); --md-accent-fg-color: var(--cosmic); --md-default-fg-color--lightest: rgba(0, 0, 0, 0.14); } [data-md-color-scheme="astral-dark"] { --md-default-bg-color: var(--galaxy); --md-default-fg-color: var(--white); --md-default-fg-color--light: var(--white); --md-default-fg-color--lighter: var(--white); --md-default-fg-color--lightest: rgba(255, 255, 255, 0.5); --md-primary-fg-color: var(--space); --md-primary-bg-color: var(--white); --md-accent-fg-color: var(--cosmic); --md-typeset-color: var(--white); --md-typeset-a-color: var(--radiate); --md-typeset-mark-color: var(--sun); --md-code-fg-color: var(--white); --md-code-bg-color: var(--space); --md-code-hl-comment-color: var(--asteroid); --md-code-hl-punctuation-color: var(--asteroid); --md-code-hl-generic-color: var(--supernova); --md-code-hl-variable-color: var(--starlight); --md-code-hl-string-color: var(--radiate); --md-code-hl-keyword-color: var(--supernova); --md-code-hl-operator-color: var(--supernova); --md-code-hl-number-color: var(--electron); --md-code-hl-special-color: var(--electron); --md-code-hl-function-color: var(--neutron); --md-code-hl-constant-color: var(--radiate); --md-code-hl-name-color: var(--md-code-fg-color); --md-typeset-del-color: hsla(6, 90%, 60%, 0.15); --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15); --md-typeset-table-color: hsla(0, 0%, 100%, 0.12); --md-typeset-table-color--light: hsla(0, 0%, 100%, 0.035); } [data-md-color-scheme="astral-light"] img[src$="#only-dark"], [data-md-color-scheme="astral-light"] img[src$="#gh-dark-mode-only"] { display: none; /* Hide dark images in light mode */ } [data-md-color-scheme="astral-light"] img[src$="#only-light"], [data-md-color-scheme="astral-light"] img[src$="#gh-light-mode-only"] { display: inline; /* Show light images in light mode */ } [data-md-color-scheme="astral-dark"] img[src$="#only-light"], [data-md-color-scheme="astral-dark"] img[src$="#gh-light-mode-only"] { display: none; /* Hide light images in dark mode */ } [data-md-color-scheme="astral-dark"] img[src$="#only-dark"], [data-md-color-scheme="astral-dark"] img[src$="#gh-dark-mode-only"] { display: inline; /* Show dark images in dark mode */ } /* See: https://github.com/squidfunk/mkdocs-material/issues/175#issuecomment-616694465 */ .md-typeset__table { min-width: 100%; } .md-typeset table:not([class]) { display: table; } /* See: https://github.com/astral-sh/ruff/issues/8519 */ [data-md-color-scheme="astral-dark"] details summary a { color: var(--flare); } /* See: https://github.com/astral-sh/ruff/issues/9046 */ [data-md-color-scheme="astral-dark"] div.admonition { color: var(--md-code-fg-color); background-color: var(--md-code-bg-color); } /* Prevent the shadow from the nav title from blurring the top link. The box shadow isn't really doing anything anyway. This is a consequence of the reduced nav spacing below. */ .md-nav--primary .md-nav__title { box-shadow: none; } /* Omits the nav title "ty" entirely unless on a small screen, in which case the nav title is needed for backwards navigation in the collapsible nav variant. See https://github.com/astral-sh/uv/issues/5130 */ @media screen and (min-width: 76.25em) { .md-nav__title { display: none; } } /* Always take the full screen for content, require scrolling to see the footer This stops the size of the nav from jumping around when you visit a page without a lot of content (i.e., an overview page). We don't apply this to sma screens because the nav is in a hamburger menu anyway */ @media screen and (min-width: 76.25em) { .md-main { min-height: 100vh; } } /* Tweak the formatting of the primary nav on a large screen */ @media screen and (min-width: 76.25em) { .md-nav--primary .md-nav { font-size: 0.75rem; } /* Remove the bold from the section headings, use a larger font instead */ .md-nav__item--section > .md-nav__link { font-weight: normal; font-size: 0.85rem; } /* Reducing spacing between nav items to fit more content First, disable `nav__link` spacing then use `nav__item` to enforce margins this reduces inconsistencies in the spacing. */ .md-nav--primary .md-nav__link { margin: 0; } .md-nav--primary .md-nav__item { margin-top: 0.35em; } /* Use larger spacing for the sections headings */ .md-nav--primary .md-nav__item--section { margin-bottom: 0.75em; margin-top: 1em; } /* Decrease the font size of items in a collapsible section */ .md-nav__item--section> .md-nav > .md-nav__list > .md-nav__item > .md-nav > .md-nav__list { font-size: 0.725rem; } /* Increase top margin on the first item of a collapsible section */ .md-nav__item--section> .md-nav > .md-nav__list > .md-nav__item > .md-nav > .md-nav__list > .md-nav__item:first-of-type { margin-top: 0.5em; } /* Increase bottom margin on the last item of a collapsible section */ .md-nav__item--section> .md-nav > .md-nav__list > .md-nav__item > .md-nav > .md-nav__list > .md-nav__item:last-of-type { margin-bottom: 0.575em; } /* Increase the size of the first nav item to match the sections It has no children, so it is not considered a section */ .md-nav--primary > .md-nav__list > .md-nav__item:first-of-type { font-size: 0.85rem; margin-bottom: 0.75em; } } /* Bold the active nav link for accessibility */ .md-nav__link--active { font-weight: bold; } /* See: https://mkdocstrings.github.io/recipes/#prevent-selection-of-prompts-and-output-in-python-code-blocks */ .highlight .gp, .highlight .go { /* Generic.Prompt, Generic.Output */ user-select: none; } /* Styling for the generated CLI reference page */ .cli-reference dd { margin-top: 0.1em; margin-bottom: 0.5em; } .cli-reference dd p { margin-block-start: 0.2em; margin-block-end: 0.3em; } .cli-reference ul { margin-bottom: 0.1em; } h3.cli-reference { font-size: 1.1em; margin: 0 0 0 0; } /* Styling for anchor link headers */ .toclink { color: unset !important; } .toclink:hover { color: var(--md-accent-fg-color) !important; } /* Omit the first breadcrumb item, which is the "Introduction" */ .md-path__list > .md-path__item:first-of-type { display: none; } .md-path__list > .md-path__item:nth-of-type(2):before { display: none; } /* Hide the modified date — its positioning is awkward but will require theme modifications */ .md-source-file__fact { visibility: hidden; } ================================================ FILE: docs/suppression.md ================================================ # Suppression Rules can also be ignored in specific locations in your code (instead of disabling the rule entirely) to silence false positives or permissible violations. !!! note To disable a rule entirely, set it to the `ignore` level as described in [rule levels](rules.md/#rule-levels). ## ty suppression comments To suppress a rule violation inline add a `# ty: ignore[]` comment at the end of the line: ```py a = 10 + "test" # ty: ignore[unsupported-operator] ``` Rule violations spanning multiple lines can be suppressed by adding the comment at the end of the violation's first or last line: ```py def sum_three_numbers(a: int, b: int, c: int) -> int: ... # on the first line sum_three_numbers( # ty: ignore[missing-argument] 3, 2 ) # or, on the last line sum_three_numbers( 3, 2 ) # ty: ignore[missing-argument] ``` To suppress multiple violations on a single line, enumerate each rule separated by a comma: ```python sum_three_numbers("one", 5) # ty: ignore[missing-argument, invalid-argument-type] ``` !!! note Enumerating rule names (e.g., `[rule1, rule2]`) is optional. However, we strongly recommend including suppressing specific rules to avoid accidental suppression of other errors. ## Standard suppression comments ty supports the standard [`type: ignore`](https://typing.python.org/en/latest/spec/directives.html#type-ignore-comments) comment format introduced by PEP 484. ty handles these similarly to `ty: ignore` comments, but suppresses all violations on that line, even when `type: ignore[code]` is used. ```python # Ignore all typing errors on the next line sum_three_numbers("one", 5) # type: ignore ``` ## Multiple suppressions comments To suppress a typing error on a line that already has a suppression comment from another tool, add the `# ty: ignore` comment to the same line. For example, to suppress a type error and disable formatting for a specific line: ```python result = calculate() # ty: ignore[invalid-argument-type] # fmt: skip # or result = calculate() # fmt: off # ty: ignore[invalid-argument-type] ``` ## Unused suppression comments If the [`unused-ignore-comment`](./reference/rules.md#unused-ignore-comment) rule is enabled, ty will report unused `ty: ignore` and `type: ignore` comments. `unused-ignore-comment` violations can only be suppressed using `# ty: ignore[unused-ignore-comment]`. They cannot be suppressed using `# ty: ignore` without a rule code or `# type: ignore`. ## `@no_type_check` directive ty supports the [`@no_type_check`](https://typing.python.org/en/latest/spec/directives.html#no-type-check) decorator to suppress all violations inside a function. ```python from typing import no_type_check def sum_three_numbers(a: int, b: int, c: int) -> int: return a + b + c @no_type_check def main(): sum_three_numbers(1, 2) # no error for the missing argument ``` Decorating a class with `@no_type_check` isn't supported. ================================================ FILE: docs/type-checking.md ================================================ # Type checking After [installing ty](./installation.md), it's time to type check some code! ## Running the type checker To run the type checker, use the `check` command: ```shell ty check ``` !!! tip If you're in a project, you may need to use `uv run` or activate your virtual environment first for ty to find your dependencies. ## Environment discovery The type checker needs to discover your installed packages in order to check your use of imported dependencies. ty will find installed packages in the active virtual environment (via `VIRTUAL_ENV`) or discover a virtual environment named `.venv` in the project root or working directory. Failing that, ty will look for a `python3` or `python` binary available in `PATH`. Alternatively, the `--python` flag can be provided to ty to explicitly specify a path to a Python interpreter. See the [module discovery](./modules.md) documentation for details. ## File selection ty will run on all Python files in the working directory (including subdirectories, recursively). If used from a project, ty will run on all Python files in the project (starting in the directory with the `pyproject.toml`). You can also provide specific paths to check: ```shell ty check example.py ``` You can also persistently configure [included and excluded files](./exclusions.md). ## Rule selection and severity ty's type checking diagnostics are often associated with a rule. ty's type checking rules can be configured to your project's needs. See the [rules](./rules.md) documentation for details. You can also suppress specific violations of rules using [suppression comments](./suppression.md). ## Watch mode ty can be run in an incremental watch mode: ```shell ty check --watch ``` ty will watch files for changes and recheck any affected files — including files that depend on the changed file. ty uses [fine-grained incrementality](./features/language-server.md#fine-grained-incrementality) to perform subsequent checks much faster than running `ty check` repeatedly. ## The type system To learn more about what makes type checking in ty unique, read about the [type system](./features/type-system.md). ================================================ FILE: mkdocs.yml ================================================ site_name: ty theme: name: material logo: assets/logo-letter.svg favicon: assets/favicon.ico features: - navigation.path - navigation.instant - navigation.instant.prefetch - navigation.instant.progress - navigation.sections - navigation.indexes - navigation.tracking - content.code.annotate - toc.follow - navigation.footer - navigation.top - content.code.copy - content.tabs.link palette: # https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/#automatic-light-dark-mode - media: "(prefers-color-scheme)" toggle: icon: material/brightness-auto name: Switch to light mode - media: "(prefers-color-scheme: light)" scheme: astral-light toggle: icon: material/brightness-7 name: Switch to dark mode - media: "(prefers-color-scheme: dark)" scheme: astral-dark toggle: icon: material/brightness-4 name: Switch to system preference custom_dir: docs/.overrides repo_url: https://github.com/astral-sh/ty repo_name: ty site_author: astral-sh site_url: https://docs.astral.sh/ty/ site_dir: site/ty site_description: ty is an extremely fast Python type checker. markdown_extensions: - admonition - pymdownx.details - pymdownx.snippets: - pymdownx.magiclink: - attr_list: - toc: anchorlink: true anchorlink_class: "toclink" - md_in_html: - pymdownx.inlinehilite: - pymdownx.superfences: - markdown.extensions.attr_list: - pymdownx.keys: - pymdownx.tasklist: custom_checkbox: true - pymdownx.highlight: anchor_linenums: true - pymdownx.tabbed: alternate_style: true plugins: - search - git-revision-date-localized: timezone: UTC # It can only be in UTC unless the ISO time can include timezone. - llmstxt: markdown_description: | ty is an extremely fast Python type checker and a language server written in Rust. It can type check large Python codebases in seconds, providing fast feedback during development. ty includes both a CLI (`ty check`) and a language server (`ty server`) for editor integration. When fetching documentation, use explicit `index.md` paths for directories, e.g., `https://docs.astral.sh/ty/features/index.md`. This returns clean markdown instead of rendered HTML with JS/CSS. sections: Guides: - installation.md - type-checking.md - editors.md Concepts: - configuration.md - modules.md - python-version.md - exclusions.md - rules.md - suppression.md Features: - features/type-system.md - features/diagnostics.md - features/language-server.md Reference: - reference/configuration.md - reference/typing-faq.md - reference/rules.md - reference/cli.md - reference/exit-codes.md - reference/environment.md - reference/editor-settings.md extra_css: - stylesheets/extra.css extra_javascript: - js/extra.js extra: analytics: provider: fathom social: - icon: fontawesome/brands/github link: https://github.com/astral-sh/ty - icon: fontawesome/brands/discord link: https://discord.com/invite/astral-sh - icon: fontawesome/brands/python link: https://pypi.org/project/ty/ - icon: fontawesome/brands/x-twitter link: https://x.com/astral_sh nav: - Introduction: index.md - Guides: - Installation: installation.md - Type checking: type-checking.md - Editor integration: editors.md - Concepts: - Configuration: configuration.md - Module discovery: modules.md - Python version: python-version.md - File exclusions: exclusions.md - Rules: rules.md - Suppression: suppression.md - Features: - Type system: features/type-system.md - Diagnostics: features/diagnostics.md - Language server: features/language-server.md - Reference: - Configuration: reference/configuration.md - Typing FAQ: reference/typing-faq.md - Rules: reference/rules.md - CLI: reference/cli.md - Exit codes: reference/exit-codes.md - Environment variables: reference/environment.md - Editor settings: reference/editor-settings.md validation: omitted_files: warn absolute_links: warn unrecognized_links: warn ================================================ FILE: pyproject.toml ================================================ [project] name = "ty" version = "0.0.24" requires-python = ">=3.8" dependencies = [] description = "An extremely fast Python type checker, written in Rust." readme = "README.md" authors = [{ name = "Astral Software Inc.", email = "hey@astral.sh" }] keywords = ["ty", "typing", "analysis", "check"] classifiers = [ "Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Developers", "Operating System :: OS Independent", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Rust", "Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Testing", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", ] [project.urls] Repository = "https://github.com/astral-sh/ty" Changelog = "https://github.com/astral-sh/ty/blob/main/CHANGELOG.md" Releases = "https://github.com/astral-sh/ty/releases" Discord = "https://discord.gg/astral-sh" [build-system] requires = ["maturin>=1.0,<2.0"] build-backend = "maturin" [dependency-groups] release = ["rooster"] [tool.uv] cache-keys = [ { file = "pyproject.toml" }, { file = "dist-workspace.toml" }, { file = "ruff/Cargo.toml" }, { file = "ruff/Cargo.lock" }, { file = "**/*.rs" }, ] [tool.uv.dependency-groups] release = {requires-python = ">=3.12"} [tool.ruff] extend-exclude = ["ruff"] per-file-target-version = { "scripts/**" = "py313" } [tool.ruff.lint] select = [ "E", # pycodestyle (error) "F", # pyflakes "B", # bugbear "B9", "C4", # flake8-comprehensions "SIM", # flake8-simplify "I", # isort "UP", # pyupgrade "PIE", # flake8-pie "PGH", # pygrep-hooks "PYI", # flake8-pyi "RUF", ] ignore = [ # only relevant if you run a script with `python -0`, # which seems unlikely for any of the scripts in this repo "B011", # Leave it to the formatter to split long lines and # the judgement of all of us. "E501", ] [tool.ruff.lint.isort] required-imports = ["from __future__ import annotations"] [tool.maturin] bindings = "bin" manifest-path = "ruff/crates/ty/Cargo.toml" module-name = "ty" python-source = "python" strip = true include = [ { path = "dist-workspace.toml", format = [ "sdist", ] }, { path = "LICENSE", format = "sdist" }, ] [tool.rooster] version-files = [ "pyproject.toml", { path = "dist-workspace.toml", field = "workspace.version", format = "cargo" }, "docs/installation.md", ] submodules = ["ruff"] require-labels = [{ submodule = "ruff", labels = ["ty"] }] ignore-labels = [ "internal", "testing", "ci", "playground", "documentation", "release", ] changelog-ignore-authors = ["github-actions"] major-labels = [] # We do not use the major version number yet minor-labels = [] # We do not use the minor version number yet version-format = "cargo" trim-title-prefixes = ["[ty]"] [tool.rooster.section-labels] "Breaking changes" = ["breaking"] "Preview features" = ["preview"] "Bug fixes" = ["bug"] "LSP server" = ["server"] "CLI" = ["cli"] "Configuration" = ["configuration"] ================================================ FILE: python/ty/__init__.py ================================================ from __future__ import annotations from ._find_ty import find_ty_bin __all__ = ["find_ty_bin"] ================================================ FILE: python/ty/__main__.py ================================================ from __future__ import annotations import os import sys from ty import find_ty_bin def _run() -> None: ty = find_ty_bin() if sys.platform == "win32": import subprocess # Avoid emitting a traceback on interrupt try: completed_process = subprocess.run([ty, *sys.argv[1:]]) except KeyboardInterrupt: sys.exit(2) sys.exit(completed_process.returncode) else: os.execvp(ty, [ty, *sys.argv[1:]]) if __name__ == "__main__": _run() ================================================ FILE: python/ty/_find_ty.py ================================================ from __future__ import annotations import os import sys import sysconfig class TyNotFound(FileNotFoundError): ... def find_ty_bin() -> str: """Return the ty binary path.""" ty_exe = "ty" + sysconfig.get_config_var("EXE") targets = [ # The scripts directory for the current Python sysconfig.get_path("scripts"), # The scripts directory for the base prefix sysconfig.get_path("scripts", vars={"base": sys.base_prefix}), # Above the package root, e.g., from `pip install --prefix` or `uv run --with` ( # On Windows, with module path `/Lib/site-packages/ty` _join(_matching_parents(_module_path(), "Lib/site-packages/ty"), "Scripts") if sys.platform == "win32" # On Unix, with module path `/lib/python3.13/site-packages/ty` else _join( _matching_parents(_module_path(), "lib/python*/site-packages/ty"), "bin", ) ), # Adjacent to the package root, e.g., from `pip install --target` # with module path `/ty` _join(_matching_parents(_module_path(), "ty"), "bin"), # The user scheme scripts directory, e.g., `~/.local/bin` sysconfig.get_path("scripts", scheme=_user_scheme()), ] seen = [] for target in targets: if not target: continue if target in seen: continue seen.append(target) path = os.path.join(target, ty_exe) if os.path.isfile(path): return path locations = "\n".join(f" - {target}" for target in seen) raise TyNotFound( f"Could not find the ty binary in any of the following locations:\n{locations}\n" ) def _module_path() -> str | None: path = os.path.dirname(__file__) return path def _matching_parents(path: str | None, match: str) -> str | None: """ Return the parent directory of `path` after trimming a `match` from the end. The match is expected to contain `/` as a path separator, while the `path` is expected to use the platform's path separator (e.g., `os.sep`). The path components are compared case-insensitively and a `*` wildcard can be used in the `match`. """ from fnmatch import fnmatch if not path: return None parts = path.split(os.sep) match_parts = match.split("/") if len(parts) < len(match_parts): return None if not all( fnmatch(part, match_part) for part, match_part in zip(reversed(parts), reversed(match_parts)) ): return None return os.sep.join(parts[: -len(match_parts)]) def _join(path: str | None, *parts: str) -> str | None: if not path: return None return os.path.join(path, *parts) def _user_scheme() -> str: if sys.version_info >= (3, 10): user_scheme = sysconfig.get_preferred_scheme("user") elif os.name == "nt": user_scheme = "nt_user" elif sys.platform == "darwin" and sys._framework: # ty: ignore[unresolved-attribute] user_scheme = "osx_framework_user" else: user_scheme = "posix_user" return user_scheme ================================================ FILE: python/ty/py.typed ================================================ ================================================ FILE: scripts/autogenerate_files.sh ================================================ #!/usr/bin/env sh # # Generate files and copy documentation from Ruff. # # Usage # # ./scripts/autogenerate-files.sh # set -eu script_root="$(realpath "$(dirname "$0")")" project_root="$(dirname "$script_root")" cd "$project_root" echo "Updating lockfile..." uv lock echo "Copying reference documentation from Ruff..." cp ./ruff/crates/ty/docs/cli.md ./docs/reference/ cp ./ruff/crates/ty/docs/configuration.md ./docs/reference/ cp ./ruff/crates/ty/docs/rules.md ./docs/reference/ cp ./ruff/crates/ty/docs/environment.md ./docs/reference/ echo "Documentation has been copied from Ruff submodule" ================================================ FILE: scripts/release.sh ================================================ #!/usr/bin/env sh # # Prepare a release. # # Usage # # ./scripts/release.sh [rooster-args ...] # set -eu echo "Checking Ruff submodule status..." if git -C ruff diff --quiet; then echo "Ruff submodule is clean; continuing..." else echo "Ruff submodule has uncommitted changes; aborting!" exit 1 fi ruff_head=$(git -C ruff rev-parse --abbrev-ref HEAD) case "${ruff_head}" in "HEAD") echo "Ruff submodule has detached HEAD; switching to main..." git -C ruff checkout main > /dev/null 2>&1 ;; "main") echo "Ruff submodule is on main branch; continuing..." ;; *) echo "Ruff submodule is on branch ${ruff_head} but must be on main; aborting!" exit 1 ;; esac # Save the current typeshed source commit before updating ruff, # so we can generate a typeshed diff link for the changelog later. typeshed_commit_file="ruff/crates/ty_vendored/vendor/typeshed/source_commit.txt" old_typeshed_commit="" if [ -f "$typeshed_commit_file" ]; then old_typeshed_commit=$(cat "$typeshed_commit_file") fi echo "Updating Ruff to the latest commit..." git -C ruff pull origin main git add ruff script_root="$(realpath "$(dirname "$0")")" project_root="$(dirname "$script_root")" echo "Running rooster..." cd "$project_root" # Generate the changelog and bump versions uv run --isolated --only-group release \ rooster release "$@" # If the typeshed source commit changed and the changelog mentions a typeshed # sync, append a link to the typeshed diff so reviewers can see what changed. if [ -n "$old_typeshed_commit" ] && [ -f "$typeshed_commit_file" ]; then new_typeshed_commit=$(cat "$typeshed_commit_file") if [ "$old_typeshed_commit" != "$new_typeshed_commit" ]; then typeshed_diff_link="[Typeshed diff](https://github.com/python/typeshed/compare/${old_typeshed_commit}...${new_typeshed_commit})" # Match lines like "- Sync vendored typeshed stubs ([#NNNN](...))". # The pattern anchors on the trailing "))$" so it won't match lines # that already have a typeshed diff link appended. # Use a temp file instead of `sed -i` for macOS/Linux portability. sed "s|\(- Sync vendored typeshed stubs (.*)\))$|\1). ${typeshed_diff_link}|" CHANGELOG.md > CHANGELOG.md.tmp mv CHANGELOG.md.tmp CHANGELOG.md fi fi "${script_root}/autogenerate_files.sh" git add ./docs/reference ================================================ FILE: scripts/transform_readme.py ================================================ """Transform the README.md to support a specific deployment target. By default, we assume that our README.md will be rendered on GitHub. However, PyPI includes the README with different rendering. """ from __future__ import annotations import re import tomllib import urllib.parse from pathlib import Path # The benchmark SVG includes a CSS media query that adapts to light/dark mode. # PyPI doesn't support this, so we replace it with a light-only version. # See: https://github.com/pypi/warehouse/issues/11251 BENCHMARK_URL = "https://raw.githubusercontent.com/astral-sh/ty/main/docs/assets/ty-benchmark-cli.svg" BENCHMARK_URL_LIGHT = "https://raw.githubusercontent.com/astral-sh/ty/main/docs/assets/ty-benchmark-cli-light.svg" def main() -> None: """Modify the README.md to support PyPI.""" # Read the current version from the `dist-workspace.toml`. with Path("dist-workspace.toml").open(mode="rb") as fp: # Parse the TOML. dist_workspace = tomllib.load(fp) if "workspace" in dist_workspace and "version" in dist_workspace["workspace"]: version = dist_workspace["workspace"]["version"] else: raise ValueError("Version not found in dist-workspace.toml") content = Path("README.md").read_text(encoding="utf8") # Replace the benchmark image URL with the light-only version for PyPI. if BENCHMARK_URL not in content: msg = "README.md is not in the expected format (benchmark image not found)." raise ValueError(msg) content = content.replace(BENCHMARK_URL, BENCHMARK_URL_LIGHT) # Replace relative src="./..." attributes with absolute GitHub raw URLs. def replace_src(match: re.Match) -> str: path = match.group(1).lstrip("./") return f'src="https://raw.githubusercontent.com/astral-sh/ty/{version}/{path}"' content = re.sub(r'src="(\./[^"]+)"', replace_src, content) # Replace any relative URLs (e.g., `[CONTRIBUTING.md`) with absolute URLs. def replace(match: re.Match) -> str: url = match.group(1) if not url.startswith("http"): url = urllib.parse.urljoin( f"https://github.com/astral-sh/ty/blob/{version}/README.md", url ) return f"]({url})" content = re.sub(r"]\(([^)]+)\)", replace, content) # Replace any GitHub admonitions def replace(match: re.Match) -> str: name = match.group(1) return f"> {name}:" content = re.sub(r"> \[\!(\w*)\]", replace, content) with Path("README.md").open("w", encoding="utf8") as fp: fp.write(content) if __name__ == "__main__": main() ================================================ FILE: scripts/update_schemastore.py ================================================ """Update ty.json in schemastore. This script will clone `astral-sh/schemastore`, update the schema and push the changes to a new branch tagged with the ty git hash. You should see a URL to create the PR to schemastore in the CLI. Usage: uv run --only-dev scripts/update_schemastore.py """ from __future__ import annotations import enum import json from pathlib import Path from subprocess import check_call, check_output from tempfile import TemporaryDirectory from typing import NamedTuple, assert_never # The remote URL for the `ty` repository. TY_REPO = "https://github.com/astral-sh/ty" # The path to the root of the `ty` repository. TY_ROOT = Path(__file__).parent.parent # The path to the JSON schema in the `ty` repository. TY_SCHEMA = TY_ROOT / "ruff" / "ty.schema.json" # The path to the JSON schema in the `schemastore` repository. TY_JSON = Path("schemas/json/ty.json") class SchemastoreRepos(NamedTuple): fork: str upstream: str class GitProtocol(enum.Enum): SSH = "ssh" HTTPS = "https" def schemastore_repos(self) -> SchemastoreRepos: match self: case GitProtocol.SSH: return SchemastoreRepos( fork="git@github.com:astral-sh/schemastore.git", upstream="git@github.com:SchemaStore/schemastore.git", ) case GitProtocol.HTTPS: return SchemastoreRepos( fork="https://github.com/astral-sh/schemastore.git", upstream="https://github.com/SchemaStore/schemastore.git", ) case _: assert_never(self) def update_schemastore( schemastore_path: Path, schemastore_repos: SchemastoreRepos ) -> None: if not (schemastore_path / ".git").is_dir(): check_call( ["git", "clone", schemastore_repos.fork, schemastore_path, "--depth=1"], ) check_call( [ "git", "remote", "add", "upstream", schemastore_repos.upstream, ], cwd=schemastore_path, ) # Create a new branch tagged with the current ty commit up to date with the latest # upstream schemastore check_call(["git", "fetch", "upstream"], cwd=schemastore_path) current_sha = check_output( ["git", "rev-parse", "HEAD"], text=True, cwd=TY_ROOT ).strip() branch = f"update-ty-{current_sha}" check_call( ["git", "switch", "-c", branch], cwd=schemastore_path, ) check_call( ["git", "reset", "--hard", "upstream/master"], cwd=schemastore_path, ) # Run npm ci src = schemastore_path / "src" check_call(["npm", "ci", "--ignore-scripts"], cwd=schemastore_path) # Update the schema and format appropriately schema = json.loads(TY_SCHEMA.read_text()) schema["$id"] = "https://json.schemastore.org/ty.json" (src / TY_JSON).write_text( json.dumps(dict(schema.items()), indent=2, ensure_ascii=False), ) check_call( [ "../node_modules/prettier/bin/prettier.cjs", "--plugin", "prettier-plugin-sort-json", "--write", TY_JSON, ], cwd=src, ) # Check if the schema has changed # https://stackoverflow.com/a/9393642/3549270 if check_output(["git", "status", "-s"], cwd=schemastore_path).strip(): # Schema has changed, commit and push commit_url = f"{TY_REPO}/commit/{current_sha}" commit_body = f"This updates ty's JSON schema to [{current_sha}]({commit_url})" # https://stackoverflow.com/a/22909204/3549270 check_call(["git", "add", (src / TY_JSON).as_posix()], cwd=schemastore_path) check_call( [ "git", "commit", "-m", "Update ty's JSON schema", "-m", commit_body, ], cwd=schemastore_path, ) # This should show the link to create a PR check_call( ["git", "push", "--set-upstream", "origin", branch, "--force"], cwd=schemastore_path, ) else: print("No changes") def determine_git_protocol(argv: list[str] | None = None) -> GitProtocol: import argparse parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument( "--proto", choices=[proto.value for proto in GitProtocol], default="https", help="Protocol to use for git authentication", ) args = parser.parse_args(argv) return GitProtocol(args.proto) def main() -> None: expected_ruff_revision = check_output( ["git", "ls-tree", "main", "--format", "%(objectname)", "ruff"], cwd=TY_ROOT ).strip() actual_ruff_revision = check_output( ["git", "-C", "ruff", "rev-parse", "HEAD"], cwd=TY_ROOT ).strip() if expected_ruff_revision != actual_ruff_revision: print( f"The ruff submodule is at {actual_ruff_revision} but main expects {expected_ruff_revision}" ) match input( "How do you want to proceed (u=reset submodule, n=abort, y=continue)? " ): case "u": check_call( ["git", "-C", "ruff", "reset", "--hard", expected_ruff_revision], cwd=TY_ROOT, ) case "n": return case "y": ... case command: print(f"Invalid input '{command}', abort") return schemastore_repos = determine_git_protocol().schemastore_repos() schemastore_existing = TY_ROOT / "schemastore" if schemastore_existing.is_dir(): update_schemastore(schemastore_existing, schemastore_repos) else: with TemporaryDirectory(prefix="ty-schemastore-") as temp_dir: update_schemastore(Path(temp_dir), schemastore_repos) if __name__ == "__main__": main()