What’s up Python? __json__, __export__ and Astral stuff
July, 2026
Summary
Maybe we are going to get official symbol export in Python.
And maybe a
__json__method.
But I doubt it.
However, we are going to have better Astral tools, a very nice new Django version, and a better protobuf Python wrapper.
PEP 842 – Module Exports
This PEP suggests an __export__ variable containing names of variables as strings, similar to __all__, to mark stuff public.
Example:
__export__ = ["CANCELLED_ABSOLUTE_GEMS", "CovertShowOpinionExpresser"]
CANCELLED_ABSOLUTE_GEMS = [
"Dead like me",
"Pushing daisies",
"Firefly",
"Better Off Ted",
"Dirk Gently's Holistic Detective Agency",
"Extraordinary (TV series)"
]
OVERRATED_COMMERCIAL_SUCCESSES = [
"Euphoria",
"Attack on Titan",
"Stranger Things",
"The Bear",
"Succession"
]
class CovertShowOpinionExpresser:
...This means you could now import CANCELLED_ABSOLUTE_GEMS and CovertShowOpinionExpresser but not OVERRATED_COMMERCIAL_SUCCESSES.
It’s like the opposite of the _ prefix: you mark what is public instead of what is private.
There are several things to know about the proposal:
__export__remains optional.__all__will not go away, but it’s limited toimport *so__export__fills the gap.__export__can be any iterable or object that can__contain__. A tuple or set would work.dir()will not show anything that is missing from__export__if it is declared.If a module defines
__export__but does not define__all__, the__all__will be assigned to__export__.So this is enforced at runtime, but it doesn’t prevent access if you want to hack things; you can still get attributes with metaprogramming.
I’d like to have a whitelist for public names instead of having to constantly make sure my blacklist is up to date. But there are a few things I don’t like with this.
First, because they want to keep regular Python semantics and allow this entry to be at the top of the file, they need to be strings instead of references. I don’t like that at all.
I’d rather have:
CANCELLED_ABSOLUTE_GEMS = [
"Dead like me",
"Pushing daisies",
"Firefly",
"Better Off Ted",
"Dirk Gently's Holistic Detective Agency",
"Extraordinary (TV series)"
]
OVERRATED_COMMERCIAL_SUCCESSES = [
"Euphoria",
"Attack on Titan",
"Stranger Things",
"The Bear",
"Succession"
]
class CovertShowOpinionExpresser:
...
__export__ = [CANCELLED_ABSOLUTE_GEMS, OVERRATED_COMMERCIAL_SUCCESSES]Or allow the lazy loading of those references as we do for annotations.
Alternatively, a built-in would be nice:
CANCELLED_ABSOLUTE_GEMS: export[list[str]] = [
"Dead like me",
"Pushing daisies",
"Firefly",
"Better Off Ted",
"Dirk Gently's Holistic Detective Agency",
"Extraordinary (TV series)"
]
OVERRATED_COMMERCIAL_SUCCESSES: list[str]= [
"Euphoria",
"Attack on Titan",
"Stranger Things",
"The Bear",
"Succession"
]
@export
class CovertShowOpinionExpresser:
...Here it’s a battle between an index of public things at the top for quick scanning, or locality of behavior. I tend to opt for the second one, because it’s easier to maintain, and tooling can help with the scanning part. Apparently Guido agrees.
PEP 837 – Extensible JSON serialization
PEP 837 proposes a __json__ magic method to declare how your type should be translated to JSON. It exposes its motivation pretty well, so I’m going to quote it:
Serializing anything beyond the basic types (dict, frozendict, list, tuple, str, int, float, bool, None) with json.dumps() today requires either passing a default= function to every call, or subclassing json.JSONEncoder and threading the subclass through every call site
This would therefore allow any type in the stdlib to declare how to serialize itself to JSON, and would allow you to transparently and magically deal with Decimal, datetime and Enum. On top of that, any lib could declare the method for their own type, automatically gaining compatibility with the whole ecosystem.
It’s a nice, simple, composable concept.
But there is a problem. Why just JSON? Why not TOML? Why not YAML? Why not anything from unpack and pickle? I get JSON is ubiquitous, but that screams of a decision that will not age well.
On top of that, JSON is a nested format, which means you will need recursion, and Python has a default recursion limit.
I think I would rather see a general serialization mechanism, with a few default formats supported by the stdlib you can extend later. Or __to/from_builtins__ method to turn the object into a built-in Python object, then the format decides what to do for bytes conversion.
And the discuss thread is actually raising those questions, so I trust it will be taken into consideration.
You missed Astral news? We got Astral news
uv moved to 0.12, which doesn’t look like much, but the team took the opportunity to do a lot of cleanup and break compat. Hard to believe it’s still sub v1, right?
It’s a big release, but among all the things:
uv initnow defines itself as a build system by default. It’s actually quite nice, because it means any project can be turned into a wheel easily now.uvnow doesn’t support legacy source formats like .tar.bz2 and .tar.xz to match PEP 625. It now only accept .tar.gz, plus wheels and other ZIP archives can no longer contain entries compressed with bzip2, LZMA, or XZ. This might affect 0.0001% of users, but those will be sad. In the same vein,uvwon’t let you install old pypy versions packaged as bzip2.uvused to reject packages named “python”, and now adds to this list “Python”, “python.py”, or “Python.exe” so you can’t shadow the interpreter anymore withuv run. Kind of a no-brainer.uvnow requires a valid pylock.toml file. It won’t try to make up for mistakes, like missing mandatory fields like “packages” or bad naming. Which again makes a lot of sense.uv addpreviously converted local dependencies into a project-relative path but from now on will preserve the form of the request inpyproject.tomlanduv.lock. This is probably going to bite a few people, but it was a source of bugs, so good for them.On Linux and macOS,
uvnow attempts to raise the soft open-file limit at startup toward the hard limit. This is basically because it’s very fast, plus opens a lot of files and subprocesses, which on huge projects can cause issues.
And it adds one super dupper feature: you can now uv upgrade multiple packages at once, meaning upgrade all production dependencies, or exclude selected dependencies. Migration will get a lot easier with this.
Of course, they are buzzzzy bees, so that’s no all.
Tthey also bumped ruff to 0.16 with breaking changes, particularly activating A LOT more rules by default. From 59 to 413 to be exact, or a 700% increase, so buckle up on your next code review.
Unrelated but I find 413 being exactly divisible by 59 very disturbing.
Here is a full list, but the ones that jumps immediatly to my attention:
exec()usage is now called out.blind excepts will paint your terminal with blood.
assigning to
os.environ? That’s a red card.using
strip()with multiple characters is a no.you will have to justify those mutable arguments.
you can’t forget a
breakpoint()anymore.you really don’t need those
if x: True else: False.but you really need
enumerate()andwith open()
Those are very good things! I activate them manually all the time.
I really like most of the default rules, and I believe it will tremendously increase the quality of code bases all around, especially since LLMs are generating a lot of code now and those guardrails keep them in check.
The usual moar
Python 3.15 RC1 is here. Don’t wait until October to try it out, it’s feature complete now with lazy imports, frozen dicts, default utf8 everywhere, the new sampling profiler, and unpacking in comprehension. Lots of things to play with besides checking your stack compatibility with it.
There is a typing survey going around. If you want to influence the future of Python typing, voice your opinion now; it takes 10 minutes.
Django 6.1 is out. It comes with a niiiiice
FETCH_PEERSsetting that will avoid accidental n+1 problems, in DB modes forForeignKey.on_deletefor extra perfs, and different email backends (superb for testing!).A new protobuf package appears: protobuf-py, with a much nicer API than the official Google implementation, but still full standard compliance. It looks like idiomatic Python, it type hints like regular Python and generates vendor-generated code like a regular Python module.
