Python 3.13 that didn't make the headlines

Resume

  • The appearance of long-awaited features from the new REPL in the PDB

  • A large number of fixes for shutil, so you can finally leave prayers when using it

  • Several small multithreading improvements

  • The new annotation syntax allows the use of lists and lambda functions.

  • Python 3.13 still hasn't gotten significantly faster. Alas.

By tradition

Python 3.13 is a great release, full of various features and improvements, but there are already a ton of articles that go into detail release notes. If you need a good squeeze – have RealPython has a good articlebut I don’t see the point of going through them again in this article.

So we won't talk about the new REPL, no-GIL build, experimental JIT compiler, deprecated stuff, new type system goodies or improved error messages (as always, my favorite).

Instead, I read a short book that they call a changelog and we look at something that many have not talked about, but interested me personally.

Make debugging great again

Despite the spartan ergonomics, I really love pdb and even wrote a good one introduction to it.

But if you've ever tried this:

try:
    1 / 0
except ZeroDivisionError as e:
    breakpoint()

You know perfectly well what will happen when you try to read e:

-> breakpoint()
(Pdb) e
*** NameError: name 'e' is not defined

This is infuriating, especially because you usually encounter this in not the best frame of mind.

And it's finally fixed.

Glory to Apophis, Quetzalcoatl and Jormungand!

But that's not all. PDB itself also received some improvements:

All this in itself is a good reason to use 3.13.

A little love for working with the file system

It would seem that a 30-year-old language should have no problems when working with paths and files, but it turns out there is always room for improvement.

Module shutilwhich provides high-level FS operations such as recursive deletion or copying, has undergone the following changes: manymany bugs was fixed (especially related to error handling during recursion), and also added some options (for example, it is now possible to choose how to process symlinks).

I was always wary of using shutil because of such “features”. Recursive immersion was broken in one way or another. With these fixes I will be able to try using it again as it is really comfortable when it works.

Likewise for zipfile.Path – a pathlib-compatible wrapper for zip file traversal, which you most likely didn't know existed since its introduction in version 3.8. And you didn’t know about her for a very simple reason – there was nothing good about her. But version 3.13 gave us a lot of QoL patches for it, significantly improving directory handling, which previously required a lot of gestures. So now it can be used more often.

And finally, herself pathlib received many small optimizations, concerning productivitymany operations under the hood now use strings instead of objects Path. Serialization should also become faster. I like pathlib, but the bottleneck option is known issueso that's great news. I myself have not measured performance in practice, so I will refrain from commenting for now.

But it's not just about performance – there have also been some nice changes to the API. For example, Path.glob() And rglob() now accept path-like objects as patterns, Path.glob() now returns both files and directories if you add to the end of the pattern **and also a method was added Path.from_uri().

Small wins in concurrency

Annotation changes that no one asked for

Remember that annotations weren't originally limited to just types? This was an experiment to test what people would use them for and so they accept arbitrary Python expressions.

But it doesn't seem to be arbitrary enough, since the ability to use lists And lambda functions.

The bug report ticket is quite funny:

The following code causes a SystemError during compilation.

class name_2[*name_5, name_3: int]:
    (name_3 := name_4)

    class name_4[name_5: name_5]((name_4 for name_5 in name_0 if name_3), name_2 if name_3 else name_0):
        pass

What are you saying, Frank, what are you saying…

Well, let's see what other ways the developers will come up with to mock this functionality.

Disappointments

Performance improvements are not that greatas expected in this release – as in 3.12, we got only a small improvement in traditional benchmarks for async operations (hopefully this doesn't hurt my optimism for pathlilb). Speeding up Python is much, much more difficult than Guido expected. This could be understood from past failed attempts, so it is not particularly surprising.

Like me mentioned in the last post, there really were incremental GC changes canceledbecause they did not give the expected results.

Moreover, despite the many improvements to import in the changelog, my measurements did not show a significant difference in the startup time of Python on my machine.

Well, on top of all this there are inevitable breakdowns:

If you read the changes carefully Path.glob() Above, you may notice that the return value can vary significantly and I'm guessing this will break someone's code.

Well, a few other details that some may not like:

  • Usage support pathlib. Objects path can't do it anymore used for context management. Not that it would be useful. And in general it was confusing. So it's probably a good change, but You know.

  • Launching new threads and processes using os.fork() during interpreter shutdown (for example, from handlers atexit) no longer supported.

  • Positional arguments maxsplit, count And flags For re.split(), re.sub() And re.subn() marked as obsolete in favor of using keywords. Not removed yet, just a warning. Seems a bit redundant to me.

  • Likewise for all arguments sqlite3.connect()except the first one.

  • Obsolescence undocumented functions glob.glob0() And glob.glob1(). I hated them because they confused my students, so I'm glad about this change, but still.

  • Files .pth with names starting with a dot or having the attribute hidden are now ignored for safety reasons.

  • C API headers have been cleanedso we are waiting for dragons on the other side.

Language development is always a delicate balance between clean code, modern features and lack of technical debt on the one hand, and maintaining enough stability to maintain the productivity of the community on the other.

There will always be people who will say that the language got stuck in the past, and those who will ask why everything was broken again. There's no winning here. Language development is a thankless job.

Various pleasant changes

Let's end on a positive note. I don't have any category for these changes:

What surprised me most when reading the changelog for versions 3.12 and 3.13 was the focus on improving existing functionality. A large number of bug fixes, small API changes, attempts to slightly improve performance, code cleanup and removal of obsolete code. Even the famously bad documentation argparse got better.

After years of chasing new features with async, typing, the walrus operator and multiple interpreters, I expected nothing more than an increase in this trend from new people on the development team. But instead we got improved error messages, an improved PDB, a more flexible parser, an improved REPL, and so on.

And for me this is a sign that they care.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *