The Move to Python 3 Begins
133 points by rmpr
133 points by rmpr
Wild to see this headline in 2026. The last big Python3 migration I remember is Dropbox in 2019.
Scale-wise, EVE has 2,400 kloc of Python 2.7. That's double Dropbox's desktop client (1000 kloc) and 2-5x more code than the recent Bun rewrite (535 kloc Zig to 1,000 kloc Rust).
Python's dynamic typing makes that harder to wrangle than the static typing in Zig and Rust, especially because Python 2.7 doesn't have any native type annotation support.
Python's modern type annotation system actually got a big boost from the Dropbox migration. Dropbox employed the Mypy core team at that time.
Not that I think it will be a huge help, but you can run mypy against a 2.7 codebase in case anyone reading this has a silent shame they’re inspired to try to upgrade.
and 2-5x more code than the recent Bun rewrite (535 kloc Zig to 1,000 kloc Rust).
This is not a good comparison. The Python 2 to Python 3 migration often just requires small modifications whereas most of the code can remain exactly the same. In addition to that, you can do it piecewise by modifying the code such that it works for both Python 2 and Python 3, which only requires a few shims.
desktop client (1000 kloc)
This seems hilarious, what does it do?
For a Linux user, you can already build such a system yourself quite trivially by getting an FTP account, mounting it locally with curlftpfs, and then using SVN or CVS on the mounted filesystem. From Windows or Mac, this FTP account could be accessed through built-in software.
50 uses of <>, a way of writing "not equal" so old that many working Python developers have never seen it.
Heh, OCaml also uses this.
So does SQL!
Goes back to Pascal and some dialects of ALGOL, and older BASIC dialects. It was a Whole Thing
and older BASIC dialects.
Yup. I was shocked a few years ago -- as in, since COVID -- when some programmer/techie types didn't know what it meant.
I had to switch to != instead.
C, a language that was the subject of jokes 30Y ago for its famous unreadability, is now the only syntax that many millennials know.
In my opinion, readability is so influenced by familiarity that it is very difficult to reason about what's readable.
I am not being funny, but your comment is, er, not very readable at all to me.
... Pardon?
Whoops, I wrote a "readability" when I wanted to write "familiarity" which rendered everything stupid.
Luckily the edit window was not over. I think it's clearer now, but just in case; if you have been dealing with C-style syntax forever, C syntax will be familiar to you and will feel the most readable thing in the world- even if it weren't.
I guess it's a common obstacle to innovation; deviating from common practice hurts even if it's for a good reason.
I read it with:
that is it very
Changed to:
that it is very
And I think it made sense to me then.
Dang, two huge mistakes. Thanks!
Ah, right. Now I see.
I think this is just another expression of the old observation that there is no computers and no software are truly "intuitive".
2016:
https://www.thinkcompany.com/blog/the-myth-of-intuitive-design/
2019:
https://www.lemodesittjr.com/2019/05/17/the-non-intuitive-nature-of-the-intuitive/
I see it in Forths a lot. Forth 200x (a standard you should probably ignore fwiw) states:
<> not-equals CORE EXT ( x1 x2 -- flag )
flag is true if and only if x1 is not bit-for-bit the same as x2.
I've been writing Python for well over 10 years now and never knew it once supported <> as an alternate form of !=.
I came to Python (2.2, IIRC) from Visual Basic, for a long time I didn't realise <> support was unusual.
It is news to me that it is. Ah well.
“Those who cannot remember the past are condemned to repeat it.” – George Santayana, The Life of Reason, 1905.
“Those who don’t understand UNIX are doomed to reinvent it, poorly”. — Henry Spencer, news:sci.space.shuttle,1987
We should be teaching the history of computing. It's old enough now that middle-aged professionals don't know the basics (and, indeed, the BASICs).
I was part of google's migration from 2.7 to 3.6. it was long and tedious, but also a fascinating project. I primarily worked on the type checker (ensuring code type checked under 2.7 was an extraordinarily valuable step on the way to migration) and on automated refactoring tools. the refactoring tools were very specific to the task at hand and were discarded afterwards, but the typechecker continued to be valuable long after the migration was done. I think it is to date the only typechecker that attempted whole-program type inference and analysis of unannotated python code.
But stockless python does not exist anymore, so what'll they do there?
The devblog could use a little more explanatory background, I suppose. As I recall, they've decided to become active contributors to python-greenlet (https://github.com/ccpgames/greenlet, forked from the python-greenlet project) so they can continue to use the greenlet pattern without being pinned to an older interpreter version. As I recall, at the time that Stackless was created the CPython interpreter did not expose its internals in such a way that Greenlets could be added as an extension, hence Stackless being a different interpreter. CPython3 resolves these issues so that Greenlets can be added as a native extension.
https://youtu.be/-x299qHLQs0 is a 45 minute talk explaining how they did this for their EVE Frontier game, with the intention to do the same thing for EVE Online.
Short version: they built https://github.com/carbonengine/scheduler for this.
From the wording I presume it will use standard CPython. They did not mention here any particular minimal target version other than "3", but mentioned speed ups to the language would mean minimum 3.11 (also 3.14 was another bump), because previous versions could be sometimes even slower than 2.7. If they don't have too much C modules they could possibly try out PyPy with even earlier versions. Asyncio with coroutines give CPython a Stackless-Python-like functionality, it was added in CPython 3.4.
That's the main issue with python is that nobody really seems to be too happy with asyncio. I used to be a big twisted head for instance pre-3.
I would guess that asyncio will be a dead battery in a few years. The loop-bound primitives like asyncio.Event and asyncio.Queue and others don’t play particularly well with having multiple threads, each with its own loop, which freethreading makes viable.
I sometimes wonder what Python would look like if Stackless Python had been merged into CPython and stackful concurrency had become the usual style, something closer in spirit to what Project Loom's virtual threads do for Java.
I did some Python 2.7 work with gevent and remember finding it a lot nicer than asyncio. No threading async/await through the whole call chain, a function could just yield somewhere inside it and the caller never had to know. But that invisibility cuts both ways: a dependency change three calls down can quietly introduce a yield point, and nothing at the call site tells you a concurrency bug just became possible. await is tedious to write everywhere, but at least it leaves a trace in the source.
Some of this might come down to CPython internals rather than taste. My rough understanding is that decoupling execution from the C stack, which is what Stackless does, sits awkwardly with how much the C API and extensions assume ordinary C call/return. Generator-based coroutines could be added with far less disruption to the interpreter. So the outcome may have had more to do with implementation cost than any real preference for explicit color over implicit switching.
This is going to be a fascinating project, I hope they keep reporting how it is going. Some big questions for me:
If ever there was an argument not to use python!
Why? Because after 2 decades running a succesful business, EVE must now deal with some technical debt?
I interpreted it the other way around: if there was an argument not to use python (due to 2->3, lack of types, interpreted, whatever), EVE shows it doesn't matter and that it was still more than good enough for a giant business with deep technical requirements.
And I say that as someone who will.personally never touch python for anything more than single file script without external dependencies.
never touch python for anything more than single file script without external dependencies.
Sounds to me like you've been burned in the past by Python's shared dependencies and confusing environments.
In case you're not aware, this works now:
# /// script
# requires-python = "==3.14.*"
# dependencies = [
# "beautifulsoup4==4.15.0",
# "httpx2==2.12.0",
# ]
# ///
import sys
import httpx2
from bs4 import BeautifulSoup
url = sys.argv[1]
html = httpx2.get(url).raise_for_status().text
soup = BeautifulSoup(html, "html.parser")
for header in soup.find_all(["h1", "h2", "h3", "h4", "h5", "h6"]):
print(header.name, header.get_text(" ", strip=True))
Then run it with:
uv run headers.py https://simonwillison.net
The uv run command reads those inline script dependency comments, creates an isolated, cached environment, installs those exact versions and runs the script.
I think Python's reputation for unreliable package management deserves to be reconsidered!
Is this in a Python standard or is it UV's own custom parsing convention? Asking because after years of using Poetry I'm more likely to jump to PDM or something else supported by Python itself than another non-standard packaging solution.
This is fine as far as it goes but doesn't solve how to deliver this to a customer or a production environment in a responsible way.
Yeah, Python's dependency management burned me, hard. Your proposed solution looks a lot more pleasant, thanks!
There are still couple of issues with it: