asyncio (PyPI backport)
The `asyncio` package on PyPI (version 4.0.0) is a deprecated backport of the `asyncio` module, which has been part of Python's standard library since Python 3.4. This PyPI distribution is no longer needed and serves primarily to prevent accidental installation of outdated backports. Users should *not* install this package and instead rely on the built-in `asyncio` module provided by their Python installation.
Common errors
-
RuntimeWarning: coroutine 'foo' was never awaited
cause This warning occurs when a coroutine is defined but not awaited, meaning the coroutine is created but never executed.fixEnsure that all coroutines are awaited using the 'await' keyword: 'await foo()'. -
RuntimeError: This event loop is already running
cause This error arises when attempting to run a new event loop while another one is already running, often in environments like Jupyter notebooks.fixUse 'await' directly in Jupyter notebooks instead of 'asyncio.run()' to avoid nested event loops. -
Task was destroyed but it is pending!
cause This warning indicates that an asyncio Task was garbage collected before it had a chance to complete, often due to not keeping a reference to the Task.fixKeep a reference to the Task until it completes, for example by storing it in a variable or a list. -
AttributeError: module 'asyncio' has no attribute 'run'
cause This error occurs when trying to use 'asyncio.run()' in Python versions prior to 3.7, where this function is not available.fixFor Python versions before 3.7, use 'loop = asyncio.get_event_loop()' and 'loop.run_until_complete()' to run coroutines. -
RuntimeError: Event loop is closed
cause This error happens when an operation is attempted on an event loop that has already been closed.fixEnsure that the event loop is open before performing operations, and avoid closing the loop prematurely.
Warnings
- breaking The `asyncio` package on PyPI is an obsolete backport for Python versions prior to 3.4. Installing it on modern Python (3.4+) can conflict with and break the standard library `asyncio` due to `sys.path` order. Version 4.0.0 and above of the PyPI package contain no code and exist solely to prevent accidental installation of outdated backports; it *should not be installed*.
- deprecated The `@asyncio.coroutine` decorator for defining coroutines was deprecated in Python 3.8 and completely removed in Python 3.10. Using it will result in `SyntaxError` or `DeprecationWarning` depending on the Python version.
- gotcha A very common mistake is calling an `async def` function without `await`ing it. This creates a coroutine object but does not schedule or run it, leading to silent bugs or unexpected behavior (e.g., the code inside the coroutine never executes).
- gotcha `asyncio.run()` cannot be called when an event loop is already running in the current thread. This is a common issue in interactive environments like Jupyter notebooks or when attempting to nest `asyncio.run()` calls.
- breaking Python 3.12 introduced significant changes to `asyncio` affecting task management, exception handling within `TaskGroup`, and cancellation behavior. The `asyncio.get_event_loop()` function became stricter, and `asyncio.run()` now explicitly ensures the event loop and associated resources (like executors and async generators) are properly shut down by default. Code relying on low-level event loop interactions or specific task behavior might encounter breaking changes.
- breaking Python 3.14 changed `asyncio`'s internal implementation in ways that break previous workarounds for nested event loops, notably affecting libraries like `nest_asyncio`. This primarily impacts environments like Jupyter notebooks where an event loop is already active, preventing the automatic async-to-sync conversion previously offered by such workarounds.
- gotcha Using `asyncio.Lock` (or other synchronization primitives like `Semaphore`, `Event`) in a global scope and then repeatedly calling `asyncio.run()` can lead to `RuntimeError`. This happens because `asyncio.run()` creates a new event loop each time, and the global lock object might retain a stale reference to a previously closed loop.
Install
-
pip install asyncio
Imports
- asyncio
import asyncio
Quickstart
import asyncio
import time
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
print(f"started at {time.strftime('%X')}")
await say_after(1, 'hello')
await say_after(2, 'world')
print(f"finished at {time.strftime('%X')}")
if __name__ == "__main__":
# The recommended way to run the top-level async function.
asyncio.run(main())