pyo3-async-runtimes
PyO3 bridges between Rust async runtimes and Python's asyncio
Repository Health
Technical Analysis
pyo3-async-runtimes converts between Rust Futures and Python asyncio coroutines, letting native PyO3 extension modules expose async Rust functions as awaitable Python functions, and letting Rust applications embedding Python await async Python code. It supports both tokio and async-std as the Rust-side runtime, selected via Cargo feature flags, with all async Python code still running on the standard asyncio event loop for compatibility with existing Python libraries.
The crate is a fork of the now-unmaintained pyo3-asyncio, continued to track newer PyO3 releases (0.21+) and maintained under the official PyO3 GitHub organization. It provides both a low-level generic module for building custom runtime integrations and ready-made tokio/async_std modules with #[main]-style attribute macros for the common cases.
What You Get
into_future()to convert a Python coroutine into an awaitable RustFuturefuture_into_py()to expose a RustFutureas an awaitable object callable from Pythonasync defcode- Runtime-specific modules for
tokioandasync-std, each with#[main]attribute macros for managing event-loop startup - A
genericmodule for implementing support for additional custom async runtimes - A
testingfeature with utilities for writing PyO3 async integration tests that run under bothasyncioanduvloop
Common Use Cases
- Writing native Python extension modules (via
maturin) that expose async Rust functions asasync def-callable Python functions - Calling existing async Python libraries (e.g. an
asyncio-based SDK) from a Rust application that embeds the Python interpreter - Building performance-critical async I/O paths in Rust while keeping the surrounding application logic in Python
- Testing PyO3 async integrations against multiple event-loop implementations (stock
asyncio,uvloop) via the crate’stestingfeature
Under The Hood
Architecture — The crate’s core logic lives in src/generic.rs (the largest module, ~1,900 lines), which implements runtime-agnostic conversion logic between Rust futures and Python awaitables using a generic Runtime trait, so each concrete backend only needs to supply an executor and spawn primitive. src/tokio.rs and src/async_std.rs (each 700-850 lines) implement that trait for their respective runtimes and re-export the #[main] proc-macro attributes (defined in the separate pyo3-async-runtimes-macros workspace member) that wrap a user’s async fn main to correctly initialize the Python interpreter and hand control of the main thread to Python’s event loop — a PyO3-specific constraint the README documents at length, since Python’s asyncio needs the main thread for signal handling. src/testing.rs provides a clap+inventory-based test harness for running the same test suite against multiple event-loop backends.
Tech Stack — Rust, organized as a Cargo workspace with a pyo3-async-runtimes-macros proc-macro crate; core dependency is pyo3 itself, with tokio and async-std as optional runtime backends behind feature flags (tokio-runtime, async-std-runtime), plus async-channel/futures-channel for the unstable-streams feature. Requires Rust 1.83+ and targets Python 3.9+ (CPython and PyPy).
Code Quality — The pytests/ directory contains dedicated integration tests for every runtime/event-loop combination (test_tokio_multi_thread_uvloop.rs, test_async_std_asyncio.rs, test_race_condition_regression.rs, etc.) plus a documented race-condition regression test, indicating the project actively tracks and guards against concurrency bugs at the Rust/Python boundary. CI badges (GitHub Actions, codecov) are present in the README, and the crate has 24 contributors and 8 tagged releases since its 2024 fork, showing sustained maintenance under new ownership.
API Design — The primary entry points (into_future, future_into_py) are two functions, and the attribute macros (#[pyo3_async_runtimes::tokio::main]) remove most of the event-loop bootstrapping boilerplate a user would otherwise have to write by hand. The tradeoff, clearly documented in the README, is that Python’s need for main-thread control means users can’t use the runtime’s own convenience macros (#[tokio::main]) directly — they must use this crate’s variants instead, which is a small but real learning curve for developers new to embedding Python.
Used by 3 apps in this directory
LanceDB
Databases · AI Development
Open-source, embedded vector database built on the Lance columnar format for fast multimodal search across billions of vectors, backed by Y Combinator (W23).
monty
AI Development · Developer Tools
Run LLM-generated Python code safely inside your agent—no containers, no CPython, no compromise—with sub-microsecond startup.
Rivet
AI Agents · Developer Tools
Stateful actors as a primitive for AI agents, real-time collaboration, and durable execution — with in-memory state, WebSockets, queues, and scheduling built in.