{"id":1913,"library":"async-generator","title":"Async Generators and Context Managers","description":"The `async-generator` library provides a backport of asynchronous generators and asynchronous context managers to Python 3.5, which were introduced natively in Python 3.6 (PEP 525) and 3.7. It allows developers to write cleaner asynchronous code for stream processing and I/O-bound tasks using familiar generator syntax. Maintained by the Trio project, it is compatible with any async framework like asyncio or Trio. The current version is 1.10, and it is considered stable, with the last release in July 2018.","status":"active","version":"1.10","language":"en","source_language":"en","source_url":"https://github.com/python-trio/async_generator","tags":["async","generator","context manager","backport","asyncio","trio","python3.5"],"install":[{"cmd":"pip install async-generator","lang":"bash","label":"Install stable version"}],"dependencies":[],"imports":[{"note":"The primary decorator for async generator functions is named `async_generator`.","wrong":"from async_generator import generator","symbol":"async_generator","correct":"from async_generator import async_generator"},{"note":"In Python 3.5, inside an `@async_generator` decorated function, you must use `await yield_(value)` instead of a bare `yield value`.","wrong":"yield some_value","symbol":"yield_","correct":"from async_generator import yield_"},{"note":"This provides a backported `asynccontextmanager` decorator for Python 3.5, similar to `contextlib.asynccontextmanager` in Python 3.7+.","wrong":"from contextlib import asynccontextmanager","symbol":"asynccontextmanager","correct":"from async_generator import asynccontextmanager"},{"note":"Used as an async context manager to ensure proper cleanup of async generators, similar to `contextlib.closing`.","symbol":"aclosing","correct":"from async_generator import aclosing"}],"quickstart":{"code":"import asyncio\nfrom async_generator import async_generator, yield_, asynccontextmanager, aclosing\n\n@async_generator\nasync def my_async_generator(limit):\n    for i in range(limit):\n        await asyncio.sleep(0.01)\n        await yield_(i)\n\n@asynccontextmanager\n@async_generator\nasync def managed_resource():\n    print(\"\\n[SETUP] Acquiring resource...\")\n    resource = []\n    try:\n        await yield_(resource)\n    finally:\n        print(\"[TEARDOWN] Releasing resource.\")\n        resource.clear()\n\nasync def main():\n    print(\"--- Using async generator ---\")\n    async for item in my_async_generator(3):\n        print(f\"Consumed: {item}\")\n\n    print(\"\\n--- Using async context manager ---\")\n    async with managed_resource() as res:\n        res.append(\"data\")\n        print(f\"Resource in context: {res}\")\n    print(\"Resource out of context.\")\n\n    print(\"\\n--- Generator cleanup with aclosing ---\")\n    gen = my_async_generator(5)\n    async with aclosing(gen):\n        # Consume partially\n        await gen.__anext__() # Consume first item (0)\n        print(\"Partially consumed async generator, 'aclosing' will ensure cleanup.\")\n    print(\"Generator closed via aclosing.\")\n\nif __name__ == '__main__':\n    asyncio.run(main())","lang":"python","description":"This example demonstrates creating an asynchronous generator with `@async_generator` and `await yield_`, an asynchronous context manager with `@asynccontextmanager`, and ensuring proper cleanup of a partially consumed async generator using `aclosing`."},"warnings":[{"fix":"Always wrap async generator iteration in an `aclosing` context manager from this library (or `contextlib.async_closing` in 3.10+). For example: `async with aclosing(my_agen()) as agen: async for item in agen: ...`","message":"Async generators (especially when partially consumed or exited via `break`) do not guarantee `finally` block execution or proper resource cleanup without explicit closing. The garbage collector cannot await cleanup tasks.","severity":"gotcha","affected_versions":"All versions (Python 3.5+), including native async generators in 3.6+."},{"fix":"Ensure all `yield` statements in `@async_generator` decorated functions are replaced with `await yield_()` when targeting Python 3.5. Python 3.6+ supports native `yield` in `async def` functions.","message":"For Python 3.5, you *must* use `await yield_(value)` within an `@async_generator` decorated function. Direct `yield value` (native async generator syntax) is a syntax error in Python 3.5 and will not work.","severity":"breaking","affected_versions":"Python 3.5"},{"fix":"Ensure your async generator functions contain at least one explicit `yield` or `await yield_()` statement, even if their primary purpose is to delegate with `await yield_from_()` to another async iterator.","message":"A native async generator in Python 3.6+ that *only* contains `await yield_from_(...)` calls might not be correctly recognized as an async generator by the Python compiler, leading to unexpected behavior. It needs at least one actual `yield` or `await yield_()` expression.","severity":"gotcha","affected_versions":"All versions when used with native async generators (Python 3.6+)."},{"fix":"When using native async generators (Python 3.6+), ensure they only return `None` implicitly or explicitly. If a value needs to be communicated, yield it as the last item. If using `async-generator`'s decorated functions, be aware this behavior differs from native.","message":"While `async-generator` supports returning non-`None` values from an `@async_generator` decorated function (similar to `return 'value'` in regular generators), native async generators introduced in Python 3.6 (PEP 525) do not support non-empty `return` statements and will raise a `SyntaxError`. This can cause confusion if mixing patterns.","severity":"gotcha","affected_versions":"All versions, especially when migrating or mixing with native async generators (Python 3.6+)."}],"env_vars":null,"last_verified":"2026-04-09T00:00:00.000Z","next_check":"2026-07-08T00:00:00.000Z"}