Asynchronous Programming

Libraries for asynchronous, concurrent and parallel execution. Also see awesome-asyncio.

I/O-bound or CPU-bound? I/O-bound code wants a Python async library, and asyncio comes built in. CPU-bound work goes to a concurrent.futures process pool.

How to choose:

  • I/O-bound code written with async/await: asyncio
  • CPU-bound work, or blocking calls, in a pool of processes or threads: concurrent.futures
  • Trio-style task groups and cancel scopes on asyncio, or a library that runs on both asyncio and Trio: AnyIO
  • A faster, drop-in event loop for an asyncio app on Linux or macOS: uvloop
  • Structured concurrency from the ground up, with its own libraries: Trio
  • Existing synchronous code, made concurrent without async/await: gevent
  • Network servers and clients with protocols built in, like SSH, mail, and DNS: Twisted
  • Processes you manage yourself, talking through queues and pipes: multiprocessing

Listed in editorial order, grouped by use case. Click a column header to re-sort the whole list.

Press / to search. Tags open their category. Click a row for details.

Search and filter

Results

Row number Tags

Async I/O 6 projects

(Python standard library) Asynchronous I/O, event loop, coroutines and tasks.
Also see: awesome-asyncio
A high-level async concurrency and networking framework that works on top of asyncio or trio.
agronholm/github.com/agronholm/anyio /2,564 stars /
Ultra-fast asyncio event loop.
MagicStack/github.com/MagicStack/uvloop /11,921 stars /
A friendly library for async concurrency and I/O.
python-trio/github.com/python-trio/trio /7,356 stars /
A coroutine-based Python networking library that uses greenlet.
gevent/github.com/gevent/gevent /6,441 stars /
An event-driven networking engine.
twisted/github.com/twisted/twisted /5,994 stars /

Parallelism 2 projects

(Python standard library) High-level asynchronous execution of callables.
(Python standard library) Process-based parallelism.

Asynchronous Programming guide

asyncio is the standard library's way to write concurrent code with async/await, and many async web servers, database drivers, and task queues build on it. Start your program with asyncio.run(), called once as the main entry point. App code should rarely need the event loop object itself. Run related tasks in an asyncio.TaskGroup: when one task fails, it cancels the rest, which gather() doesn't. CPU-heavy code holds up every task on the loop, so run it in another process: hand it to a ProcessPoolExecutor with loop.run_in_executor().

concurrent.futures runs callables on threads or processes behind the same interface. ThreadPoolExecutor is for overlapping I/O. For CPU-bound work on a multi-core machine, the threading docs advise processes instead, and ProcessPoolExecutor runs them for you. It takes only picklable functions and arguments, though. Use either executor in a with block, which shuts it down and waits for its work to finish.

AnyIO brings Trio-like structured concurrency to asyncio. Code written against its API runs unmodified on asyncio or Trio, so a library built on it doesn't choose for its users. Its docs see strong merits in its APIs for applications too, starting with cancel scopes for more predictable cancellation. Start with anyio.run(main), which runs on asyncio unless you pass backend="trio". Spawn tasks in a task group: when one child task raises, the rest are cancelled.

uvloop is a drop-in replacement for asyncio's event loop, built on libuv. Its README prefers uvloop.run(main()), which configures asyncio.run() to use uvloop, so the rest of your asyncio code stays the same. uvloop runs on Linux and macOS.

Trio has an obsessive focus on usability and correctness. Child tasks run in a nursery, opened with async with trio.open_nursery(), and Trio never discards their exceptions. Functions take no timeout arguments: you wrap the code in a cancel scope like trio.move_on_after(). Trio runs its own event loop, so asyncio functions don't work inside trio.run(). Check the Trio library list for what you need first.

gevent uses greenlets to give you a synchronous API on top of an event loop. Its monkey patching swaps the standard library's blocking sockets for cooperative ones, so code that knows nothing about gevent runs concurrently. Most programs should patch everything with monkey.patch_all(), and the main module should do it before any other imports.

Twisted is an event-based framework for internet applications that ships clients and servers for HTTP, SSH, IMAP, POP3, SMTP, DNS, IRC, and XMPP. Write new Twisted code as async def coroutines, which its docs prefer over inlineCallbacks, and start one with Deferred.fromCoroutine().

multiprocessing runs work in subprocesses, so it can use every processor on a machine. Its own docs point to ProcessPoolExecutor as the higher-level interface for pooled tasks. Reach for multiprocessing when you need what it adds, like killing a running process or passing data through queues and pipes. Its guidelines say to avoid shared state and keep the data moving between processes small.

On an event loop, a blocking call holds up every other task, so push it to a worker thread: asyncio has asyncio.to_thread(), AnyIO to_thread.run_sync(), and Trio trio.to_thread.run_sync(). When multiprocessing or ProcessPoolExecutor runs your code in child processes, define its functions in a module. Guard your entry point with if __name__ == '__main__': too, so each new process can import your main module safely.

Know a project that belongs here?

Tell us what it does and why it stands out.