News for hackers. Stuff that matters.
Re:A viable solution for Python concurrency by ferdowsi on Friday October 15, 2021 @02:41PM (#28881408)
If this effort succeeds (and I hope it does) now Python developers will need to contend with the event-loop albatross of asyncio and all of its weird complexity.

In an alternate Python timeline, asyncio was not introduced into the Python standard library, and instead we got a natively supported, robust, easy-to-use concurrency paradigm built around green/virtual threading that accommodates both IO and CPU bound work.

Re:A viable solution for Python concurrency by btown on Friday October 15, 2021 @04:57PM (#28883059)
> instead we got a natively supported, robust, easy-to-use concurrency paradigm built around green/virtual threading that accommodates both IO and CPU bound work

Minus the "natively supported" part, we have this today in http://www.gevent.org/ ! It's so, so empowering to be able to access the entire historical body of work of synchronous-I/O Python libraries, and with a single monkey patch cause every I/O operation, no matter how deep in the stack, to yield to your greenlet pool without code changes.

We fire up one process per core (gevent doesn't have good support for multiprocessing, but if you're relying on that, you're stuck on one machine anyways), spend perhaps 1 person-day a quarter dealing with its quirks, and in turn we never need to worry about the latencies of external services; our web servers and batch workers have throughput limited only by CPU and RAM, for which there's relatively little (though nonzero) overhead.

IMO Python should have leaned into official adoption of gevent. It may not beat asyncio in raw performance numbers because asyncio can rely on custom-built bytecode instructions, whereas gevent has "userspace" code that must execute upon every yield. And, as with asyncio, you have to be careful about CPU-intensive code that may prevent you from yielding. But it's perfect for most horizontal-scaling soft-realtime web-style use cases.

Re:A viable solution for Python concurrency by smetj on Saturday October 16, 2021 @03:42AM (#28886232)
Gevent is the best thing which ever happened within the async world of Python. It's just great to work with. Rock solid. I have high IO production software running for years with gevent as the workhorse, copes with high load, no maintenance, brilliant.

Then came Asyncio, which I personally disliked for the simple reason it became so popular that everybody thinks it's necessary to write an asyncio version of their library/module. The result is it now tears everything in 2. Asyncio code and non-asyncio code ...

Everything like Asyncio, Curio, Trio are impressive and interesting but their interfaces are way too involved for the intended result and it should never be like that. (Gevent suffered less from that).

The only way forward is proper thread support without the GIL and therefor I applaud this effort.

Re:A viable solution for Python concurrency by ayush--s on Saturday October 16, 2021 @04:14AM (#28886336)
It's really easy to shoot yourself in the foot by using global mutable variables. How do you guard against those when using gevent in production?
Re:A viable solution for Python concurrency by btown on Saturday October 16, 2021 @12:58PM (#28889274)
It's actually quite doable in gevent, because you have a guarantee that only one thread will ever be touching those variables ever. You can have 5 or 50 lines of code and be guaranteed that they will operate atomically, read their writes, be immune to interruption, all that good stuff... as long as they don't do any I/O. Of course, the difference from a platform like Node.js or asyncio (where every async/await yield must be explicit all the way down) is that one of your libraries calling `logger.info(...)` might cause I/O, and then cause an implicit yield to the event loop, and break your atomicity without you knowing about it. But if you don't log, and you just work in memory, with code you own or have audited to not do I/O, the sky's the limit. And you almost always want this kind of well-tested, non-logging, high-performance abstraction layer around global mutable state access anyways.
Re:A viable solution for Python concurrency by sillysaurusx on Saturday October 16, 2021 @04:28AM (#28886385)
Global state isn't a problem for multithreaded code. State updates are the problem; it doesn't matter whether it's global or local.

If you can solve the local case, you can solve the global case. Pick your technique; any ol' technique is just as good as any other.

Re:A viable solution for Python concurrency by jgehrcke on Saturday October 16, 2021 @09:15AM (#28887660)
This!

> gevent doesn't have good support for multiprocessing

This is where my little library https://github.com/jgehrcke/gipc might help! It's been working well for people since 2012 :)

By the way, gevent is huge for the Python ecosystem and I would love to acknowledge both Denis Bilenko and Jason Madden who have done a great job over the years. I am in particular impressed by Jason's maintenance work -- creating a new project is always exciting. Maintaining it over the years is sometimes just tedious, hard work. Jason has impressed me a lot with his one man show there.

Not sure if everybody knows, but gevent was for starters built on top of "only" libev -- another super impressive project built and maintained by practically a single person: Marc Lehmann. I learned a lot from his work, too. So so so many serious software projects build on top of libev.

Only 'recently' gevent included support for libuv, the event loop that originated from the NodeJS ecosystem.

I find it super interesting to see that CPython+gevent+libuv is highly similar to running NodeJS!

By the way, the LWN article is once again a wonderfully written piece by Jonathan Corbet. His way to doing technical writing is simply great, I have enjoyed it very much over the years and learned a lot.

Re:A viable solution for Python concurrency by Buttons840 on Friday October 15, 2021 @07:39PM (#28884350)
> our web servers and batch workers have throughput limited only by CPU and RAM

Are you able to fully utilize a multicore processor? I'm not familiar with gevent.

Re:A viable solution for Python concurrency by js2 on Friday October 15, 2021 @09:03PM (#28884753)
This is why you run one process per core, and you'll typically have something like nginx+uWSGI distribute requests across them. I use this combination with https://falconframework.org/ and boto3 to spool HTTP POST requests to S3 and SQS and am pretty happy with it.

uWSGI supports gevent https://uwsgi-docs.readthedocs.io/en/latest/Gevent.html

Falcon also benefits from Cython acceleration. It's been a while but at the time I tested against PyPy and either it was slower or had quirks I was unable to resolve (don't recall w/o consulting my notes).

Re:A viable solution for Python concurrency by btown on Friday October 15, 2021 @11:29PM (#28885325)
Yep, we use Gunicorn in full gevent mode, tuned to spawn and route to a gevent-patched Django process per core, each of which can handle as many concurrent requests as will fit in (its slice of) RAM.

A far cry from the one-request-per-thread days of yore!

Re:A viable solution for Python concurrency by sandGorgon on Saturday October 16, 2021 @01:34AM (#28885782)
you use gunicorn as a reverse proxy - it seems that you are not running Gunicorn to spawn django processes is it ?

I'm wondering how would this work in a kubernetes/docker world.

Re:A viable solution for Python concurrency by btown on Saturday October 16, 2021 @12:26PM (#28888954)
Gunicorn both acts as a reverse proxy and a master process that forks workers - each of which, in our case, loads Django.

https://docs.gunicorn.org/en/stable/design.html

Re:A viable solution for Python concurrency by unityByFreedom on Friday October 15, 2021 @07:44PM (#28884378)
Gevent is indeed amazing. I just started using it. Isn't the point of this post's effort to bake in its functionality? Handling exceptions with gevent doesn't seem trivial to me, for example, and it'd be nice to not have to monkey patch, as easy as it is.
Re:A viable solution for Python concurrency by btown on Saturday October 16, 2021 @12:03AM (#28885451)
Two different parts of the problem! Gevent is all about saying "I/O shouldn't keep my current thread from doing useful work." And if you're I/O bound, that's great! But if you have a lot of trivially parallelizable CPU work to do, it won't help much there, because gevent does nothing about the GIL (global interpreter lock).

The OP is trying to solve the GIL problem, which is saying: "activity on other threads, which might touch objects in memory that my thread cares about, shouldn't keep my current thread from doing useful work." Right now, Python needs to be super conservative to keep threads from stomping on each others' memory, and this should make it a lot more feasible to do that in a sane way. As the comments in the OP post itself suggest, though, this isn't necessarily a sufficient solution. If you're looking for the real solution to "let other threads borrow my memory without global locks," the Rust language and its borrow checker is really the holy grail there.

Regarding gevent exceptions, I find them actually quite natural to work with. If you're looking at old tutorials that talk about linking callbacks to handle exceptions, in practice I've never needed to do that - that stuff is primarily useful to those writing things like gunicorn itself. Pool.imap[_unordered], greenlet.get(), and more will simply re-raise exceptions in the caller's greenlet if there are exceptions in the farmed-out greenlets. And if you need even more control, you can try-catch within the functions that are running inside your greenlets, and return results that may either be a success or a failure.

Re:A viable solution for Python concurrency by throwaway81523 on Friday October 15, 2021 @03:44PM (#28882211)
Yes, I have a big sense of tragedy about Python 3. Python should run on something like (or maybe the actual) Erlang BEAM with lightweight isolated processes. All my threaded Python code is written using that style anyway (threads communicating through synchronized queues) and I've almost never needed traditional shared mutable objects. Maybe completely never, but I'm not sure about a certain program any more.

Added: I don't understand the downvotes. If Python 3 was going to make an incompatible departure from Python 2, they might as well have done stuff like the above, that brought real benefits. Instead they had 10+ years of pain over relatively minor changes that arguably weren't all improvements.

Re:A viable solution for Python concurrency by slaymaker1907 on Friday October 15, 2021 @11:25PM (#28885300)
Yeah, I'd agree it's kind of stupid to use OS threads if you are going to have a GIL. It does make the implementation simpler, but it comes at tremendous cost to IO bound programs. If you are actually trying to do computationally intensive work, you should really be using multiple processes instead of threads in a language with GC. When writing a UI, moving work to a separate thread can still lag because GC will also block the UI thread. Even if you don't care about latency, having separate memory pools via separate processes often helps GC because then GC is embarrassingly parallel regardless of GC implementation.
Re:A viable solution for Python concurrency by throwaway81523 on Saturday October 16, 2021 @02:49AM (#28886042)
The GIL is mostly a problem for cpu bound programs. GC pauses in Python really aren't much of an issue, partly because Python uses refcounting (ironically the cause of the GIL) so it usually frees stuff as it goes along, keeping pauses pretty short. Even with a real GC though, I think it's usually not much of a problem. I've written Python gui's in multi-threaded (but not too compute intensive) apps on quite slow processors by today's standards, and there wasn't much of a problem in UI responsiveness.

I think it's important to remember the difference between parallelism (trying to compute faster by using multiple cpu's simultaneously) and concurrency (communicating on multiple communication channels that are sending stuff non-deterministically). The GIL gets in the way of parallelism but except in extreme cases, it doesn't interfere with concurrency.

Re:A viable solution for Python concurrency by BeetleB on Friday October 15, 2021 @06:43PM (#28883991)
You are likely being downvoted because most claims about the pain of a Python 3 transition are inflated/hyperbole.

It took less than a day to migrate all my code to Python 3. And by "less than a day" I mean "less than 2 hours". Granted, bigger projects would take longer, but saying stuff like "10+ years of pain" is ridiculous. Probably less than 1% of projects had serious issues with the migration. We just hear of a few popular ones that had some pain and assume that was representative.

Re:A viable solution for Python concurrency by throwaway81523 on Friday October 15, 2021 @07:08PM (#28884177)
The entire Python community was in pain over Python 3 for 10 years, even if migrating any particular program wasn't much trouble. If you want to contest the notion that there was pain, then fine: most of the community simply ignored Python 3 for 10 years, because there was no reason until quite late in the process to worry about it.

I myself never bothered migrating any of my Python 2 stuff. It might not be difficult to do so, but continuing to run it under Python 2 still works fine. If you migrated all of yours in 2 hours, you must not have had much to start with.

I do use Python 3 for new stuff most of the time by now, but I keep running into little snags, like the .decode() method not working on strings, or having some (but not all) of the codecs removed from the string module so you have to use the codecs module.

There's also the matter of stuff that is supposedly ported but isn't completely. For example, Beautiful Soup works nicely under py3, but it doesn't have a typeshed entry so its import needs a special annotation to stop mypy from complaining about it.

The real loss with Python 3 is that it could have been so much better than it is. I remember hearing that Go expected to pick up a lot of migrating C++ users, but it got migrating Python users instead.

Here's a pain story about a 2 to 3 migration though:

https://dropbox.tech/application/how-we-rolled-out-one-of-th...

Re:A viable solution for Python concurrency by zzzeek on Friday October 15, 2021 @08:51PM (#28884713)
And yet python tops the Tiobe index anyway. The python 3 transition was not that big a deal, and I migrated or assisted in the migration of many codebases some of which are as big as they get (openstack). the ten year thing especially made it really gradual, and we're all done now. Python 3 is great. Folks migrating to rust / go etc are looking at performance concerns for high volume server software, which python is simply never going to specialize in, and for which rust and go were both introduced only very recently. They didn't switch because python 3 was so disappointing.
Re:A viable solution for Python concurrency by jgehrcke on Saturday October 16, 2021 @09:20AM (#28887690)
By the way Mike I think https://techspot.zzzeek.org/2015/02/15/asynchronous-python-a... is still one of the best articles on the topic of "python and concurrency". So many important insights for real-world engineering.

Thanks for all your work in the Python ecosystem!

Re:A viable solution for Python concurrency by tialaramex on Friday October 15, 2021 @11:18PM (#28885268)
However if you're confident Python simply is "never going to specialize in" performance, maybe the GIL isn't a problem and this work is irrelevant right?

I'm dubious about this work because I think it tries to paper over the cracks in code with data races, but maybe the problem was there already with the GIL and this changes little.

Re:A viable solution for Python concurrency by zzzeek on Saturday October 16, 2021 @10:55AM (#28888270)
I agree, the GIL is really not a huge problem unless you are trying to write a high frequency trading app in Python, which you shouldn't (and not just for technical reasons ;) )
Re:A viable solution for Python concurrency by rtpg on Friday October 15, 2021 @11:01PM (#28885196)
`.decode` doesn't work on strings because you decode bytes! PYthon has UTF strings!

I agree about the annoyance of the codec modules, and stuff like the urllib reorg happening on 3.0 instead of shifting it to later releases (and self-owns like the u prefix). But if you're calling `decode` on a string you have a bug in hiding, from the moment that your "code that handles encodings transparently" blows up when someone gives you a file that isn't UTF-encoded.

signed: someone who did the 2 -> 3 transition for a project that deals with a lot of non-UTF-stuff.

Re:A viable solution for Python concurrency by garmaine on Friday October 15, 2021 @11:23PM (#28885289)
So? `.decode(“hex”)` still makes sense on a string, and was >95% of the encode/decode calls I had.
Re:A viable solution for Python concurrency by rtpg on Saturday October 16, 2021 @01:43AM (#28885819)
Hex-encoding works off of bytes… like if a character has a different byte representation between two encodings you need to specify in both directions

Even the Python 2 strategy for hex might lead to your text getting garbled if you’re using anything not représentable in ASCII (roughly).

Re:A viable solution for Python concurrency by garmaine on Saturday October 16, 2021 @05:35AM (#28886633)
That’s not what decode(“hex”) does though. It takes strings in whatever encoding and turns 0-9,a-f into bytes. In Python 3 it would presumably return a bytes object instead, which you could then hypothetically call encode(“hex”) on. But the point is that it would have kept the syntax the same even if the underlying types changed.

Python3 unnecessarily broke compatibility in many places. Yes I agree that using bytes.fromhex() and .hex() are better design choices. But requiring manual inspection and updating of all strong manipulation code for the transition was an outrageous requirement, particularly for a dynamic language that doesn’t (or at least didn’t at that time) have compile-time type checking to catch such errors.

Re:A viable solution for Python concurrency by __turbobrew__ on Friday October 15, 2021 @08:05PM (#28884486)
Some people don’t get that migrating large legacy code bases from 2 to 3 is not trivial. Your dropbox example is a good one: the migration took 3 years to complete. What else could dropbox have achieved with a 3 year long project if they didn’t have to migrate from 2 to 3?
Re:A viable solution for Python concurrency by throwaway81523 on Friday October 15, 2021 @08:14PM (#28884532)
And Guido van Rossum himself was working at dropbox at the time, contributing to that port. Imagine how much harder it might have been without him.
Re:A viable solution for Python concurrency by BeetleB on Saturday October 16, 2021 @12:21AM (#28885524)
> The entire Python community was in pain over Python 3 for 10 years

Sorry, but that's fiction. Yes, lots of projects (still the minority) simply didn't upgrade, but it's not because they tried and failed. It's because they never prioritized it.

> Here's a pain story about a 2 to 3 migration though:

Consistent with my claim:

> Probably less than 1% of projects had serious issues with the migration. We just hear of a few popular ones that had some pain and assume that was representative.

Re:A viable solution for Python concurrency by snovv_crash on Saturday October 16, 2021 @04:14AM (#28886333)
Lots of things didn't upgrade because their dependencies didn't. And the dependencies had no reason to, because 3.0 didn't offer anything they didn't have before.
Re:A viable solution for Python concurrency by BeetleB on Saturday October 16, 2021 @01:07PM (#28889338)
Quite a bit before the deadline, pretty much all dependencies had upgraded. Those that hadn't were more or less abandoned projects.

Sure, you could hunt and find some that were still maintained/developed with Python 2 in, say, 2019. But these again will be the tiny minority.

I still stand by my 1% claim. The problems with upgrading have been vastly exaggerated.

Re:A viable solution for Python concurrency by JanisL on Sunday October 17, 2021 @10:24AM (#28896675)
The pain in the Python 2->3 conversion I found came more from in house packages being written in Python 2 rather than public packages.
Re:A viable solution for Python concurrency by dagenix on Friday October 15, 2021 @07:55PM (#28884423)
IMO, it's a good thing that decode() doesn't on strings in Python 3.
Re:A viable solution for Python concurrency by throwaway81523 on Friday October 15, 2021 @08:10PM (#28884516)
There is a case to be made either way. The point is that it's yet another quirky difference that pops up sometimes, and using python3 keeps bringing up a stream of such issues.

It does seem in the spirit of Python's duck typing to be able to say '616263'.decode('hex') and get 'abc', and that does work fine in py2. Try it in py3 and you get a type error. So ok, convert the string to bytes, e.g. by saying b'616263' instead of '616263'. That doesn't work either, but I'll leave the experiment to you.

Re:A viable solution for Python concurrency by adwn on Saturday October 16, 2021 @02:21AM (#28885941)
So, like this?

    codecs.decode(b'616263', 'hex')
If that's correct, what's so hard/bad about this?
Re:A viable solution for Python concurrency by qstwall on Saturday October 16, 2021 @03:43AM (#28886235)
It took several years to migrate Twisted or NumPy. The 10 years is the cumulative pain of having 30% of packages not migrated yet. It took at least 7 years to have 90% migrated.
Re:A viable solution for Python concurrency by beebmam on Friday October 15, 2021 @09:06PM (#28884766)
I think some people are upset about the irony/sarcasm here, and many people don't appreciate ironic/sarcastic posts (but I do!)
Re:A viable solution for Python concurrency by throwaway81523 on Saturday October 16, 2021 @02:50AM (#28886047)
I was being quite literal, not sarcastic. I really do wish that Python had switched to a BEAM like architecture at the Python 3 transition. There is no way that can possibly happen now.
Re:A viable solution for Python concurrency by BiteCode_dev on Friday October 15, 2021 @03:00PM (#28881666)
asyncio is not a competition to threads, it's complementary.

In fact, it's a perfectly viable strat in python to have several processes, each having several threads, each having an event loop.

And it will still be so, once this comes out. You will certainly use threads more, and processes less, but replacing 1000000 coroutines by 1000000 system threads is not necessarily the right strategy for your task. See nginx vs apache.

Re:A viable solution for Python concurrency by Zarathust on Friday October 15, 2021 @03:50PM (#28882293)
"Viable" as in "you have no other choice sometimes". This forces you to deal with 3 libraries each with their own quirks, pitfalls and incompatibilities. Sometimes you even deal with dependencies reimplementing some parts in a 4th or 5th library to deal with shortcomings.

I really don't care that much which of them survive, I just want to rely on less of them

Re:A viable solution for Python concurrency by BiteCode_dev on Friday October 15, 2021 @04:27PM (#28882774)
No, it's just useful. They are techs with different trade off, and life is full of opportunities.
Re:A viable solution for Python concurrency by throwaway81523 on Friday October 15, 2021 @06:23PM (#28883826)
Python Zen = one obvious way to do it. Having a bunch of very different ones, each with serious disadvantages, is a bad look.
Re:A viable solution for Python concurrency by heavyset_go on Friday October 15, 2021 @06:29PM (#28883886)
Zen of Python is an ideal, and at this point, kind of tongue-in-cheek.

This is the same language that shipped with at least 3 different methods to apply functions across iterables when the Zen of Python was adopted as a PEP in 2004.

Re:A viable solution for Python concurrency by throwaway81523 on Friday October 15, 2021 @06:57PM (#28884097)
There is at least some recognition in those cases that they introduced the new thing because they got it wrong in the old thing. That's different than saying they should co-exist on equal terms.
Re:A viable solution for Python concurrency by heavyset_go on Friday October 15, 2021 @07:07PM (#28884166)
> That's different than saying they should co-exist on equal terms.

I'm not sure who is claiming that. Here's the OP we're replying to:

> They are techs with different trade off, and life is full of opportunities.

Re:A viable solution for Python concurrency by throwaway81523 on Friday October 15, 2021 @07:38PM (#28884344)
Yes, that says each has good and bad points and you should weigh them against each other in the context of your application, to figure out which one to use. I.e. equal terms.

Zen would be: pick one of the two approaches, keep its strengths while fixing it to get rid of its weaknesses, then declare the fixed version as the one obvious way to do it. You might still have to keep the other one around for legacy support, but that's similar to the situation with applying functions across iterables.

This is what Go did. Go has one way to do concurrency (goroutines) and they are superior to both of Python's current approaches. Erlang has of course been in the background all along, doing something similar.

Re:A viable solution for Python concurrency by BiteCode_dev on Friday October 15, 2021 @06:34PM (#28883928)
It's a technical thread, not a political one. If you were so sure of your argument, you wouldn't use a throwaway.

Besides, it's weird, like saying we should not have int, float and complex, there should be one way to do it.

Just because those are 3 numbers doesn't mean they don't have each their own specific benefit.

Re:A viable solution for Python concurrency by throwaway81523 on Friday October 15, 2021 @06:55PM (#28884078)
int, float, and complex are for different purposes. async and threads paper over each others' weaknesses, instead of fixing the weaknesses at the start. Async itself is an antipattern (technical opinion, so there) but Python uses it because of the hazards and high costs of threads. Chuck Moore figured out 50 years ago to keep the async stuff out of the programmer's way, when he put multitasking into Polyforth, which ran on tiny machines. Python (and Node) still make the programmer deal with it.

If you look at Haskell, Erlang/Elixir, and Go, they all let you write performant sequential code by pushing the async into the runtime where the programmer doesn't have to see it. Python had an opportunity to do the same, but stayed with async and coroutines. What a pain.

Re:A viable solution for Python concurrency by BiteCode_dev on Saturday October 16, 2021 @04:53AM (#28886496)
Oh, you meant like, why don't Python didn't reimplement the whole interpreter around concurrency instead of using the tools it already had to find solutions to problems?

Well, that question is as old as engineering itself, and it's always a matter of resources, cost, history and knowledge.

Re:A viable solution for Python concurrency by dralley on Friday October 15, 2021 @03:42PM (#28882194)
Multiple threads with one asyncio loop per thread would be absolutely pointless in Python, because of the GIL.

With that said, sure, threads and asyncio are complimentary in the sense that you can run tasks on threadpool executors and treat them as if they were coroutines on an event loop. But that serves no purpose unless you're trying to do blocking IO without blocking your whole process.

Re:A viable solution for Python concurrency by BiteCode_dev on Friday October 15, 2021 @04:26PM (#28882760)
It would not be pointless at all, because while one thread may lock on CPU, context switching will let another one deal with IO. This can let you smooth out the progress of each part of your program, and can be useful for workload when you don't want anything to block for too long.
Re:A viable solution for Python concurrency by azinman2 on Friday October 15, 2021 @06:59PM (#28884114)
This entire article is about removing the GIL
Re:A viable solution for Python concurrency by heavyset_go on Friday October 15, 2021 @04:07PM (#28882526)
I read it as each process having multiple threads and an event loop. If the threads are performing I/O or calling out to compiled code and releasing the GIL, said GIL won't block the event loop.
Re:A viable solution for Python concurrency by bonzini on Friday October 15, 2021 @04:02PM (#28882460)
In Python it would be pointless, but for example it's how Seastar/ScyllaDB work: each thread is bound to a CPU on the host and has its own reactor (event loop) with coroutines on it. QEMU has a similar design.
Re:A viable solution for Python concurrency by yellowapple on Friday October 15, 2021 @06:59PM (#28884112)
It's also (to my knowledge) how Erlang's VMs (e.g. BEAM) work: one thread per CPU core, and a VM on each thread preemptively switching between processes.
Re:A viable solution for Python concurrency by harpiaharpyja on Friday October 15, 2021 @04:27PM (#28882773)
If you are ever considering making use of asyncio for your project, I would strongly recommend taking a look at curio [1] as an alternative. It's like asyncio but far, far easier to use.

[1] https://curio.readthedocs.io/en/latest/index.html

Re:A viable solution for Python concurrency by quietbritishjim on Friday October 15, 2021 @05:05PM (#28883136)
Curio's spiritual successor is Trio [1], which was written by one of the main Curio contributors and is more actively maintained (and, at this point, much more widely used). Like Curio, it's much easier to use than asyncio, although ideas from it are gradually being incorporated back into asyncio e.g. asyncio.run() was inspired by curio.run()/trio.run().

I have used Trio in real projects and I thoroughly recommend it.

This blog post [2] by the creator of Trio explains some of the benefits of those libraries in a very readable way.

[1] https://trio.readthedocs.io/en/stable/

[2] https://vorpus.org/blog/some-thoughts-on-asynchronous-api-de...

Re:A viable solution for Python concurrency by nerdponx on Friday October 15, 2021 @07:47PM (#28884389)
As yet another option, I really like Anyio, which is a "frontend" to either Asyncio or Trio, which provides a consistent and tidy API for what is known as "structured concurrency" (I think the name was popularized by the C library Dill).

https://anyio.readthedocs.io

Re:A viable solution for Python concurrency by miohtama on Friday October 15, 2021 @07:37PM (#28884339)
This is super interesting. How does async approaches work across different Python libraries? Are there any assumptions about usage of asyncio?
Re:A viable solution for Python concurrency by nerdponx on Friday October 15, 2021 @07:50PM (#28884402)
The "async/await" syntax is agnostic of the underlying async library, but Asyncio and Trio provide incompatible async "primitives". So yes, you need to write your code for one or the other, or use Anyio, which is a common layer over both.

Many more libraries use Asyncio than Trio, so I have come to recommend Anyio (which has a Trio-like API) but with Asyncio as the backend. That gives you the extensive Asyncio ecosystem but with the structured concurrency design of Trio.

Re:A viable solution for Python concurrency by acidbaseextract on Friday October 15, 2021 @04:47PM (#28882968)
The video (or blog post) below is one of the best explanations I've seen about what subtle bugs are easy to make with asyncio, why it's easy to make them, and how the trio library addresses them.

But yes, consider alternatives before you pick asyncio as your approach!

Talk: https://www.youtube.com/watch?v=oLkfnc_UMcE

Blog post: https://vorpus.org/blog/notes-on-structured-concurrency-or-g...

Re:A viable solution for Python concurrency by BiteCode_dev on Friday October 15, 2021 @05:05PM (#28883130)
While the design of Curio is quite interesting, it's may not be a good choice, not for technical reasons, but for logistical reasons: the chances it gets a wide adoption are slim to None.

And since we are stuck with colored functions in python, the choice of stack matters very much.

Now, if you want easier concurrency, and a solution to a lot of concurrency problems that curio solves, while still being compatible with asyncio, use anyio:

https://anyio.readthedocs.io/en/stable/

It's a layer that works on top of asyncio, so it's compatible with all of it. But it features the nursery concept from Trio, which makes async programming so much simpler and safer.

Re:A viable solution for Python concurrency by heavyset_go on Friday October 15, 2021 @05:14PM (#28883199)
anyio is also compatible with asyncio and Trio, so you can use it with either library or paradigm.
Re:A viable solution for Python concurrency by sandGorgon on Friday October 15, 2021 @05:28PM (#28883318)
Uvloop/uvicorn - which is the production grade asgi server only works with asyncio.

Hypercorn works with trio..but you lose a LOT of performance

Re:A viable solution for Python concurrency by VWWHFSfQ on Friday October 15, 2021 @04:54PM (#28883024)
Highly recommend curio
Re:A viable solution for Python concurrency by nine_k on Friday October 15, 2021 @03:23PM (#28881977)
BTW I wonder why async is so painless in ES6 compared to Python. Why the presence of GIL (which JS also has) did not make running async coroutines completely transparent, as it made running generators (which are, well, coroutines already). Why the whole even loop thing is even visible at all.
Re:A viable solution for Python concurrency by laurencerowe on Friday October 15, 2021 @03:46PM (#28882251)
Because JavaScript never had threads so I/O in JavaScript has always been non-blocking and the whole ecosystem surrounding it has grown up under that assumption.

JavaScript doesn't need a GIL because it doesn't really have threads. WebWorkers are more akin to multiprocessing than threads in Python. Objects cannot be shared directly across WebWorkers so transferring data comes with the expense of serializing/deserializing at the boundary.

Re:A viable solution for Python concurrency by catlifeonmars on Friday October 15, 2021 @03:53PM (#28882334)
JS now has shared array buffers.
Re:A viable solution for Python concurrency by laurencerowe on Friday October 15, 2021 @04:04PM (#28882487)
SharedArrayBuffer is just raw memory similar to using mmap from Python multiprocessing. The developer experience is very different to simply sharing objects across threads.
Re:A viable solution for Python concurrency by heavyset_go on Friday October 15, 2021 @04:16PM (#28882644)
> Why the whole even loop thing is even visible at all.

It isn't anymore.

    In [3]: from asyncio import run

    In [4]: async def async_func(): print('Ran async_func()')

    In [5]: run(async_func())
    Ran async_func()
Top-level async/await is also available in the Python REPL and IPython, and there are discussions on the Python mailing list about making top-level async/await the default for Python[1].

    In [1]: async def async_func(): print('Ran async_func()')

    In [2]: await async_func()
    Ran async_func()

[1] https://groups.google.com/g/python-ideas/c/PN1_j7Md4j0/m/0xy...
Re:A viable solution for Python concurrency by BiteCode_dev on Friday October 15, 2021 @05:15PM (#28883207)
Oh, top level await... I missed that.

Not sure it will get there, but it would be nice. I think putting a top level "await" is explicit enough for stating you want an event loop anyway.

Now, with TaskGroup in 3.11, things are going to get pretty nice, espacially if this top level await plays out, provided they include async for and async with in the mix.

Now, if they could just make so that coroutines are automatically schedules to the nearest task group, we would almost have something usable.

Re:A viable solution for Python concurrency by BiteCode_dev on Friday October 15, 2021 @04:15PM (#28882628)
I used them both extensively, and here are the main reasons I can think of:

- The event loop in JS is invisible and implicit. V8 proved it can be done without paying a cost for it, and in fact most real life python projects are using uvloop because it's faster than asyncio default loop. JS dev don't think of the loop at all, because it's always been there. They don't have to chose a loop, or thinking about its lifecycle or scheduling. The API doesn't show the loop at all.

- Asynchronous functions in JS are scheduled automatically. On python, calling a coroutine function does...nothing. You have to either await it, or pass it to something like asyncio.create_task(). The later is not only verbose, it's not intuitive.

- Async JS functions can be called from sync functions transparently. It just returns a Promise after all, and you can use good old callbacks. Instantiating a Python coroutine does... nothing as we said. You need to schedule it AND await it. If you don't, it may or may not be executed. Which is why asyncio.gather() and co are to be used in python. Most people don't know that, and even if you know, it's verbose, and you can forget. All that, again, because using the event loop must be explicit. That's one thing TaskGroup from trio will help with in the next Python versions...

- the early asyncio API sucked. The new one is ok, asyncio.run() and create_task() with implicit loop is a huge improvement. But you better use 3.7 at least. And you have to think about all the options for awaiting: https://stackoverflow.com/questions/42231161/asyncio-gather-...

- asyncio tutorials and docs are not great, people have no idea how to use it. Since it's more complex, it compounds.

E.G, if you use await:

With node v14.8+:

    await async_func(params)
With python 3.7+:

    import asyncio 

    async def main():
        # no top level await, it must happen in a loop
        await async_func(params) 

    asyncio.run(main) # explicit loop, but easy one thanks to 3.7
E.G, deep inside functions calls, but no await:

With node:

    ...
    async_func(params)

With python 3.7+:

    ...
    # async_func(params) alone would do nothing
    res = asyncio.create_task(async_func(params))

    ...

    # you MAY get away with not using gather() or wait()
    # but you also may get "coroutine is never awaited"
    # RuntimeWarning: coroutine 'async_func' was never awaited
    asyncio.gather(res)
Of course, you could use "run_until_complete()", but then you would be blocking. Which is just not possible in JS, there is one way to do it, and it's always non blocking and easy. Ironic, isn't it? Beside, which Python dev knows all this? I'm guessing most readers of this post will have heard of it for the first time.

Python is my favorite language, and I can live with the explicit loop, but explicit scheduling is ridiculous. Just run the damn coroutine, I'm not instantiating it for the beauty of it. If I want a lazy construct, I can always make a factory.

Now, thanks to the trio nursery concept, we will get TaskGroup in the next release (also you can already use them with anyio):

    async with asyncio.TaskGroup() as tg:
        tg.start_soon(async_func, params)
Which, while still verbose, is way better:

- no gather or wait. Schedule it, it will run or be cleaned up.

- no need to chose an awaiting strat, or learn about a 1000 things. This works for every cases. Wanna use it in a sync call ? Pass the tg reference in it.

- lifecycle is cleanly scoped, a real problem with a lot of async code (including in JS, where it doesn't have a clean solution)

Re:A viable solution for Python concurrency by masklinn on Saturday October 16, 2021 @05:02AM (#28886532)
> Python is my favorite language, and I can live with the explicit loop, but explicit scheduling is ridiculous. Just run the damn coroutine

You can't "just run the damn coroutine", that's not what coroutines are.

But that does point to the mistake of Python, at least from a UX perspective: coroutines are more efficient but they're also a lot more prone to use errors, especially in dynamically typed languages. Async functions should probably just have been tasks, which is what they are in JS.

Re:A viable solution for Python concurrency by BiteCode_dev on Sunday October 17, 2021 @06:16AM (#28895334)
I know, but we could make coroutine creates with async have a special marker that is different to yield and auto schedule.
Re:A viable solution for Python concurrency by masklinn on Tuesday October 19, 2021 @06:25AM (#28916631)
That’s what a task is.
Re:A viable solution for Python concurrency by justusw on Friday October 15, 2021 @10:34PM (#28885098)
I’m not an expert on Node.js but I was always under the impression that you couldn’t write await outside of an async function. Has that changed recently?
Re:A viable solution for Python concurrency by BiteCode_dev on Saturday October 16, 2021 @04:50AM (#28886477)
You can await at the top level of a module since node v14.8.

It has been proposed to Python: https://groups.google.com/g/python-ideas/c/PN1_j7Md4j0/m/0xy...

But I have no more info about it.

Re:A viable solution for Python concurrency by shauns on Saturday October 16, 2021 @03:14AM (#28886154)
You are right that you can only await in an async function, but you can call an async function anywhere and get back a Promise.

await-ing that promise is often what you want to do but there are other ways to do work once the promise has resolved.

Re:A viable solution for Python concurrency by KaiserPro on Friday October 15, 2021 @04:04PM (#28882476)
> easy-to-use concurrency paradigm

Well it has queues and threads already.

Its just that asyncio for socket handling at least (in the testing that I did) is about 5% faster. (one asyncio socket "server" vs ten threads [with a number of ways to monitor for new connections])

I always assumed that people wanted asyncio because they look at javascript and thought "hey I want GOTOs cosplaying as a fun paradigm"

Re:A viable solution for Python concurrency by BiteCode_dev on Friday October 15, 2021 @05:06PM (#28883142)
GOTO cosplaying should go away with structured concurrency (via TaskGroup) being adopted in 3.11, as pioneered by Trio.

Check out anyio if you want to use them now.

Re:A viable solution for Python concurrency by quietbritishjim on Saturday October 16, 2021 @04:33AM (#28886401)
TaskGroup in asyncio has been promised at least as far back as Python 3.8 [1]. They're still not in the draft "What's new in python 3.11" [2] and searching the web didn't return any official statements. I believe they're planned but don't believe they'll arrive any time soon.

If you want to use structured concurrency now, IMO the best bet is to use Trio directly. Reading posts by the author makes it clear that every detail of the library had been extremely scrutinised, not just the API (e.g. see this long post on ctrl-C handling [3], or any number of long technical discussions on the issue tracker), so I think it's a better choice in any case.

[1] https://twitter.com/1st1/status/1041855365745455104

[2] https://docs.python.org/3.11/whatsnew/3.11.html

[3] https://vorpus.org/blog/control-c-handling-in-python-and-tri...

Re:A viable solution for Python concurrency by BiteCode_dev on Saturday October 16, 2021 @04:49AM (#28886474)
Anyio is probably a best bet to use TaskGroup IMO. You can use the trio backend if you want, after all.

But it's asyncio compatible, which makes it more future proof.

Re:A viable solution for Python concurrency by quietbritishjim on Saturday October 16, 2021 @04:10PM (#28891030)
Hmm, we'll just have to disagree on that one.

Trio has the better API, so you might as well use that directly rather some other library that attempts to match it, even if it can/does use it under the hood. It reminds me of that xkcd about standards. It's also going to have to settle for the least common denominator to some extent even if it reimplements some parts itself (e.g. if one library implements serial ports but the other didn't, then it surely can't be in anyio). I also dispute that it's "more future proof", because you're still relying on anyio being maintained, and to me it seems to be the more obscure library.

Re:A viable solution for Python concurrency by BiteCode_dev on Sunday October 17, 2021 @06:15AM (#28895329)
The idea is that once task groups are implemented, you can switch back to asyncio, which as more chance than Trio to be supported.
Re:A viable solution for Python concurrency by birdyrooster on Saturday October 16, 2021 @03:38AM (#28886226)
I don’t see what is so weird about it. The syntax is simple and straightforward. It’s how I wrote a ton of Python and it works great as long as you know what blocks and what doesn’t. Using aio_multiprocess it’s easy to saturate all the cores on a machine using the same basic syntax. It’s lovely really but still Python is too slow compared to golang so I rarely use it.
Re:A viable solution for Python concurrency by andrewstuart on Saturday October 16, 2021 @05:00AM (#28886525)
>>> In an alternate Python timeline, asyncio was not introduced into the Python standard library,

I totally disagree with this negative perspective of asyncio.

async programming is MUCH easier to program and understand than other concurrency solutions like multithreading.

async works extremely well as a programming model as evidenced by the fact that it's exactly the model being adopted by all languages that want to implement practical and developer friendly concurrency.

When I read such a negative take on asyncio I assume only this is a developer who hasn't done alot of programming with it, and is therefore still somewhat lacking in full understanding.

Re:A viable solution for Python concurrency by tbabb on Friday October 15, 2021 @02:53PM (#28881581)
What specifically is the problem with asyncio? I quite like using it, so I'm curious if there's some aspect that makes it unsustainable?
Re:A viable solution for Python concurrency by aeyes on Friday October 15, 2021 @06:10PM (#28883711)
It solves only one problem, the name says it: Async I/O

If you do anything on the CPU or if you have any I/O which is not async you stall the event loop and everything grinds to a halt.

Imagine a program which needs to send heartbeats or data to a server in a short interval to show liveness, Kafka for example. Asyncio alone can't reliably do this, you need to take great care to not stall the event loop. You only have exactly one CPU core to work with, if you do work on the CPU you stall the event loop.

We see web frameworks built on asyncio but even simple API only applications constantly need to serialize data which is CPU-bound. These frameworks make no effort (and asyncio doesn't give us any tools) to protect the event loop from getting stalled by your code. They work great in simple benchmarks and for a few types of applications but you have to know the limits. And I feel that the general public does not know the limitations of asyncio, it wasn't made for building web frameworks on the async event loop. It was made for communicating with external services like databases and calling APIs.

Re:A viable solution for Python concurrency by tbabb on Friday October 15, 2021 @08:16PM (#28884541)
> If you do anything on the CPU or if you have any I/O which is not async you stall the event loop and everything grinds to a halt.

But that's always going to be the case for single threaded code. If you're ready to use threads, then that integrates pretty seamlessly with asyncio by using an executor task.

> asyncio doesn't give us any tools to protect the event loop from getting stalled by your code

Again, check out executors: https://docs.python.org/3/library/asyncio-eventloop.html#asy...

Asyncio in Python and JS are very different beasts.

Re:A viable solution for Python concurrency by Matthias247 on Saturday October 16, 2021 @05:57PM (#28891953)
That's a problem - but its one that is common to all kinds of framework which make use of cooperative multitasking - whether they are in Python, Java (Netty, NIO, etc), C (Nginx, libevent, ...), C# or Rust.

All those frameworks trade off high performance if everything is well behaved against easy of use and potential of very bad performance if some regression is introduced.

Whether that tradeoff ia a good one to take will depend on the application.

Re:A viable solution for Python concurrency by calpaterson on Friday October 15, 2021 @02:59PM (#28881652)
The key disadvantage is largely that it bifurcates the library base. Async libraries and sync libraries co-exist uneasily in the same program.

For nearly every popular library there is now a (usually inferior, less robust) async one. The benefits of Linus' Law are reduced.

Re:A viable solution for Python concurrency by tbabb on Friday October 15, 2021 @05:21PM (#28883257)
Fair point. Async is a big enough idea that it probably warrants designing the language with it in mind. I guess another way of phrasing it would be that it violates the "there's only one way to do it" maxim, and the "two ways of doing it" circumstance necessarily came about because the idea was discovered long after the core language and libraries were already written.
Re:A viable solution for Python concurrency by Redoubts on Friday October 15, 2021 @04:22PM (#28882702)
Trifucates, since now there’s stdlib asyncio, and a popular trio async flavor too.
Re:A viable solution for Python concurrency by throwaway81523 on Friday October 15, 2021 @03:48PM (#28882271)
> What specifically is the problem with asyncio?

Watch the very NSFW (lots of swearing) but hysterically funny video "node.js is bad ass rock star tech" on youtube sometime ;). https://www.youtube.com/watch?v=bzkRVzciAZg

Re:A viable solution for Python concurrency by fullstop on Friday October 15, 2021 @03:03PM (#28881727)
I like using it as well, but I've been bit several times by having runtime exceptions completely swallowed.
Re:A viable solution for Python concurrency by Too on Saturday October 16, 2021 @09:19AM (#28887689)
Let me guess. Using create_task() without await and assuming it will run to completion in the background to trigger some other event?

This is almost always the case of async tasks disappearing into the void. After shooting myself in the foot with this one time to many I have a hard rule to never use create_task. Instead make sure every single task ends up in some kind of gather() or wait() awaited from the top level. This will ensure any exceptions are propagated. If the number of tasks are dynamically created, while others are still ongoing, add them to a common list and restart your wait() call.

Re:A viable solution for Python concurrency by fullstop on Saturday October 16, 2021 @11:33AM (#28888549)
Yes, exactly this scenario.
Re:A viable solution for Python concurrency by Too on Sunday October 17, 2021 @01:08AM (#28894122)
:)

Its not that strange actually. If you compare it to threads, where you shoot off a thread which you never join(). It’s exactly the same scenario.

Any thread which doesn’t have a join needs a watchdog. Or register the unhandled exception handler.

Can’t remember if threads do this by default. Asyncio tasks for sure don’t, and they can’t really do it either, since a task in error state could be saved for later use for legitimate reasons. Closest you get is the warning printed to log about tasks that got GCd without ever being awaited, if you enable ASYNCIO_DEBUG=1

Re:A viable solution for Python concurrency by alfanerd on Sunday October 17, 2021 @02:49AM (#28894511)
Yes, yes, yes and I hope the alternate timeline will start now.

IMO the reason that we have so many ways of doing concurrency in Python (fra asyncio, curio, trio to gevent, multiprocessing etc.) is that it was never properly dealt with.

It has to be built into the language, a feature of the language, like fx. garbage collection. The model of Pony or Erland, where are execution thread is started per core could also have been used in python, instead we got the async/await mess, which almost created a whole new language, where every library has to be rewritten.

It saddens me to think that the ugly cludge that async/await is got added to Python almost without any discussion, whereas the insignificant walrus operator got so much heat that GVR quit.

Re:A viable solution for Python concurrency by dekhn on Friday October 15, 2021 @03:24PM (#28881984)
so true. I've been writing thread-callback code for decades (common in network and gui event loops, see QtPy as an example) and when I looked at asyncio my first thought is "this is not better". It's entirely nontrivial to analyze code using asyncio (or yield) compared to callbacks.
Re:A viable solution for Python concurrency by dapids on Saturday October 16, 2021 @12:38AM (#28885588)
I still use greenthreads every chance I get. IMO The asyncio headaches are just not worth the abstraction hell involved in their event loop design, without being forced to stick to fragile concepts or consistently staying up to date with what is the current best way to do something with asyncio.
Re:A viable solution for Python concurrency by int_19h on Friday October 15, 2021 @06:01PM (#28883626)
How would those green/virtual threads interface with native async APIs (e.g. the entirety of WinRT)?