Posted by willm 9/2/2025
For any new app that is mostly IO constraint I'd still encourage the use of asyncio from the beginning.
It’s clear that Dr. Frankenstein has been at large and managed to get his hands on Python’s corpse.
During development, asyncio was called tulip. A quick search turns up this talk by Guido:
https://www.youtube.com/watch?v=aurOB4qYuFM
I seem to recall that Guido was in touch with the author of Twisted at the time, so design ideas from that project may have helped shape asyncio.
Before asyncio, Python had asyncore, a minimal event loop/callback module. I think it was was introduced in Python 1.5.2, and remained part of the standard library until 3.12.
so of course in their infinite wisdom, they removed it
Not worth the trouble. Shell pipelines are way easier to use. Or simply waiting —no pun intended— for the synchronous to finish.
Use a tuple, maybe walrus, and return the last item[-1].
How do I get variables for not redoing long-running computations that depend on one-another? So, what if the third tuple value depends on the second and the second in turn depends on the first?
future = lambda age: (
print('Your age is:', age),
older := age + 5,
print('Your age in the future:', older),
older,
)[-1]
print(future(20))
# out
Your age is: 20
Your age in the future: 25
25
[x
for x in [some_complicated_expression]
if x > 0
for y in [x + 1]
...
][0]
That said, I wouldn't recommend this because of poor readability.The article says SQLalchemy added async support in 2023 but actually it was 2020.
Async Python is practically a new language. I think for most devs, it's a larger than than 2 to 3 was. One of the things that made python uptake easy was the vast number of libraries and bindings to C libraries. With async you need new versions of that stuff, you can definitely use synchronous libraries but then you get to debug why your stuff blocks.
Async Python is a different debugging experience for most python engineers. I support a small handful of async python services and think it would be an accellerator for our team to rewrite them on Go.
When you hire python engineers, most don't know async that well, if at all.
If you have a mix of synchronous and asynchronous code in your org, you can't easily intermix it. Well you can, but it won't behave as you usually desire it to, it's probably more desirable to treat them as different code bases.
Not to be too controversial, but depending upon your vintage and they was you've learned to write software I think you can come to python and think async is divine manna. I think there are many more devs that come to python from datascience or scripting or maybe as a first language and I think they have a harder time accepting the value and need of async. Like I said above, it's almost an entirely different language.
And Python's async cancellation model is pretty nice! You can reason about interruptions, timeouts, and the like pretty well. It's not all roses: things can ignore/defer cancellations, and the various wrappers people layer on make it hard to tell where, exactly, Tasks get cancelled--awaitable functions are simple here, at least. But even given that, Python's approach is a decent happy medium between Node's dangling coroutines and Rust's no-cleanup-ever disappearing ones (glib descriptor: "it's pre-emptive parallelism, but without the parallelism").
More than a little, I think, of the "nobody does it this way" weirdness and frustration in Python asyncio arises from that. That doesn't excuse the annoyances imposed by the resulting APIs, but it is good to know.
If any code in your coroutine, including library code, has a broad try/except, there's good chances that eventually the cancellation exception will be swallowed up and ignored.
Catch-all try/except of course isn't the pinnacle of good software engineering, but it happens a lot, in particular in server-tyoe applications. You may have some kind of handler loop that handles events periodically, and if one such handling fails, with an unknowabl exception, you want to log it and continue. So then you have to remember to explicitly reraise cancellation errors.
Maybe it's the least bad Pythonic option, but it's quite clunky for sure.
But it took me some time to realize I can do the same idioms in Go as in Scala:
// Scala
f := Future(x)
// Do something else until you need f
...
for r <- f { ... }
can be written as c := channel
// Do something else until you need the result
...
r<-c
My mind model was channel as a queue, but it can easily be used like channel as a future for one value.And `select` for more complicated versions.
I miss the easy composition and delaying of futures though (f.map etc.)
"There should be one-- and preferably only one --obvious way to do it : Aim for a single, clear solution to a problem. "
Snide remark aside, I actually like the Zen of Python as programming language folklore but in 2025 AD it's kinda crazy to pretend that Python actually adheres to those tenets or whatever you wish to call them, and I'd go as far as to claim that it does a disservice to a language flexible enough for a lot of use cases. There's even someone on YouTube developing a VR game with Python.