What’s up Python ? Rust in CPython, immutable dicts, unpacking in comprehensions...
November, 2025
Summary
For the 3rd time, a proposal for adding an immutable mapping type to Python has landed.
We will be allowed to use unpacking in comprehensions in Python 3.15.
The debate of whether to introduce Rust to CPython has started, and it’s intense.
And moar stuff.
Looks like frozendict is back on the menu, boys
Python's distinction between mutable (can be modified after creation) and immutable (can’t be modified after creation) lives on the objects themselves, not the variable, like, say, in Rust with the mut keyword.
Strings and tuples are immutable. But byte arrays and lists are mutable.
Sets even have a frozenset counterpart:
>>> s = {1, 2, 3}
>>> s.add(4)
>>> s
{1, 2, 3, 4}
>>> s2 = frozenset(s)
>>> s2
frozenset({1, 2, 3})
>>> s2.add(4)
Traceback (most recent call last):
File “<python-input-4>”, line 1, in <module>
s2.add(4)
^^^^^^
AttributeError: ‘frozenset’ object has no attribute ‘add’It’s not for lack of trying. PEP 416 and PEP 603 were both previous attempts to bring an immutable mapping type to Python, but didn’t gather support.
Of course, now that we have a GIL-less version of Python, and immutable types being very useful for concurrency, maybe a new proposal could succeed. And tada! Victor Stinner submitted a new one: PEP 814.
This version is not controversial in design and mirrors frozenset.
frozendict()creates a new empty immutable mapping.frozendict(collection)creates a mapping from the passed collection object.Unpacking works:
frozendict(collection, **kwargs).Keys must be hashable and therefore immutable, but values can be mutable. Using immutable values creates a hashable
frozendict.items(),.keys(),.values()and.copy()exist and do what you expect.frozendict()can be compared todict().|behaves like+on tuples.Insertion order is preserved, it implements the collections.abc.Mapping protocol and supports pickling.
So, it’s the right time, by the right person, with the right proposal, which is why I think it will be accepted.
Personally, I want an immutable mapping for a single reason: to use them as default arguments and avoid having ruff forcing me to use MappingProxy by warning me to death. Because usable mutable default values are bad, and I’m a bad person.
Looks like unpacking in Comprehensions is... well you get it.
Except this one is already accepted.
PEP 798 – Unpacking in Comprehensions‘s title is pretty much the gist of it: we can use unpacking everywhere, why not in comprehensions?
After all, you can do this:
devices = [*phones, *laptops, *tablets, “commodore 64”, “steam deck”]So why not this?
devices = [*category for category in device_by_category]Well, it turns out it’s been a loooooong discussion. In fact, many long discussions.
To finally, eventually, just exactly do that.
It’s a nice QoL improvement, which should free us from flattening nested iterables with:
from itertools import chain
devices = list(chain.from_iterable(device_by_category))Or worse:
devices = [category for category in device_by_category for device in category]It’s planned for Python 3.15, and will work with all the usual flavors of comprehension:
[*it for it in its] # list comprehension
{*it for it in its} # set comprehension
{**d for d in dicts} # dict comprehension
(*it for it in its) # generator expressionYou may argue that “There should be one-- and preferably only one --obvious way to do it“ has been dead, incinerated, and the ashes buried.
But next time I’ll be looking to take a nested iterable and make it nice and flat, this will be the obvious way for me.
We trade the “preferably only one” away to stay relevant as time goes by. Having one consistent idiomatic solution displacing all the others while not breaking the world is the right direction to go.
And now they want Rust in Python
Because of course they do. We have Rust in the Linux project and Rust in the Git project, plus there is already a Rust implementation of Python.
It was only a matter of time before someone suggested CPython, called that way because it is implemented in C, should also make a first oxidation step.
My tone is tongue-in-cheek. Those moves have been made by very knowledgeable people with clear objectives and a well-defined experimental stage. And the pre-PEP discussion about adding Rust for CPython is no exception:
It’s championed by two core devs.
It advocates that CPython has had numerous bugs in the past that would have been impossible with Rust.
It suggests, as with other projects, to start very small and with Rust-based extension modules that are neither part of the core of CPython nor the standard library.
And it’s, of course, just a discussion of a topic that would have been brought up one day or another.
Guido himself commented:
I think this is a great development
While the core dev, and time zone Guru, Paul Ganssle notes that:
as someone who always introduces memory leaks and segfaults and such any time he writes any kind of C extension code, I welcome the idea
Because beyond the hype around Rust, there is real value: zero cost high-level datastructures, provable memory safety, robust concurrency... All hard problems to tackle that the language and its ecosystem have been very good at addressing.
Plus, Rust is not new anymore. It’s stable (v1.91), it’s 13 years old, and it’s been widely used in the Python community to create compiled extensions already, thanks to the PyO3 project. The two languages have been BFF for a while, and you’ll find well-respected figures of the community that have a foot in both: Armin Ronacher (flask), Samuel Colvin (pydantic), Charlie Marsh (uv)...
Personally, I have been consistently experiencing better quality from Rust-written software.
Maybe it’s the type of language that attracts people who are interested in getting the details right.
Or maybe the qualities of the language mean that if a project manages to reach the production stage, it will be better than an alternative that would reach the production stage because the minimal level of quality and checks required is better.
Or maybe the ecosystem is just very good.
Or maybe it’s all together, and something more.
Doesn’t matter.
The fact is, I did have a subjectively better experience with software written in Rust than in Python, JS, or even Go or Java.
So “written in Rust, BTW” will unironically make it more likely that I try some software.
Does that make it the right fit for Python? I’m not competent to answer, but I welcome the talk.
Of course, there are a lot of issues to address:
What does it mean for Python's support of exotic platforms? You can compile C on a toaster after all.
What’s going to happen to the API that C extensions use?
Rust has a notoriously slow compile time, which means slow feedback loops. How does that affect Python?
How much effort can be engaged for this, and when does one consider the ROI worth it? On what metrics?
How do you deal with the accumulated knowledge of people who have been on the project forever, experts in C, but with no intention to learn a new language?
What’s the timeline for all that?
All this is compounded by the fact that it’s touching a very old code base on a hugely popular project that affects billions of people, carried by community work. And if you needed even more intensity, this is, of course, also a strongly emotionally charged topic.
The discussion is already full of questions and answers, then more questions, and is quickly becoming one of the most active threads on discuss.python.org ever, with already 234 levels of pagination in less than a month.
To give you a context of how much of a heated debate it is, the discussion about removing the bloody GIL was 130 pagination levels. After almost two years.
So, unless you want to participate in the debate yourself, it’s better to wait and see until the dust settles a little because right now there is way too much movement to get a clear picture of what is and what can be.
Moar?
Mypy 1.19 has been released. It’s the last one to support Python 3.9, making the new fast cache stable (use
--fixed-format-cacheto activate it), and providing unstable support for PEP 747 (types to represent types).Python 3.15 alpha 2 is out. For now, the most notable features is the new profiler, and it looks pretty.
Python 3.13.10 and 3.14.1 provide the usual bug fixes and security patches.
VSCode Python extension gets a new Code Action that lets you quickly convert
import *into an explicit import.Django gets the usual security releases for 5.2.9, 5.1.15, and 4.2.27. Yep, they still support 4 because they are awesome.
Django 6 is out with Content Security Policy support, the new Template Partials (that will be nice with HTMX), and integrated background tasks, which we talked about on the blog previously.
yt-dlp, the fantastic Python video downloader CLI tool, will now require a JS runtime for full youtube support.
