mrkeyoor.com_
Sat 08 Aug 22:49 UTC
PyPIWeb Backendupdated 08 Aug 2026

asyncio

The asyncio project on PyPI is a placeholder, not a library. It began as a backport of the asyncio module for Python versions before 3.4, which stopped being useful the moment asyncio shipped in the standard library in 2014. Version 4.0.0, published in August 2025, contains no code at all; its own description says the release exists only to prevent accidental installation of outdated backports and tells you to use the implementation that comes with Python. The thing you actually want, the event loop, coroutines, tasks and streams, is already installed with your interpreter and needs no dependency.

Verdict

This distribution is a tombstone: the current release contains no code and exists to stop people installing a 2015 backport of a standard library module. Remove it from your requirements and import asyncio from the interpreter you already have.

API stability5/5There is no API here to be unstable, since 4.0.0 ships no modules. Judged against what it stands in for, the standard library module has kept asyncio.run, coroutines, tasks, futures, queues and streams compatible for years, and additions such as TaskGroup in 3.11 arrived alongside the old primitives rather than replacing them. The one genuine break is get_event_loop, which went from implicitly creating a loop to warning in 3.12 and raising in 3.14, and that is a CPython change rather than anything this package controls.
Docs4/5The package description is four short paragraphs and does the only job it has: it says the backport is obsolete, tells you not to install it, links to the module documentation, and explains that the release exists to block accidental installs of old versions. That is clearer than most deprecation notices. The score is not higher only because the PyPI page says nothing about the version 3.4.x releases still sitting in the index, which are the ones capable of causing real trouble.
Maintenance1/5The linked repository, python/asyncio, has been archived since 2021-04-28, carries the description asyncio historical repository, and has a README that says the repository is closed and directs pull requests to CPython. Its default branch is literally named redirect. The distribution itself last changed in August 2025 to become an empty tombstone. Nothing about the package is maintained, and nothing needs to be; the code it once contained lives in CPython, which is maintained on its own schedule.
Ecosystem5/5Judged by the module it points at, the ecosystem is enormous: aiohttp, httpx, asyncpg, FastAPI, Starlette, SQLAlchemy's async engine, redis-py and countless others target the stdlib event loop, and AnyIO exists to let Trio-style code run on it. uvloop swaps in a faster loop without changing a line of application code. That breadth is the reason nobody needs this distribution, and it is also why the empty tombstone still records over six million weekly installs from stale requirements files.

Use it if

  • You are auditing a requirements file, found asyncio pinned in it, and want confirmation that removing the line is safe on any supported Python
  • You maintain a package whose install instructions still mention this distribution and need the reference that says it is empty
  • You are looking for how to use the standard library module, which is what the patterns below cover, and got here by searching the package name
  • You need to explain to a teammate why pip install asyncio appears to succeed and yet changes nothing about their program
Skip it if

Setup reality

There is no setup, and that is the whole point of the entry. pip install asyncio completes without error, which is exactly why it keeps ending up in requirements files: it looks like it worked. On Python 3.4 and later, nothing you install under this name affects the asyncio you import, because the standard library module always wins the import. The one way this package can hurt you is by installing an old release. Versions 3.4.1 through 3.4.3, from 2015, contain a real copy of the module from that era, and depending on your environment layout that copy can shadow the interpreter's own, which produces bewildering failures against modern code. Version 4.0.0 was published precisely to stop that, since a fresh install now resolves to an empty distribution. Practical advice is short: delete the requirement, then import asyncio and check nothing changes. If a transitive dependency pins it, look at whether that dependency has been touched since 2015 before worrying about the pin. The links in the package metadata point where you should be reading anyway: the module documentation at docs.python.org and the historical repository at python/asyncio, which has been archived since April 2021 and whose README directs bug reports to CPython. For the real module, the version of Python matters far more than any package version. TaskGroup, asyncio.timeout and Runner all arrived in 3.11 and changed how correct asyncio code is written; asyncio.to_thread arrived in 3.9; and get_event_loop moved from quietly creating a loop, to warning in 3.12, to raising in 3.14. Code copied from a pre-3.11 tutorial will run and still be the wrong shape.

Patterns

Take it out of your requirementsremove-the-dependency

# requirements.txt
-asyncio==3.4.3

# nothing else changes; the module is part of Python
import asyncio
print(asyncio.__file__)  # .../lib/python3.12/asyncio/__init__.py

Print __file__ after removing it. If the path is inside your interpreter's lib directory rather than site-packages, the standard library is what you were using all along.

