RSSAmplifier

Bite code! · Aug 6, 2026

What’s up Python? __json__, __export__ and Astral stuff

0
Sign in to vote or save

Bite Code! · Bite code!

  • 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.

This PEP suggests an __export__ variable containing names of variables as strings, similar to __all__, to mark stuff public.

Example:

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 to import * 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:

Or allow the lazy loading of those references as we do for annotations.

Alternatively, a built-in would be nice:

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 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.

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 init now 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.

  • uv now 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, uv won’t let you install old pypy versions packaged as bzip2.

  • uv used 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 with uv run. Kind of a no-brainer.

  • uv now 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 add previously converted local dependencies into a project-relative path but from now on will preserve the form of the request in pyproject.toml and uv.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, uv now 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() and with 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.

  • 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_PEERS setting that will avoid accidental n+1 problems, in DB modes for ForeignKey.on_delete for 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.

Read the original on bitecode.dev

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.