Astral's ty: A New Blazing-Fast Type Checker for Python

Astral's ty: A New Blazing-Fast Type Checker for Python

by Bartosz Zaczyński Publication date Sep 29, 2025 Reading time estimate 20m intermediate tools

After Ruff and uv, the Astral team is back with another Rust-based tool called ty, short for type check. This new tool delivers a lightning-fast static type-checking experience in Python, aiming to outpace existing tools in both performance and convenience. By incorporating ty into your daily workflow, you’ll catch type-related bugs earlier and get clearer feedback.

But is ty suitable for you? Take a look at the table below to make a quick decision:

Use Case Pick ty Pick Other Tools
Development or experimentation
Production use

At the time of writing, ty is available as an early preview release with hundreds of open issues. Despite being actively developed and boasting over ten thousand stars on GitHub, it’s still missing essential features and might occasionally fail.

As such, it’s not ready for full adoption in production yet, nor is it going to be a drop-in replacement for any of its competitors. Bugs can take you by surprise in unexpected ways! Additionally, because ty’s implementation is moving fast, some of the information you’ll find in this tutorial may become outdated over time.

If you’d like to get familiar with a new, robust, and promising type checker in your personal projects, then by all means give ty a try! Ready to dive in? Click the link below to grab the sample code you’ll be working with in this tutorial:

Take the Quiz: Test your knowledge with our interactive “Astral's ty Type Checker for Python” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Astral's ty Type Checker for Python

Test your knowledge of Astral's ty—a blazing-fast, Rust-powered Python type checker. You'll cover installation, usage, rule configuration, and the tool's current limitations.

Start Using ty in Python Now

Python is a dynamically typed language, so it requires third-party tools to perform type checking and other kinds of static code analysis. Recently, ty joined the club of external type-checking tools for Python. Despite being created by a private company, Astral, the tool itself remains open source and MIT-licensed.

Although ty is mostly written in Rust, you don’t need the Rust compiler or its execution environment to type check your Python projects with it. To quickly get started with ty, you can install it directly from PyPI with pip, preferably within an activated virtual environment:

Shell
(venv) $ python -m pip install ty

As long as you’re on one of the three major operating systems—Windows, macOS, or Linux—this command will bring a hefty binary script called ty into your virtual environment. The script is already compiled to machine code suitable for your platform with all the necessary dependencies baked in, so you can run it directly, just like any other command-line program.

To verify the installation, run the following command in your terminal emulator app:

Shell
(venv) $ ty --version
ty 0.0.1-alpha.21

You should see a version number similar to the one above appear in the output. Alternatively, if that doesn’t work, then try to execute ty as a Python module using the interpreter’s -m option:

Shell
(venv) $ python -m ty --version
ty 0.0.1-alpha.21

When you do, Python runs a tiny wrapper script that looks for the ty binary executable in your path and invokes it for you.

Note that the pip install command only installs ty into the given virtual environment, which is usually associated with a specific project. To make ty available globally from any folder on your computer, you’ll need to use a different approach. Check out the official documentation for more installation options.

From now on, you won’t see the virtual environment’s name (venv) in the command prompts for the rest of this tutorial. To keep the code blocks concise, it’ll be assumed that ty is already installed globally and available in your system’s path.

Alright. It’s time for the fun part: letting ty scrutinize some Python code to catch bugs before they sneak in.

Catch Typing Errors With ty

The command-line interface of ty is pretty minimal and straightforward, as you’ll notice when you run ty without providing any subcommands or options:

Shell
$ ty
An extremely fast Python type checker.

Usage: ty <COMMAND>

Commands:
  check    Check a project for type errors
  server   Start the language server
  version  Display ty's version
  help     Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version

The highlighted line indicates the only subcommand you really need to care about: ty check. It runs a number of type checks against your codebase using a comprehensive set of built-in rules, which you can fine-tune later to match your preferences.

To reveal more details about the ty check subcommand, use the --help flag as follows:

Shell
$ ty check --help
Check a project for type errors

Usage: ty check [OPTIONS] [PATH]...

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

Options:
      --project <PROJECT>
          Run the command within the given project directory.

          (...)

This displays a ton of options, which might feel a bit intimidating at first. Fortunately, you can ignore all of them for now by relying on ty’s sensible defaults and a clever discovery of Python code. The help page above will become more useful as you gain some experience with ty.

When you run the bare ty check command without any options from the root folder of your project, it’ll recursively scan all the files and directories within that project. For starters, you can try running it against the source code of the popular Django framework to see how ty works in a real-world scenario.

You can use Git to clone Django’s source code from GitHub if you haven’t already. Don’t forget to install Django and its dependencies into a virtual environment afterward:

Shell
$ git clone git@github.com:django/django.git
$ cd django/
$ python -m venv .venv/ --prompt django
$ source .venv/bin/activate
(django) $ python -m pip install .
(django) $ deactivate

Now, while you’re still in the top-level django/ folder, run the ty check command as follows:

Shell
$ ty check
WARN ty is pre-release software and not ready for production use.
⮑ Expect to encounter bugs, missing features, and fatal errors.
Checking ----------------------------------------------------- 2871/2871 files

(...)

Found 14249 diagnostics

Notice the prominent warning message that ty displays up top, serving as a reminder that this tool isn’t yet suitable for production environments. In this case, ty issued numerous errors and warnings, collectively called diagnostics, which have been omitted for brevity.

By the way, it’s worth appreciating how ty checks 2,871 Python files—averaging 175 lines of code each—in just a few seconds. Some of this time is actually spent printing the diagnostic messages on the screen. When you use ty’s silent mode (-qq), the measured time drops even further:

Shell
$ time ty check -qq

real    0m3.804s
user    0m12.224s
sys     0m1.531s

The discrepancy between the real and user times reported by the time command is a good thing. It indicates that ty leverages your multi-core processor architecture for parallel execution.

And here’s what a sample error message displayed by ty looks like by default:

Program Output
 1error[missing-argument]: No arguments provided for required parameters `year`, `month`, `day` of function `__new__`
 2  --> django/utils/dateparse.py:78:20
 3   |
 476 |         if match := date_re.match(value):
 577 |             kw = {k: int(v) for k, v in match.groupdict().items()}
 678 |             return datetime.date(**kw)
 7   |                    ^^^^^^^^^^^^^^^^^^^
 8   |
 9info: rule `missing-argument` is enabled by default

On most terminals, this output is colorful and resembles Ruff’s, which ty is based upon. In fact, the new type checker includes Ruff as a submodule on GitHub, meaning much of ty’s core implementation is developed within the Ruff codebase.

The structure of the above error message is the following:

  • Line 1 contains the severity level, which can be error, warning, or info, followed by a rule name in square brackets identifying the type of issue. The line ends with a human-readable description.
  • Line 2 indicates the location consisting of the file path, line number, and column number, which tell you exactly where the issue was found.
  • The following lines reveal a line-numbered code snippet, which shows a few surrounding lines of code for context. The caret symbol (^) highlights the exact code segment related to the issue.
  • Line 9 contains additional notes, such as why a particular rule was triggered.

Sometimes, this might be too much information shown at once. To reduce the noise, you can tell ty to use a more concise format:

Shell
$ ty check --output-format concise
WARN ty is pre-release software and not ready for production use.
⮑ Expect to encounter bugs, missing features, and fatal errors.
Checking ----------------------------------------------------- 2846/2846 files
django/apps/config.py:197:27: error[invalid-argument-type] Argument to function `issubclass` is incorrect: Expected `type`, found `None | (@Todo & ~None) | Unknown`
django/apps/registry.py:87:26: error[not-iterable] Object of type `Unknown | None` may not be iterable

tests/xor_lookups/tests.py:86:13: error[unresolved-attribute] Type `<class 'Number'>` has no attribute `objects`
Found 14249 diagnostics

With the --output-format switch, you can change the output style to a compact format that fits each diagnostic message on a single line. This can be particularly helpful for parsing the output programmatically in CI/CD pipelines, for example, with the grep command.

Each line still contains the corresponding rule name, allowing you to override how ty behaves by default. Next up, you’ll learn what you can do with this information.

Tweak the Type Checking Rules

Out of the box, ty assigns a default severity level to each of its rules, which you can view in the reference documentation. Here are a couple of examples:

Rule Severity Level
unresolved-attribute error
deprecated warning
division-by-zero ignore

Depending on the rule’s level, ty uses different colors to display the violation and takes a slightly different action. When the command detects an error, it returns with an exit code of 1. On the other hand, if there are only warnings and you haven’t used the --error-on-warning flag, then the command returns with an exit code of 0. Finally, problems with the ignore level aren’t reported.

By knowing the rule names, you can change their default levels using the corresponding command-line options. Specifically, you can elevate or degrade the default levels with the --error, --warn, and --ignore options. Each of these options can appear more than once within a single invocation:

Shell
$ ty check --ignore unresolved-attribute \
           --ignore=unresolved-import \
           --warn division-by-zero \
           --error deprecated

As you can see, listing the new rule levels directly on the command line can quickly get cumbersome and error-prone. Plus, this approach isn’t easily repeatable across a team of developers. You’ll often prefer tweaking the rules for your project using a persistent configuration file, such as pyproject.toml or ty.toml:

TOML pyproject.toml
# ...

[tool.ty.rules]
unresolved-attribute = "ignore"
unresolved-import = "ignore"
division-by-zero = "warn"
deprecated = "error"

If you have both files in your project, then ty.toml takes precedence over pyproject.toml. Note that you can also include a user-level configuration shared across multiple projects that can override the individual rules with their own configuration.

Both the command-line switches and the configuration files have a global effect on ty’s behavior across your entire project. To suppress rules only in a specific location in your code, you can write comments that start with a special marker on a given line. This marker comes in a few flavors:

  • # ty: ignore
  • # ty: ignore[rule1]
  • # ty: ignore[rule1, rule2, rules3, ...]

When you don’t specify any rule names in square brackets, then ty will disable all rules affecting that line. Otherwise, it’ll only disable the listed rules. Note that you can’t elevate rules to errors or warnings with comments like you did before with the command-line switches—you can only ignore them.

If the affected expression or statement spans multiple lines, then try putting your comment either on its first or last line:

Python adder.py
import warnings

@warnings.deprecated("Use the new_adder() instead")
def legacy_adder(x, y):
    return x + y

legacy_adder(  # ty: ignore
    42,
    555
)

legacy_adder(
    42,
    555
)  # ty: ignore

Unfortunately, commenting such multiline expressions doesn’t always work as expected at the moment. Quirks like this are to be expected in a pre-release version of ty.

As mentioned earlier, the bare # ty: ignore marker comment sets the level for all potential rule violations that might occur on the chosen line of code. Meanwhile, the official documentation recommends always explicitly listing one or more rules in square brackets:

Python adder.py
# ...

type Number = int | float

def new_adder(x: Number, y: Number) -> Number:
    return x + y

new_adder(a=42, b=555)  # ty: ignore[unknown-argument, missing-argument]

You use a comma to separate the listed rule names. This approach helps avoid potential ambiguity and prevents unintentional silencing of important red flags in your code.

At this point, you know a lot about tweaking the output and the default rule levels in ty. However, you glossed over a few important details about its convenient module discovery mechanism, which you’ll explore in the next section.

Narrow Down the Discovery Scope

You can narrow down the scope of the search by specifying particular files or directories as positional arguments, or by changing your working directory to a subdirectory. Conversely, by using the --exclude option, you can define glob patterns to be exempt from type checking, which can reduce unnecessary checks on files that are irrelevant to your current task:

Shell
$ cd tests/
$ ty check auth_tests/ runtests.py --exclude "**/test_*.py"
(...)

Found 10 diagnostics

This cuts down the output significantly, yet it’s still pretty verbose.

Regardless of where in your project you run the ty check command, the tool detects the configuration files, such as pyproject.toml, and remains fully aware of the overall project structure. It supports both src and flat layouts, among others. This is crucial for resolving imports correctly, letting you operate on relative paths.

When scanning, ty looks for plain Python files (.py), stub files (.pyi) with type signatures, and even Jupyter Notebooks (.ipynb). At the same time, it skips commonly ignored directories like virtual environments, which often contain Python’s own standard library and any installed third-party packages. You don’t want to type check those directly, after all.

However, since your code often depends on these third-party packages, the type checker still needs to access their source code—or their accompanying type stubs—to verify if your own code uses them correctly. The discovery of third-party modules in ty is also automated and involves multiple steps. Here are a few common scenarios:

  • Active Environment: If you run ty check from within an activated virtual environment or a Conda environment, then ty will automatically detect and include the packages installed in that environment.
  • Inactive Environment: If your project has a virtual environment stored in a local folder named .venv/ (prefixed with a dot), then ty will recognize it even if you haven’t activated that environment yourself. Note that ty doesn’t currently recognize the equally common venv/ folder as a virtual environment during the discovery phase. However, it ignores venv/ when scanning for files to type check.
  • Custom Interpreter or Environment: You can explicitly provide the path to a given python executable or a virtual environment’s home folder by using the --python option, but you can specify it only once.
  • System Python: Finally, ty falls back on your system Python to find any missing modules.

Apart from discovering first-party and third-party modules, ty makes a best effort to determine a suitable Python version for your project based on various configuration options, metadata, and the environment. At the same time, you can override it with the --python-version switch, providing a custom Python version as low as 3.7:

Shell
$ ty check --python-version 3.7
WARN ty is pre-release software and not ready for production use.
⮑ Expect to encounter bugs, missing features, and fatal errors.
Checking ----------------------------------------------------------- 1/1 files
error[invalid-syntax]
 --> python_version.py:3:1
  |
