Python 3.13.0 Is Released(docs.python.org) |
Python 3.13.0 Is Released(docs.python.org) |
The language itself is (more than) complex enough already - I hope this focus on implementation quality continues.
" Python now uses a new interactive shell by default, based on code from the PyPy project. When the user starts the REPL from an interactive terminal, the following new features are now supported:
Multiline editing with history preservation.
Direct support for REPL-specific commands like help, exit, and quit, without the need to call them as functions.
Prompts and tracebacks with color enabled by default.
Interactive help browsing using F1 with a separate command history.
History browsing using F2 that skips output as well as the >>> and … prompts.
“Paste mode” with F3 that makes pasting larger blocks of code easier (press F3 again to return to the regular prompt). "
Sounds cool. Definitely need the history feature, for the few times I can't run IPython.
https://www.bitecode.dev/p/happiness-is-a-good-pythonstartup or search for a gist
> The new REPL will not be implementing inputrc support, and consequently there won't be a vi editing mode.
https://github.com/python/cpython/issues/118840#issuecomment...
# bisect.py
...
# main.py
import random
with: Traceback (most recent call last):
File ".../foo.py", line 1, in <module>
import random
File "/usr/lib/python3.12/random.py", line 62, in <module>
from bisect import bisect as _bisect
ImportError: cannot import name 'bisect' from 'bisect'
This is very frustrating because Python stdlib is still very large and so many meaningful names are effectively reserved. People are aware of things like "sys" or "json", but e.g. did you know that "wave", "cmd", and "grp" are also standard modules?Worse yet is that these errors are not consistent. You might be inadvertently reusing an stdlib module name without even realizing it just because none of the stdlib (or third-party) modules that you import have it in their import graphs. Then you move on to a new version of Python or some of your dependencies, and suddenly it breaks because they have added an import somewhere.
But even if you are careful about checking every single module name against the list of standard modules, a new Python version can still break you by introducing a new stdlib module that happens to clash with one of yours. For example, Python 3.9 added "graphlib", which is a fairly generic name.
It was ultimately rejected due to issues with how it would need to change the dict object.
IMO all the rejection reasons could be overcome with a more focused approach and implementation, but I don't know if there is anyone wishing to give it another go.
There may be a person in the world panicking that they need to be on Python 3.13 and also need to parse Amiga IFF files, but it seems unlikely.
Looking forward to JIT maturing from now onwards.
As do I.
I suppose only time will tell if that effort succeeds. But the intent is promising.
This is a win for the DX, but this is not yet widely used. For example, "TypeGuard[" appears in only 8k Python files on GitHub.[2]
[0] -- https://docs.python.org/3.13/library/typing.html#typing.Type...
[1] -- https://docs.python.org/3.13/library/typing.html#typing.Type...
[2] -- https://github.com/search?q=%22TypeGuard%5B%22+path%3A*.py&t...
It's day and night compared to typeguard.
Also the dev is... Completely out of this world
Pylance with pyright[0] while developing (with strict mode) and mypy[1] with pre-commit and CI.
Previously, I had to rely on pyright in pre-commit and CI for a while because mypy didn’t support PEP 695 until its 1.11 release in July.
https://github.com/python/cpython/issues/84904
(Don't let the associates with asyncio throw you: that was merely the code in which it was first found; later code transcends it.)
Would be nice to see performance improvements for libraries like FastAPI, NetworkX etc in future.
And will python 3.14 be named pi-thon 3.14. I will see myself out.
Specifically, pendulum hasn't released a wheel yet for 3.13 so it tried to build from source but it uses Rust and the Python docker image obviously doesn't have Rust installed.
I really like using Python, but I can’t keep using it when they just keep breaking things like this. Most people don’t read all the release notes.
> Python 3.13 was released on October 7, 2024
Otherwise, there's always the excellent `pyenv` to use, including this person's docker-pyenv project [1]
[0] https://hub.docker.com/layers/library/python/3.13.0rc3-slim-... [1] https://github.com/tzenderman/docker-pyenv?tab=readme-ov-fil...
What I meant is: While I am already inside a container running Debian, can I ...
1: ./myscript.py
2: some_magic_command
3: ./myscript.py
So 1 runs it under 3.11 (which came with Debian) and 2 runs it under 3.13.I don't need to preserve 3.11. some_magic_command can wrack havoc in the container as much as it wants. As soon as I exit it, it will be gone anyhow.
The in a sense, the question is not related to Docker at all. I just mentioned that I would do it inside a container to emphasize that I don't need to preserve anything.
Am I seeing a cached version and you see 3.13 ? Cause I can't see it on the homage page download link either.
While it’s not perfect, I know a few other people people who do “set up lots of data structures, including in libraries, then make use of the fact multiprocessing uses fork to duplicate them”. While fork always has sharp edges, it’s also long been clearly documented that’s the behavior on Linux.
both fork() and spawn() are just wrappers around clone() on most libc types anyway.
spawn() was introduced to POSIX in the last century to address some of the problems with fork() especially related to multi threading, so I an curious how your code is so dependent on UTM, yet multi threading.
It use fork in Python multiprocess, because many packages can't be "pickled" (the standard way of copying data structures between processes), so instead my code looks like:
* Set up big complicated data-structures.
* Use fork to make a bunch of copies of my running program, and all my datastructures
* Use multiprocessing to make all those python programs talk to each other and share work, thereby using all my CPU cores.
Still, this one doesn’t seem too bad. Add method=FORK now and forget about it.
So much perl clutching. Just curious, since I guess you've made up your mind, what's your plan to migrate away? Or are you hoping maintainers see your comment and reconsider the road-map?
Rust isn't perfect (no language is), but they do seem to try much harder to not break backwards compatability.
I.e. we currently run 3.11 and will now schedule work to upgrade to 3.12, which is expected to be more or less trivial for most services.
The rationale is that some of the (direct and transitive) dependencies will take a while to be compatible with the latest release. And waiting roughly a year is both fast enough to not get too much behind, and slow enough to expect that most dependencies have caught up with the latest release.
Python libraries support https://pyreadiness.org/3.13/
Which is mostly latest_major - 1, adjusted to production constraints, obviously. And play with latest for fun.
I stopped using latest even for non serious projects, the ecosystem really needs time to catch up.
No, because it varies widely depending on your use case and your motivations.
>Is it usually best to wait for the first patch version before using in production?
This makes it sound like you're primarily worried about a situation where you host an application and you're worried about Python itself breaking. On the one hand, historically Python has been pretty good about this sort of thing. The bugfixes in patches are usually quite minor, throughout the life cycle of a minor version (despite how many of them there are these days - a lot of that is just because of how big the standard library is). 3.13 has already been through alpha, beta and multiple RCs - they know what they're doing by now. The much greater concern is your dependencies - they aren't likely to have tested on pre-release versions of 3.13, and if they have any non-Python components then either you or they will have to rebuild everything and pray for no major hiccups. And, of course, that applies transitively.
On the other hand, unless you're on 3.8 (dropping out of support), you might not have any good reason to update at all yet. The new no-GIL stuff seems a lot more exciting for new development (since anyone for whom the GIL caused a bottleneck before, will have already developed an acceptable workaround), and I haven't heard a lot about other performance improvements - certainly that hasn't been talked up as much as it was for 3.11 and 3.12. There are a lot of quality-of-implementation improvements this time around, but (at least from what I've paid attention to so far, at least) they seem more oriented towards onboarding newer programmers.
And again, it will be completely different if that isn't your situation. Hobbyists writing new code will have a completely different set of considerations; so will people who primarily maintain mature libraries (for whom "using in production" is someone else's problem); etc.
Old Systems Admins like me have been following this simple rule for decades. It's the easiest way at scale.
If your assumption is correct, then I'm anxiously waiting for having the default Python executable in Ubuntu, for example, being licensed under a non-copyleft license. Then one would be able to build proprietary-licensed executables via PyInstaller much more easily.
Do you happen to have discrepancies between both? (e.g. an error raised by one and not the other)
The python multiprocessing module has been problematic for a while, as the platform abstractions are leaky and to be honest the POSIX version of spawn() was poorly implemented and mostly copied the limits of Windows.
I am sure that some of the recent deadlocks are due to this pull request as an example that calls out how risky this is.
https://github.com/python/cpython/pull/114279
Personally knowing the pain of fork() in the way you are using it, I have moved on.
But I would strongly encourage you to look into how clone() and the CLONE_VM and CLONE_VFORK options interact, document your use case and file an actionable issue against the multiprocessing module.
Go moved away from fork in 1.9 which may explain the issues with it better than the previous linked python discussion.
But looking at the git blame, all the 'fixes' have been about people trading known problems and focusing on the happy path.
My reply was intended for someone to address that tech debt and move forward with an intentional designed refactoring.
As I just focus on modern Linux, I avoid the internal submodule and just call clone() in a custom module or use python as glue to languages that have better concurrency.
https://bugs.python.org/issue35537
My guess is that threads in Cython are an end goal. While setting execution context will get you past this release, fork() has to be removed if the core interpreter is threaded.
The delta between threads and fork/exec has narrowed.
While I don't know if that is even an option for you, I am not seeing any real credible use cases documented to ensure that model is supported.
Note, I fully admit this is my own limits of imagination. I am 100% sure there are valid reasons to use fork() styles.
Someone just needs to document them and convince someone to refactor the module.
But as it is not compatible with threads, has a ton of undefined behavior and security issues, fork() will be removed without credible documented use cases that people can weigh when considering the tradeoffs.
Someone using Python doesn't "just use Rust", there are very clear pros and cons and people already using Python are doing so for a reason. It is sometimes helpful to have type checks in Python though.
> Use beartype to assure the quality of Python code beyond what tests alone can assure. If you have yet to test, do that first with a pytest-based test suite, tox configuration, and continuous integration (CI). If you have any time, money, or motivation left, annotate callables and classes with PEP-compliant type hints and decorate those callables and classes with the @beartype.beartype decorator.
Don't get me wrong, I think static type checking is great. Now if you need to add a decorator on top of each class and function AND maintain 100% code coverage, well that does not sound like "zero-cost" to me. I can hardly think of a greater cost just to continue dynamically typing your code and maintain guarantees about external dependencies with no type hints.
And anyway beartype has a mechanism to run a hook once and it will automagically decorate all functions.
However, changing the default silently just means people's code is going to change behaviour between versions, or silently break if someone with an older version runs their code. At this point, it's probably better to just require people give an explicit choice (they can even make one of the choice names be 'default' or something, to make life easy for people who don't really care).
That is the thing about fork(), spawn(), and even system() being essential wrappers around clone() in glibc and musl.
You can duplicate the behavior of fork() without making the default painful for everyone else.
In musl systems() calls posix_spawn() which calls clone().
All that changes is replacing a legacy call fork() that is nothing more than a legacy convenience alias with real issues and foot guns with multiple threads.
But the problem remains, because these warnings - whether they come from linters or Python itself - can only warn you about existing stdlib modules. I'm not aware of any way to guard against conflicts with any future new stdlib modules being added.
Everyone must now pay the mental cost of multithreading for the chance that you might want to optimize something.
That hasn't been true for many variable accesses for a very long time. LOAD_FAST, LOAD_CONST, and (sometimes) LOAD_DEREF provide references to variables via pointer offset + chasing, often with caches in front to reduce struct instantiations as well. No hashing is performed. Those access mechanisms account for the vast majority (in my experience; feel free to check by "dis"ing code yourself) of Python code that isn't using locals()/globals()/eval()/exec() tricks. The remaining small minority I've seen is doing weird rebinding/shadowing stuff with e.g. closures and prebound exception captures.
https://github.com/python/cpython/blob/10094a533a947b72d01ed...
https://github.com/python/cpython/blob/10094a533a947b72d01ed...
So too for object field accesses; slotted classes significantly improve field lookup cost, though unlike LOAD_FAST users have to explicitly opt into slotting.
Don't get me wrong, there are some pretty regrettably ordinary behaviors that Python makes much slower than they need to be (per-binding method refcounting comes to mind, though I hear that's going to be improved). But the old saw of "everything is a dict in python, even variable lookups use hashing!" has been incorrect for years.
I'm assuming that by "everyone" you mean everyone who works on the Python implementation's C code? Because I don't see how that makes sense if you mean Python programmers in general. As far as I know, things will stay the same if your program is single-threaded or uses multiprocessing/asyncio. The changes only affect programs that start threads, in which case you need to take care of synchronization anyway.
The mental cost of multithreading is there regardless because GIL is usually at the wrong granularity for data consistency. That is, it ensures that e.g. adding or deleting a single element to a dict happens atomically, but more often than not, you have a sequence of operations like that which need to be locked. In practice, in any scenario where your data is shared across threads, the only sane thing is to use explicit locks already.
That sounds like a fantastic reason to make it run faster on the multi-core CPUs we're commonly running it on today.
So if you care about performance why are you writing that part in python?
> multi-core CPUs we're commonly running it on today.
If you spawn processes to do work you get multi core for free. Think of the whole system, not just your program.
Pro tip: outside Docker, don’t ever use the OS’s own Python if you can avoid it.
This includes Homebrew's Python installation, which will update by itself and break things.
(Technically, I use uv now, but to the same ends.)
Why not?
You almost always want to develop in a virtualenv so you can install the exact versions of things you need without conflicting with the ones the OS itself requires. If you're abstracting out the site-packages directory anyway, why not take one more step and abstract out Python, too? Things like pyenv and uv make that trivially easy.
For instance, this creates a new project using Python 3.13.
$ uv init -p python3.13 foo
$ cd foo
$ uv sync
$ .venv/bin/python --version
Python 3.13.0rc2
I did not have Python 3.13 installed before I ran those commands. Now I do. It's so trivially easy to have per-project versions that this is my default way of using Python.You can get 95% of the same functionality by installing pyenv and using it to install the various versions you might want. It's also an excellent tool. Python's own built-in venv module (https://docs.python.org/3/library/venv.html) makes it easy to create virtualenvs anytime you want to use them. I like using uv to combine that and more into one single tool, but that's just my preference. There are many tools that support this workflow and I highly recommend you find one you like and use it. (But not pipenv. Don't pick that one.)
You want precompiled Python binaries. Use “uv” for this, rather than hacking it together with pyenv.
If you already have pyenv, use it. If you don't have pyenv or uv, install uv and use that. Either one is a huge upgrade over using the default Python from your OS.
One other advantage is that you know the provenance of the python executable when you build it yourself. Uv downloads a prebuilt exe from https://gregoryszorc.com/docs/python-build-standalone/main/ who is a very reputable, trusted source, but it's not the official version from python.org. If you have very strict security requirements, that may be something to consider. If you use an OS other than Linux/Mac/Windows on x86/ARM, you'll have to build your own version. If you want to use readline instead of libedit, you'll have to build your own version.
I am personally fine with those limitations. All of the OSes I regularly use are covered. I'm satisfied with the indygreg source security. The libedit version works fine for me. I like that I can have a new Python version a couple seconds after asking for uv. There are still plenty of reasons why you might want to use pyenv to build it yourself.
1. The whole system is dedicated to running my one program, 2. I want to use multi threading to share large amounts of state between workers because that's appropriate to my specific use case, and 3. A 2-8x speedup without having to re-write parts of the code in another language would be fan-freaking-tastic.
In other worse, I know what I'm doing, I've been doing this since the 90s, and I can imagine this improvement unlocking a whole lot of use cases that've been previously unviable.
Having a “python only” ecosystem makes about as much sense as a “bash only” ecosystem. Your tech stack includes much more.
> In other worse, I know what I'm doing, I've been doing this since the 90s
ditto. So that’s not relevant.
> ditto. So that’s not relevant.
Then I'm genuinely surprised you've never once stumbled across one of the many, many use cases where multithreaded CPU-intensive code would be a nice, obvious solution to a problem. You seem to think these are hypothetical and my experience has been that these are very real.
This issue is discussed extensively in “the art of Unix programming” if we want to play the authority and experience game.
> multithreaded CPU-intensive code would be a nice, obvious solution to a problem
Processes are well supported in python. But if you’re maxing your CPU core with the right algorithm then python was probably the wrong tool.
> my experience has been that these are very real.
When you’re used to working one way it may seem impossible to frame the problem differently. Just to remind you this is a NEW feature in python. JavaScript, perl, and bash, also do not support multi threading for similar reasons.
One school of design says if you can think of a use case, add that feature. Another tries to maintain invariants of a system.
Yes, make a venv for each work project.
If you're doing work on a large Python app for production software, then using system Python isn't going to cut it.
Keeping the number of dependencies reasonable is probably the most important factor, rather than lines of code.