{"id":3290,"library":"taskgroup","title":"asyncio.TaskGroup, Runner, and Timeout Backport","description":"The `taskgroup` library provides a backport of the `asyncio.TaskGroup`, `asyncio.Runner`, and `asyncio.timeout` functionalities from Python 3.12.8 to older Python versions, specifically Python 3.8, 3.9, 3.10, and 3.11. This allows developers to leverage the structured concurrency model introduced in Python 3.11+ for `asyncio` in environments that are not yet able to upgrade to the latest Python releases.","status":"active","version":"0.2.2","language":"en","source_language":"en","source_url":"https://github.com/graingert/taskgroup","tags":["asyncio","backport","concurrency","python3.8","python3.9","python3.10","python3.11"],"install":[{"cmd":"pip install taskgroup","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"note":"For Python versions < 3.11, otherwise use `from asyncio import TaskGroup`.","symbol":"TaskGroup","correct":"from taskgroup import TaskGroup"},{"note":"For Python versions < 3.11, otherwise use `from asyncio import Runner`.","symbol":"Runner","correct":"from taskgroup import Runner"},{"note":"For Python versions < 3.11, otherwise use `from asyncio import timeout`.","symbol":"timeout","correct":"from taskgroup import timeout"},{"note":"For Python versions < 3.11, otherwise use `from asyncio import run`.","symbol":"run","correct":"from taskgroup import run"}],"quickstart":{"code":"import sys\nimport asyncio\n\n# Conditionally import TaskGroup, run, and timeout based on Python version\nif sys.version_info >= (3, 11):\n    from asyncio import TaskGroup, run, timeout\nelse:\n    from taskgroup import TaskGroup, run, timeout\n\nasync def task_printer(name, delay):\n    print(f\"Task {name}: Starting in {delay} seconds...\")\n    await asyncio.sleep(delay)\n    print(f\"Task {name}: Finished.\")\n    return f\"Result from {name}\"\n\nasync def main_task_group():\n    print(\"Main: Creating tasks...\")\n    async with TaskGroup() as group:\n        task1 = group.create_task(task_printer(\"A\", 2))\n        task2 = group.create_task(task_printer(\"B\", 1))\n        task3 = group.create_task(task_printer(\"C\", 3))\n    print(\"Main: All tasks in group finished.\")\n    print(f\"Task A result: {task1.result()}\")\n    print(f\"Task B result: {task2.result()}\")\n    print(f\"Task C result: {task3.result()}\")\n\nasync def main_runner():\n    print(\"\\n--- Using Runner ---\")\n    async def inner_coro():\n        await task_printer(\"D\", 0.5)\n        await task_printer(\"E\", 1.5)\n    \n    with Runner() as runner:\n        runner.run(inner_coro())\n    print(\"Runner finished.\")\n\nasync def main_timeout():\n    print(\"\\n--- Using Timeout ---\")\n    async def long_running_task():\n        await asyncio.sleep(5)\n        print(\"Long running task completed (should not happen if timeout works).\")\n    \n    try:\n        async with timeout(2):\n            await long_running_task()\n    except TimeoutError:\n        print(\"Task timed out as expected.\")\n\nif __name__ == \"__main__\":\n    print(f\"Running with {'asyncio' if sys.version_info >= (3, 11) else 'taskgroup backport'}.\")\n    run(main_task_group())\n    run(main_runner())\n    run(main_timeout())","lang":"python","description":"This quickstart demonstrates the usage of `TaskGroup`, `Runner`, and `timeout` through the `taskgroup` backport (or `asyncio` directly if Python 3.11+). It shows how to create and manage multiple concurrent tasks with `TaskGroup`, how to run a top-level coroutine with `Runner`, and how to apply a timeout to an asynchronous operation."},"warnings":[{"fix":"Refer to the official `asyncio` documentation for `TaskGroup` behavior and be aware that the backport might not perfectly match all nuances, especially regarding complex cancellation scenarios or recent CPython bug fixes.","message":"This library is a backport. While it aims for faithful replication, subtle behavioral differences or edge cases might exist compared to the native `asyncio.TaskGroup`, `Runner`, or `timeout` in Python 3.11+. Always test thoroughly.","severity":"gotcha","affected_versions":"< 0.2.2"},{"fix":"Prefer the native `asyncio.TaskGroup`, `Runner`, and `timeout` on Python 3.11 and later. Use conditional imports (as shown in the quickstart) to ensure the correct implementation is used for the host Python version.","message":"The `asyncio.TaskGroup` requires Python 3.11 or higher. Using this backport on Python 3.11+ might lead to conflicts or unexpected behavior if both the native and backported versions are accidentally imported or used, although the provided quickstart handles this with a `sys.version_info` check.","severity":"breaking","affected_versions":"All versions when used on Python 3.11+"},{"fix":"Familiarize yourself with `ExceptionGroup` and its handling via `except*` as introduced in Python 3.11. Design your async code within `TaskGroup` to gracefully handle task cancellations and aggregate exceptions.","message":"When an exception occurs in a task within an `asyncio.TaskGroup`, all other running tasks in that group are cancelled, and all non-cancellation exceptions are propagated as an `ExceptionGroup`. Proper error handling using `except*` (for `ExceptionGroup`) is crucial for robust applications. This behavior is fundamental to structured concurrency.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}