What’s up Python? Astral's new type checker, McGugan's new tool and Django new CSRF protection
December 2025
Summary
It’s the end of the year as you know it, and we close 2025 with the release of ty, the little brother of ruff and uv, that will take care of your code’s type. But, surprise, it goes beyond that!
On top of this, Django seems to move away from the dreaded CSRF token.
And finally, the author of rich and textual has a new AI toy, just for you: toad, an agent-agnostic chat UI.
Plus, of course, moar stuff.
Astral officially releases its type checker
After the exceptional ruff and uv, a lot of people were eagerly waiting for Charlie Marsh’s team to provide an alternative to mypy and pyright. The project, first known by the codename redknot, was developed in the open, so you could actually get the branch and install it yourself to test it at any time, but it was really immature.
However, this month they posted ty‘s first release, in beta quality, and in typical Astral fashion, they already did 5 more since then.
So what to make of it?
It’s as fast as you can expect from their work. Virtually instant. If you have used the painfully slow competition in the past, you get once again this “wow” feeling in the transition.
And it’s unfinished. Typing is complicated, ty has many gaps and holes that will still take months of iteration to fix. That’s why they have a beta.
But while I wouldn’t use it with my clients just yet, I’m definitely using it in all my personal projects already, because the convenience of it is worth the missing features.
You see, ty is not just a command-line type checker; it’s also a Language Server Protocol service, which means you can replace the Python support of the best editors with it, including pylance in VSCode. It even has its dedicated extension.
This means not only is your code type checked with ty in a fast and efficient manner, but also “go to definition”, display of inlays, docstring tooltips (including non-standard attribute docstrings), and, more importantly, completion and automatic import, are done with it.
There is currently no alternative in the market to this LSP. It will let you type any class name in a file and instantly offer you a list of names from anywhere in your code base and deps, while adding the import automatically in the blink of an eye.
Pylance and PyCharm sort of do that, but it’s less precise, reliable, and much slower.
This is the killer feature, right now. Not the type checking.
Now, don’t get me wrong, the type checking is useful, but it’s the cherry on the cake.
ty‘s type checking is, as I said, incomplete. For example, Self support is to be improved, but as you can see, they are already on it.
However, it can already do useful things that others can’t.
Let’s take this example from their doc:
class Person:
name: str
class Animal:
species: str
def greet(being: Person | Animal | None):
if hasattr(being, "name"):
print(f"Hello, {being.name}!")
else:
print("Hello there!")As a human, you know that if it’s either a Person, an Animal or None, this code is valid. And if the object has an attribute name, it’s a Person.
Yet both pyright and mypy are not smart enough to understand this.
pyright stays stuck on the idea that if it’s an animal, being.name is going to fail:
pyright greet.py
greet.py
greet.py:9:31 - error: Cannot access attribute "name" for class "Animal"
Attribute "name" is unknown (reportAttributeAccessIssue)
greet.py:9:31 - error: "name" is not a known attribute of "None" (reportOptionalMemberAccess)
2 errors, 0 warnings, 0 informationsAs pyright, mypy thinks that None can’t have a name, so this can’t be right.
mypy greet.py
greet.py:9: error: Item "None" of "Person | Animal | None" has no attribute "name" [union-attr]
Found 1 error in 1 file (checked 1 source file)ty sees the hasattr() check and resolves the type.
In general ty chooses practicality by reporting the most useful, relevant problems instead of trying to be pedantic about types and printing a wall of false positives. Soon, we might be able to opt in for a stricter policy for projects that requires to awaken the OCD warrior in you.
But meanwhile, this is accepted by ty:
a = [1]
a.append("i'm not an int")
a.pop() + 1mypy and pyright will tell you to remove the ambiguity, either by annotating a more lenient type like list[int|str] and therefore get flagged on the pop(), or removing the offending append().
Practice will tell if Pareto or strictness wins on this one. Maybe I will like having both behaviors available, as I do hate false positives, but sometimes want a very strong safety net, depending on contexts. Maybe it will be... a tie.
Meanwhile, you know you can give it a try with uvx ty check and get decent type checking at light speed with much better error messages.
Django is getting less obnoxious CSRF protection
Django has best-in-class security default settings; one of the numerous reasons it’s still my first pick. One of them is the prevention of Cross Site Request Forgery, an attack that targets cookie authentication and permission systems.
It works by abusing the fact that non GET HTTP requests (POST for most scenarios) can be sent from browsers visiting a malicious “evil.com” site, to another “target.com” site, BUT with parameters from “evil.com”, and YET with valid cookies from the user for “target.com”.
Django mitigates that by adding a unique sequence of characters (the csrf token) in every forms that come from the “target.com” site and checks if it exists in the requests it receives. Since “evil.com” cannot have the token, its requests are denied.
This feature is one of the most annoying things to deal with, however, and people are quickly tempted to disable this protection to avoid the dreaded “CSRF token missing or incorrect” error message.
But a better way has existed for two years, a simpler header to set:
Sec-Fetch-Site: same-origin, supported by 95% of web browsers, with a fallback on the Origin header since it’s been here for a long time.
And as the OWASP recommendation is being updated to suggest you can actually do that in the corporate world, Django is moving in the same direction.
What does it mean for us?
Short term, not much, as it won’t be merged for the next release, but long term, something like django-modern-csrf, meaning a mostly transparent CSRF protection. No token, no form manipulation, just a check that the requests come from your site, which is done automatically for you.
Now, to be honest, my first reaction was a bolt of joy, akin to when I heard Python was going to use UTF8 everywhere automatically. Finally, fewer errors! No config! Beginners will not have to suffer anymore!
But then I realized something:
CSRF don’t affect SPA that don’t rely on cookies for auth. Which is most of them. And those are pretty popular.
I can’t recall the last time I had to configure this, since AI will automatically settle the matter for me.
So in the modern world, CSRF token errors were kinda becoming a non-issue anyway.
It’s very possible that my enthusiasm about this development is more a reflection of how old I am than a real game-changer.
Look, I’m happy about it, ok?
Where there is a Will McGugan is a way
In a recent post, textual and rich‘s author announced the release of his new Python project, a AI chat client using textual named toad.
The UI is super clean:
It’s compatible with all big names in the LLM industry, thanks to the Agent Client Protocol.
It has code coloration, the ability to edit back previous entries, completion for all commands (including @ a file), pass-through calls to the shell, and all the nice ergonomics you expect from this terminal whisperer.
Of course, you can install it in a non-python related way, but, since it’s python you can also give it a quick try with:
uv tool install -U batrachian-toad --python 3.14
toadI had to update node to a version > 20 for it to work with Claude (since their client uses JS), so be warned. But toad does offer to configure the rest for you.
A few months ago, I had an interview with Will where he explained his whole journey, and how it continues with Toad, and I haven’t published it yet. So I guess it’s time.
Less is moar
This time, we swear, Python will be faster. 15% for python 3.15. On windows.
3.14.2 and 3.13.11 are out with a bunch of bug fixes.
Python 3.15 alpha 3 is out, with minor new features.
