{"id":7194,"library":"duet","title":"Duet Async Library","description":"Duet is a simple future-based async library for Python, version 0.2.9. It takes inspiration from the `trio` library's structured concurrency but primarily relies on the standard `concurrent.futures.Future` interface for parallelism, allowing `async/await` coroutines to interact with these futures. A distinctive feature of Duet is its re-entrancy, permitting multiple calls to `duet.run()` within a single asynchronous context, which can simplify the incremental refactoring of synchronous codebases to asynchronous ones. The library is actively maintained with a steady release cadence for bug fixes and minor improvements.","status":"active","version":"0.2.9","language":"en","source_language":"en","source_url":"https://github.com/google/duet","tags":["async","concurrency","futures","structured-concurrency","event-loop"],"install":[{"cmd":"pip install duet","lang":"bash","label":"Install with pip"}],"dependencies":[{"reason":"Required Python version for the library.","package":"python","optional":false}],"imports":[{"note":"The primary entry point to execute an async function with Duet.","symbol":"run","correct":"from duet import run"},{"note":"Used to wrap standard `concurrent.futures.Future` objects to make them awaitable within Duet's async context.","symbol":"AwaitableFuture","correct":"from duet import AwaitableFuture"}],"quickstart":{"code":"import concurrent.futures\nimport time\nimport duet\n\ndef long_running_sync_task(value):\n    \"\"\"A blocking synchronous task.\"\"\"\n    time.sleep(0.1) # Simulate work\n    return f\"Processed {value}\"\n\nasync def async_wrapper(future: concurrent.futures.Future):\n    \"\"\"Wraps a concurrent.futures.Future to be awaitable in Duet.\"\"\"\n    return await duet.AwaitableFuture(future)\n\nasync def main():\n    print(\"Starting Duet example...\")\n    \n    # Example 1: Running a synchronous task in a thread pool executor\n    with concurrent.futures.ThreadPoolExecutor() as executor:\n        future = executor.submit(long_running_sync_task, \"data_item_A\")\n        result_a = await async_wrapper(future)\n        print(f\"Result 1: {result_a}\")\n\n        # Example 2: Demonstrate re-entrancy (calling duet.run within an async function)\n        # In most async libraries (e.g., asyncio, trio), this would raise a RuntimeError\n        # Duet explicitly allows it for easier refactoring of mixed sync/async code.\n        async def nested_task():\n            print(\"  Running nested_task synchronously via duet.run...\")\n            nested_future = executor.submit(long_running_sync_task, \"data_item_B\")\n            nested_result = await async_wrapper(nested_future)\n            print(f\"  Nested task result: {nested_result}\")\n            return \"Nested task completed\"\n\n        nested_call_result = duet.run(nested_task()) \n        print(f\"Result 2: {nested_call_result}\")\n\n    print(\"Duet example finished.\")\n\nif __name__ == \"__main__\":\n    duet.run(main())","lang":"python","description":"This quickstart demonstrates how to use Duet to run asynchronous code, wrap standard `concurrent.futures.Future` objects using `AwaitableFuture`, and showcases Duet's unique re-entrancy feature by calling `duet.run` within an already running async context."},"warnings":[{"fix":"Wrap your `concurrent.futures.Future` instance with `duet.AwaitableFuture(your_future)` before `await`ing it.","message":"When working with `concurrent.futures.Future` objects, they do not inherently support the `__await__` protocol. You must wrap them using `duet.AwaitableFuture` before you can `await` them directly within a Duet asynchronous function.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Understand that Duet allows nested `duet.run()` calls. If migrating from other async frameworks, adjust your expectations for loop re-entrancy or ensure you are not accidentally mixing event loops without proper bridging.","message":"Duet explicitly supports re-entrancy of its event loop, meaning you can call `duet.run()` from within an already running `duet.run()` context. This differs significantly from other popular async libraries like `asyncio` or `trio`, which would typically raise a `RuntimeError` in such a scenario. While this simplifies certain refactoring efforts, be aware of this unique behavior if you're accustomed to strict single-event-loop execution patterns.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Import `AwaitableFuture` from `duet` and wrap the future: `await duet.AwaitableFuture(your_future)`.","cause":"Attempting to `await` a `concurrent.futures.Future` object directly without wrapping it in `duet.AwaitableFuture`.","error":"TypeError: object Future can't be awaited"},{"fix":"Ensure you have imported the necessary components, e.g., `from duet import run` or `from duet import AwaitableFuture`.","cause":"The `run` function, or other Duet symbols, were used without being properly imported from the `duet` library.","error":"NameError: name 'run' is not defined"}]}