Duet Async Library

0.2.9 · active · verified Thu Apr 16

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.

Common errors

Warnings

Install

Imports

Quickstart

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.

import concurrent.futures
import time
import duet

def long_running_sync_task(value):
    """A blocking synchronous task."""
    time.sleep(0.1) # Simulate work
    return f"Processed {value}"

async def async_wrapper(future: concurrent.futures.Future):
    """Wraps a concurrent.futures.Future to be awaitable in Duet."""
    return await duet.AwaitableFuture(future)

async def main():
    print("Starting Duet example...")
    
    # Example 1: Running a synchronous task in a thread pool executor
    with concurrent.futures.ThreadPoolExecutor() as executor:
        future = executor.submit(long_running_sync_task, "data_item_A")
        result_a = await async_wrapper(future)
        print(f"Result 1: {result_a}")

        # Example 2: Demonstrate re-entrancy (calling duet.run within an async function)
        # In most async libraries (e.g., asyncio, trio), this would raise a RuntimeError
        # Duet explicitly allows it for easier refactoring of mixed sync/async code.
        async def nested_task():
            print("  Running nested_task synchronously via duet.run...")
            nested_future = executor.submit(long_running_sync_task, "data_item_B")
            nested_result = await async_wrapper(nested_future)
            print(f"  Nested task result: {nested_result}")
            return "Nested task completed"

        nested_call_result = duet.run(nested_task()) 
        print(f"Result 2: {nested_call_result}")

    print("Duet example finished.")

if __name__ == "__main__":
    duet.run(main())

view raw JSON →