Start an event loop the current wayrun-a-coroutine

import asyncio

async def main():
    await asyncio.sleep(0.1)
    return "done"

if __name__ == "__main__":
    print(asyncio.run(main()))

asyncio.run creates a loop, runs the coroutine and closes the loop. Since Python 3.7 there is no reason to touch get_event_loop or run_until_complete in application code.

Run tasks concurrently with structured concurrencytask-group

import asyncio

async def main():
    async with asyncio.TaskGroup() as tg:
        a = tg.create_task(fetch("a"))
        b = tg.create_task(fetch("b"))
    return a.result(), b.result()

asyncio.run(main())

Requires Python 3.11. If one task raises, the rest are cancelled and the group raises an ExceptionGroup, so catch it with except* rather than a plain except.

Collect results on older interpretersgather-results

import asyncio

async def main():
    results = await asyncio.gather(fetch("a"), fetch("b"), return_exceptions=True)
    for r in results:
        if isinstance(r, Exception):
            log(r)

asyncio.run(main())

gather is the pre-3.11 option. Without return_exceptions the first failure propagates while the other coroutines keep running unsupervised, which is the gap TaskGroup closes.

Bound how long a block may taketimeout

import asyncio

async def main():
    try:
        async with asyncio.timeout(5):
            await slow_call()
    except TimeoutError:
        return None

asyncio.run(main())

asyncio.timeout arrived in 3.11 and raises the builtin TimeoutError. On 3.10 and earlier use asyncio.wait_for, which takes the awaitable rather than wrapping a block.

Move blocking work off the loopblocking-call-offload

import asyncio

async def main():
    data = await asyncio.to_thread(open("big.csv").read)
    return len(data)

asyncio.run(main())

asyncio.to_thread needs 3.9. A blocking call made directly inside a coroutine stalls every other task on that loop, which is the most common cause of a slow async program.

Hand work between tasks with a queueproducer-consumer

import asyncio

async def worker(q):
    while True:
        item = await q.get()
        try:
            await handle(item)
        finally:
            q.task_done()

async def main():
    q = asyncio.Queue(maxsize=100)
    workers = [asyncio.create_task(worker(q)) for _ in range(4)]
    for item in source():
        await q.put(item)
    await q.join()
    for w in workers:
        w.cancel()

asyncio.run(main())

maxsize is the backpressure. Cancel the workers after join, or asyncio.run will warn about tasks still pending when the loop closes.

Clean up when a task is cancelledhandle-cancellation

import asyncio

async def job():
    try:
        await long_running()
    except asyncio.CancelledError:
        await release_resources()
        raise

Re-raise CancelledError. Swallowing it makes the task uncancellable and leaves TaskGroup and timeout blocks waiting on work they already gave up on.

Hold on to background taskskeep-task-references

import asyncio

background = set()

def spawn(coro):
    task = asyncio.create_task(coro)
    background.add(task)
    task.add_done_callback(background.discard)
    return task

The event loop keeps only a weak reference to a task. Without a strong reference somewhere, a background task can be garbage collected mid-flight and simply disappear.

Stop calling get_event_loopavoid-get-event-loop

import asyncio

# inside a coroutine, when you really need the loop object
loop = asyncio.get_running_loop()

# not this: warns on 3.12, raises RuntimeError on 3.14
# loop = asyncio.get_event_loop()

get_running_loop has been the correct call since 3.7 and fails loudly outside a running loop instead of quietly creating a second one.

Serve connections with the streams APIrun-a-server

import asyncio

async def handle(reader, writer):
    data = await reader.readline()
    writer.write(data.upper())
    await writer.drain()
    writer.close()
    await writer.wait_closed()

async def main():
    server = await asyncio.start_server(handle, "127.0.0.1", 8888)
    async with server:
        await server.serve_forever()

asyncio.run(main())

await writer.drain() is the backpressure hook; skipping it lets the write buffer grow without limit when a client reads slowly.

Use a faster event loop without changing your codeswap-the-loop

import asyncio
import uvloop

async def main():
    await work()

uvloop.run(main())  # or asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

uvloop is a separate package and is not available on Windows. Your application code stays as it is; only the loop implementation changes.

Alternatives

PackageRegistryPick it when
anyioPyPIYou want structured concurrency and cancellation scopes over the stdlib loop, and want the same code to run on Trio as well
trioPyPIYou are starting fresh and want nurseries and cancellation designed in from the beginning rather than added in 3.11
uvloopPyPIYou are keeping stdlib asyncio and want a faster event loop implementation underneath it on Linux or macOS