Skip to content

Latest commit

 

History

History
153 lines (110 loc) · 5.21 KB

README.rst

File metadata and controls

153 lines (110 loc) · 5.21 KB
Join chatroom Documentation Status Automated test status Automated test status (Windows) Test coverage

The async_generator library

Many Python programmers already know and love generators, because they're a super convenient way to write your own iterators that you can loop over using for. Python 3.6 added a new thing: async generators. Just like regular generators, they're super convenient; the difference is that now you loop over them using async for, and this means you can use await inside them. If you're not doing async programming, you probably don't care about this at all. But if you are doing async programming, this is super awesome. If you're not convinced, check out this 5-minute demo (warning: contains Nathaniel's first ever attempt to live-code on stage).

But what if you aren't using the latest and greatest Python, but still want that async generator goodness? That's where this package comes in. It gives you async generators and @asynccontextmanager on Python 3.5+.

Example 1: suppose you want to write your own async generator. The goal is that you can use it like:

async for json_obj in load_json_lines(stream_reader):
    ...

If you're using Python 3.6+, you could implement load_json_lines using native syntax:

async def load_json_lines(stream_reader):
    async for line in stream_reader:
        yield json.loads(line)

Or, you could use async_generator, and now your code will work on Python 3.5+:

from async_generator import async_generator, yield_

@async_generator
async def load_json_lines(stream_reader):
    async for line in stream_reader:
        await yield_(json.loads(line))

Example 2: let's say you want to write your own async context manager. The goal is to be able to write:

async with background_server() as ...:
    ...

If you're using Python 3.7+, you don't need this library; you can write something like:

from contextlib import asynccontextmanager

@asynccontextmanager
async def background_server():
    async with trio.open_nursery() as nursery:
        value = await nursery.start(my_server)
        try:
            yield value
        finally:
            # Kill the server when the scope exits
            nursery.cancel_scope.cancel()

This is the same, but works on 3.5+:

from async_generator import async_generator, yield_, asynccontextmanager

@asynccontextmanager
@async_generator
async def background_server():
    async with trio.open_nursery() as nursery:
        value = await nursery.start(my_server)
        try:
            await yield_(value)
        finally:
            # Kill the server when the scope exits
            nursery.cancel_scope.cancel()

(And if you're on 3.6, so you have built-in async generators but no built-in @asynccontextmanager, then don't worry, you can use async_generator.asynccontextmanager on native async generators.)

Let's do this

How come some of those links talk about "trio"?

Trio is a new async concurrency library for Python that's obsessed with usability and correctness – we want to make it easy to get things right. The async_generator library is maintained by the Trio project as part of that mission, and because Trio uses async_generator internally.

You can use async_generator with any async library. It works great with asyncio, or Twisted, or whatever you like. (But we think Trio is pretty sweet.)