rsloop is a PyO3-based asyncio event loop implemented in Rust.
Each rsloop.Loop owns a dedicated Rust runtime thread for loop coordination
and I/O work. That thread runs a vibeio runtime, using io_uring on Linux,
IOCP on Windows, and mio-backed readiness on other supported platforms. Plain
TCP / Unix socket reads and non-TLS server accepts run on that runtime. Python
callbacks, tasks, and coroutines still run on the thread that calls
run_forever() or run_until_complete() (usually the main Python thread).
The package exposes:
rsloop._looppython/rsloop/__init__.pyrsloop.Loop, rsloop.EventLoopPolicy, rsloop.new_event_loop(),
rsloop.run(...), rsloop.install(), and rsloop.uninstall()Repository metadata currently targets Python >=3.8. The packaged project now
supports the core event-loop surface on Linux, macOS, and Windows, including
Windows pipe transports and subprocess workflows.
Project documentation now lives in docs/.
If you are new to the repository, start with:
To browse the docs locally with MkDocs:
uvx --from mkdocs mkdocs serve
From PyPI:
With uv:
From conda-forge, using pixi:
UsageSimple entry point:
import rsloop async def main(): ... rsloop.run(main())
Install as the default asyncio event loop policy:
import asyncio import rsloop rsloop.install() try: asyncio.run(main()) finally: rsloop.uninstall()
Manual loop creation also works:
import asyncio import rsloop loop = rsloop.new_event_loop() asyncio.set_event_loop(loop) try: loop.run_until_complete(...) finally: asyncio.set_event_loop(None) loop.close()
Importing rsloop also patches asyncio.set_event_loop() so Python 3.8 can
accept an rsloop.Loop instance, matching the behavior exercised by
tests/test_run.py.
rsloop now exposes a small Rust interop API for downstream PyO3 extensions.
That lets you write your own async Rust code, return it to Python as an
awaitable, and run it under the active rsloop event loop.
The public entry point is rsloop::rust_async:
get_current_locals(...)future_into_py(...)future_into_py_with_locals(...)local_future_into_py(...)local_future_into_py_with_locals(...)TaskLocals and into_future_with_locals(...)See examples/rust/README.md for a complete
extension example built with maturin.
The current codebase implements these user-facing areas.
Loop lifecycle and scheduling:
run_forever, run_until_complete, stop, closetime, is_running, is_closedget_debug, set_debugcall_soon, call_soon_threadsafe, call_later, call_atHandle and TimerHandle objects with cancel() / cancelled()Tasks, futures, and execution helpers:
create_future, create_taskset_task_factory, get_task_factoryset_exception_handler, get_exception_handler,
call_exception_handler, default_exception_handlerset_default_executor, run_in_executorshutdown_asyncgens, shutdown_default_executorcontextvars.Contextasyncio.get_running_loop() support while running on rslooprsloop.run(...) helper, with asyncio.run(..., loop_factory=...)
integration on Python 3.12+I/O and networking:
add_reader, remove_reader, add_writer, remove_writersock_recv, sock_recv_into, sock_sendall, sock_accept, sock_connectgetaddrinfo, getnameinfocreate_server, create_connectioncreate_unix_server, create_unix_connectionconnect_accepted_socketServer objects with close(), is_serving(), get_loop(),
and sockets()StreamTransport objects with write(), writelines(), close(),
abort(), is_closing(), write_eof(), can_write_eof(),
get_extra_info(), get_protocol(), set_protocol(),
pause_reading(), resume_reading(), is_reading()Pipes, subprocesses, and signals:
connect_read_pipe, connect_write_pipesubprocess_exec, subprocess_shellProcessTransport and ProcessPipeTransport objectsasyncio.create_subprocess_exec() and
asyncio.create_subprocess_shell()cwd, env, executable, pass_fds,
start_new_session, process_group, user, group, extra_groups,
umask, and restore_signalsadd_signal_handler, remove_signal_handlerProfiling:
profile(...), profiler_running(), start_profiler(), stop_profiler()Importing rsloop patches asyncio.open_connection() and
asyncio.start_server() by default.
That import-time behavior is controlled by RSLOOP_USE_FAST_STREAMS and can be
disabled with:
export RSLOOP_USE_FAST_STREAMS=0The native fast-stream path is used only when:
rsloop.Loopssl is unset or NoneOtherwise rsloop falls back to the stdlib asyncio.streams helpers.
The implementation lives in src/fast_streams.rs and
is backed by the lower level transport code in
src/stream_transport.rs.
The runtime is centered on one vibeio runtime per loop:
vibeio on that
thread across supported platformsadd_reader / add_writer descriptors use cancellable OS-poll
workers because vibeio does not expose arbitrary raw-descriptor registrationThe runtime dependency is now unified, but the codebase has not finished eliminating every helper thread yet.
Current LimitationsThese gaps are visible in the current implementation.
rustls backend with a narrower compatibility surface than
CPython's OpenSSL-backed ssl module. In particular, encrypted private keys
are not supported yet, and the fast-stream monkeypatch still falls back to
stdlib helpers whenever ssl is enabled. TLS transport internals also still
use helper-thread paths instead of the runtime-thread vibeio socket
path.preexec_fn remains unsupported because running arbitrary Python between
fork() and exec() is unsafe in this runtime model.create_unix_server, create_unix_connection,
add_signal_handler, remove_signal_handler.pass_fds, user, group, and umask are
still specific to Unix process spawning.Quick check:
Release build and editable install:
cargo build --release uv run --with maturin maturin develop --release
Build release wheels into dist/wheels:
scripts/build-wheels.sh currently defaults to
CPython 3.8 3.9 3.10 3.11 3.12 3.13 3.14 plus free-threaded 3.14t, and
uses uv python install / uv python find to locate interpreters.
Profiling is behind the Cargo feature profiler and is disabled by default.
Build or install with that feature first:
cargo build --release --features profiler uv run --with maturin maturin develop --release --features profiler
Then wrap the code you want to inspect:
import rsloop with rsloop.profile(): rsloop.run(main())
Or manage the session manually:
import rsloop rsloop.start_profiler() try: rsloop.run(main()) finally: rsloop.stop_profiler()
This starts a Tracy client inside the process. Build a release binary, open the Tracy desktop profiler, then connect to the running process while the profiled code is executing.
Release wheels do not include profiler support. Build locally with
--features profiler to enable it. The Tracy feature set is aimed at local
profiling: enable, only-localhost, and sampling.
For very short-lived runs you can force the process to block on exit until a
server has connected and drained all data by setting TRACY_NO_EXIT=1 in the
environment.
If the extension was built without --features profiler, profile() and
start_profiler() raise a runtime error.
Run the repository examples from the project root:
uv run python examples/01_basics.py uv run python examples/02_fd_and_sockets.py uv run python examples/03_streams.py uv run python examples/04_unix_and_accepted_socket.py uv run python examples/05_pipes_signals_subprocesses.py
Example files:
examples/01_basics.py,
examples/02_fd_and_sockets.py,
examples/03_streams.py,
examples/04_unix_and_accepted_socket.py,
examples/05_pipes_signals_subprocesses.py.
The repository also includes:
demo/fastapi_service.py for running the same
FastAPI app on stdlib asyncio, uvloop, or rsloopbenchmarks/compare_event_loops.py
for callback, task, and TCP stream comparisonsuv run --with maturin maturin develop --release uv run --with uvloop python benchmarks/compare_event_loops.py
An example output from that script on macOS (arm64) with CPython 3.14:
callbacks (200,000 ops)
loop median_s best_s ops_per_s peak_rss vs_fastest
rsloop 0.033083 0.032710 6,045,401 67.5 MiB 1.00x
uvloop 0.040958 0.040721 4,883,026 72.8 MiB 1.24x
asyncio 0.082233 0.082093 2,432,114 65.3 MiB 2.49x
tasks (50,000 ops)
loop median_s best_s ops_per_s peak_rss vs_fastest
rsloop 0.063593 0.063286 786,247 37.6 MiB 1.00x
uvloop 0.069614 0.069420 718,251 38.4 MiB 1.09x
asyncio 0.108114 0.107502 462,473 36.1 MiB 1.70x
tcp_streams (5,000 ops)
loop median_s best_s ops_per_s peak_rss vs_fastest
rsloop 0.090940 0.083355 54,981 32.2 MiB 1.00x
uvloop 0.133182 0.127404 37,543 31.5 MiB 1.46x
asyncio 0.302337 0.299813 16,538 29.6 MiB 3.32x
See benchmarks/README.md for workload details and
extra flags, and demo/README.md for the FastAPI loop
comparison demo.
rsloop builds on the Python asyncio model and is implemented with
PyO3 on the Rust side. Runtime and socket I/O are powered by
vibeio.
This project is licensed under the Apache License, Version 2.0. See
LICENSE for the full text.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | silkworm-rs: Free-Threaded Compatible Async Web Scraper | 0 | 43.33 | 19-02-2026 |
| 2 | oxyde: Type-Safe, Pydantic-Centric Async ORM | 0 | 50 | 20-02-2026 |
| 3 | Pyre: New JIT Python interpreter written in Rust | 0 | 10 | 06-04-2026 |
| 4 | Oxyde ORM - Django-like Pydantic-driven Async ORM | 0 | 31.84 | 22-03-2026 |
| 5 | djangofmt - A fast, HTML aware, Django template formatter, written in Rust | 0 | 10 | 21-02-2026 |
| 6 | Caching an Asyncio Function the Easy Way | 0 | 6.6 | 20-03-2026 |
| 7 | bocpy: Behavior-Oriented Concurrency in Python | 0 | 30 | 12-06-2026 |
| 8 | django-modern-rest: REST With Types and Async Support | 0 | 24.29 | 23-04-2026 |
| 9 | Free-threaded Python: past, present, and future | 0 | 26.67 | 27-06-2026 |
| 10 | Asyncio Is Neither Fast Nor Slow | 0 | 7.6 | 30-01-2026 |