1 | from typing import Literal
2 |
3 | type Size = Literal["S", "M", "L", "XL"]
  | ^^^^ Cannot use `type` alias statement on Python 3.7 (syntax was added in Python 3.12)
  |
info: Python 3.7 was assumed when parsing syntax because it was specified on the command line

Found 1 diagnostic

This gives you control over which elements of the Python syntax and which modules in the standard library are enabled.

Similarly, you can use the --platform switch to enforce the desired operating system mode, effectively restricting the visibility of platform-specific objects in Python. This makes cross-platform type checking easier:

Shell
$ ty check --platform win32
WARN ty is pre-release software and not ready for production use.
⮑ Expect to encounter bugs, missing features, and fatal errors.
Checking ----------------------------------------------------------- 1/1 files
info[revealed-type]: Revealed type
 --> cross_platform.py:9:13
  |
7 |     x = "macOS or Linux"
8 |
9 | reveal_type(x)
  |             ^ `Literal["Windows"]`
  |

Found 1 diagnostic

The valid values match those of Python’s sys.platform attribute. When left unspecified, ty defaults to your native operating system. Alternatively, you can use the special value all to type check every platform-specific branch of code.

Finally, ty recognizes patterns defined in .gitignore and .ignore files. This means any files or directories listed in these files will be skipped during checks, just like with exclusions in the configuration. You can use .gitignore or .ignore to manage ignored files globally for your project, and ty will automatically honor those patterns unless you override this behavior with specific command-line options or configuration settings.

Beware of Limitations and Gotchas

While ty is exciting, remember that it’s still preview software. Here are a few important things to be aware of:

  • Not production-ready: Stick to mature tools like mypy, Microsoft’s Pyright, or Meta’s Pyre for mission-critical projects.
  • Bugs and missing features: Core functionality is still evolving, and quirks are inevitable.
  • Incomplete ecosystem support: IDE integration is limited outside of VS Code, and some typing features aren’t fully implemented yet.
  • Rapid changes: The tool is moving fast, so behavior and features may shift between versions.

That said, ty shines when you need fast, lightweight, and intuitive feedback in personal or experimental projects. Its shortcomings are likely to shrink over time, so keep an eye on updates!

Conclusion

Astral’s ty offers a fresh take on Python type checking, emphasizing speed and developer experience. While it’s still in preview and not yet ready to replace mature tools in production, ty’s fast feedback loop and intuitive output make it a compelling option for experimentation and personal projects.

In this tutorial, you’ve learned how to:

  • Install ty from PyPI for immediate use
  • Run the ty check command to scan projects for type errors
  • Read and interpret ty’s structured, color-coded diagnostics
  • Configure rules globally or inline to fine-tune checks
  • Limit scope and exclude files for faster, focused analysis
  • Override Python version and platform to test cross-environment compatibility

Keep in mind that ty is evolving rapidly. By the time you read this, new features may have landed, bugs may have been fixed, and IDE support may have improved. So stay curious, keep your tools updated, and don’t hesitate to explore the latest developments in its documentation and GitHub repository.

Frequently Asked Questions

Now that you have some experience with ty in Python, you can use the questions and answers below to check your understanding and recap what you’ve learned.

These FAQs are related to the most important concepts you’ve covered in this tutorial. Click the Show/Hide toggle beside each question to reveal the answer.

No. You shouldn’t use ty in production yet because it’s a preview release with missing features and known issues. You can use it in personal or experimental projects to get fast feedback.

You install ty from PyPI with pip, ideally inside a virtual environment, or follow the documentation for global installs. After installing, you can run ty with the --version flag to confirm that the binary is on your path.

You run ty check from your project root folder and let its discovery scan your code, stubs, and notebooks. You can pass paths or use exclude patterns to narrow the scope and switch to concise or quiet output to cut noise.

You set rule levels in pyproject.toml or ty.toml and override them with options like --error, --warn, and --ignore. You can silence a single line with a comment, such as # ty: ignore[rule1, rule2], to avoid muting unrelated checks.

You can install the official VS Code extension for integrated diagnostics. For other editors, you can run the ty language server via LSP, and in PyCharm, you’ll need the LSP4IJ plugin to connect.

Take the Quiz: Test your knowledge with our interactive “Astral's ty Type Checker for Python” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Astral's ty Type Checker for Python

Test your knowledge of Astral's ty—a blazing-fast, Rust-powered Python type checker. You'll cover installation, usage, rule configuration, and the tool's current limitations.

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About Bartosz Zaczyński

Bartosz is an experienced software engineer and Python educator with an M.Sc. in Applied Computer Science.

» More about Bartosz

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Master Real-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.


Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning

Related Topics: intermediate tools