An event loop for asyncio written in Rust
Project description
rsloop: An event loop for asyncio written in Rust
rsloop is a PyO3-based asyncio event loop. Each rsloop.Loop still owns a
dedicated Rust runtime thread for timers, readiness polling, signal watchers,
and transport coordination, but Python callbacks, tasks, and coroutines are
driven on the thread that calls run_forever() / run_until_complete()
(typically the main thread). The package exposes a Python module at
rsloop._loop plus a small Python wrapper in python/rsloop/__init__.py.
The project metadata currently targets Python >=3.8.
Current Surface Area
Today’s codebase provides:
rsloop.Loop,rsloop.new_event_loop(), andrsloop.run(...)- loop lifecycle:
run_foreverrun_until_completestopclosetimeis_runningis_closed- debug flag getters/setters
- callback scheduling:
call_sooncall_soon_threadsafecall_latercall_at- returned
HandleandTimerHandleobjects
- future/task helpers:
create_futurecreate_taskset_task_factory/get_task_factory- exception handler storage and dispatch
set_default_executorrun_in_executorshutdown_asyncgensshutdown_default_executor
- callback execution inside captured
contextvars.Context asyncio.get_running_loop()support for coroutines driven byrsloopasyncio.run(..., loop_factory=rsloop.new_event_loop)support on Python 3.12+- Unix fd readiness callbacks via
add_reader/remove_reader/add_writer/remove_writer - raw socket awaitables:
sock_recvsock_recv_intosock_sendallsock_acceptsock_connect
- DNS awaitables:
getaddrinfogetnameinfo
- stream transports and servers:
create_servercreate_connectioncreate_unix_servercreate_unix_connectionconnect_accepted_socket- returned
ServerandStreamTransportobjects transport.close()/transport.abort()fully close socket transports
- pipe transports:
connect_read_pipeconnect_write_pipe
- subprocess transports:
subprocess_execsubprocess_shell- returned
ProcessTransportandProcessPipeTransportobjects - higher-level
asyncio.create_subprocess_exec() - higher-level
asyncio.create_subprocess_shell() - low-level support for
cwd,env,executable,pass_fds,start_new_session,process_group,user,group,extra_groups,umask, andrestore_signals
- Unix signal handlers via
add_signal_handler/remove_signal_handler - a free-threaded module declaration via
#[pymodule(gil_used = false)] - profiler entry points:
profileprofiler_runningstart_profilerstop_profiler
Fast Streams
Importing rsloop patches asyncio.open_connection() and
asyncio.start_server() by default. That import-time patch is controlled by
RSLOOP_USE_FAST_STREAMS and can be disabled with:
export RSLOOP_USE_FAST_STREAMS=0
The patched helpers only take the native rsloop fast-stream path when:
- the currently running loop is an
rsloop.Loop sslis unset orNone
Otherwise they fall back to the stdlib asyncio stream helpers. The fast
stream implementation lives in Rust in src/fast_streams.rs, while the lower
level transport implementation lives in src/stream_transport.rs.
Current Gaps
- TLS is not implemented:
start_tls()is stubbedssl=...is rejected forcreate_server,create_connection,create_unix_server,create_unix_connection, andconnect_accepted_socket
- stream transport flow control is still partial:
pause_reading()/resume_reading()workget_write_buffer_size()returns0get_write_buffer_limits()returns(0, 0)set_write_buffer_limits()is a no-op
- subprocess support is intentionally incomplete:
preexec_fnis unsupported- text mode is rejected for higher-level
asyncio.create_subprocess_exec()/asyncio.create_subprocess_shell()because the stdlib stream protocol is byte-oriented - low-level
loop.subprocess_exec()/loop.subprocess_shell()do support text decoding when used with a custom subprocess protocol
- Unix-only APIs remain Unix-only:
create_unix_servercreate_unix_connectionadd_signal_handler/remove_signal_handler
Build
Quick check:
cargo check
Release build and editable install:
cargo build --release
uv run --with maturin maturin develop --release
Build release wheels for CPython 3.8 through 3.14, plus the free-threaded
3.14 build, into dist/wheels:
scripts/build-wheels.sh
Publishing
PyPI releases are published from Git tags that start with v, for example
v0.1.0. The GitHub Actions workflow builds wheels by calling
scripts/build-wheels.sh, builds an sdist, and then publishes the combined
artifacts to PyPI with GitHub trusted publishing.
Before the first release, configure the rsloop project on PyPI as a trusted
publisher for this repository/workflow.
Use a release build for benchmarks and runtime comparisons. A debug extension will make the Rust loop look much slower than it really is.
Profiling
Profiling support is behind the Cargo feature profiler and is disabled by
default. Build/install a release extension with that feature enabled first:
cargo build --release --features profiler
uv run --with maturin maturin develop --release --features profiler
Then wrap the workload you want to inspect:
import rsloop
with rsloop.profile("rsloop-flamegraph.svg", frequency=999):
rsloop.run(main())
You can also manage the session manually:
import rsloop
rsloop.start_profiler(frequency=999)
try:
rsloop.run(main())
finally:
rsloop.stop_profiler("rsloop-flamegraph.svg")
This writes an SVG flamegraph that you can open directly in a browser. Run the
profile against a release build or the stack samples will be dominated by debug
overhead. If the extension was built without --features profiler,
start_profiler() and profile() will raise a runtime error.
Usage
Simple entry point:
import rsloop
async def main():
...
rsloop.run(main())
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()
Examples
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
The repository also includes:
demo/fastapi_service.pyfor running the same FastAPI app on stdlibasyncio,uvloop, orrsloopbenchmarks/compare_event_loops.pyfor comparing callback, task, and TCP stream workloads
Benchmark
uv run --with maturin maturin develop --release
uv run --with uvloop python benchmarks/compare_event_loops.py
See benchmarks/README.md for workload details and extra benchmark flags.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rsloop-0.1.0.tar.gz.
File metadata
- Download URL: rsloop-0.1.0.tar.gz
- Upload date:
- Size: 265.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a1a1fa5a1e764ad3eb3a123ca97019392ccccccf9386ab9062af9345de6811f
|
|
| MD5 |
a1fac2e0b36f926e61fdde2c7ea87914
|
|
| BLAKE2b-256 |
768115c4ee14518e4dea30ad916e16f4f2f7ec4cbbb2dbece5fd4baf63a8d2c9
|
File details
Details for the file rsloop-0.1.0-cp314-cp314t-manylinux_2_39_x86_64.whl.
File metadata
- Download URL: rsloop-0.1.0-cp314-cp314t-manylinux_2_39_x86_64.whl
- Upload date:
- Size: 842.6 kB
- Tags: CPython 3.14t, manylinux: glibc 2.39+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d60eacfbd98a84131addda3d6f220cffb76505a03ae7269c8df796057e4b6c1
|
|
| MD5 |
f58df43befd4edb761214d6dd5e62a12
|
|
| BLAKE2b-256 |
000691b317a823c04301f8e8c5ea271027213d73015687daff9a21835cea7552
|
File details
Details for the file rsloop-0.1.0-cp314-cp314-manylinux_2_39_x86_64.whl.
File metadata
- Download URL: rsloop-0.1.0-cp314-cp314-manylinux_2_39_x86_64.whl
- Upload date:
- Size: 851.2 kB
- Tags: CPython 3.14, manylinux: glibc 2.39+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c24819a74c0e544767b48ca4176d99e11f593a7b2bbedee2967fd8a3b5fa0655
|
|
| MD5 |
091074e1ee13646f0daea7028f369edf
|
|
| BLAKE2b-256 |
74c7c98360dfd05763e6aedab7100dd7434fc84e0df392784d2cda1c294dc7d8
|
File details
Details for the file rsloop-0.1.0-cp313-cp313-manylinux_2_39_x86_64.whl.
File metadata
- Download URL: rsloop-0.1.0-cp313-cp313-manylinux_2_39_x86_64.whl
- Upload date:
- Size: 853.2 kB
- Tags: CPython 3.13, manylinux: glibc 2.39+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
437618c03107d6e80a77f555755f4381e4faef0d45286786321c10b314e42bc4
|
|
| MD5 |
980330e6c0d80e8c78d11fe45c46d775
|
|
| BLAKE2b-256 |
35a92d8f956f8545f01047515894f32913e53a69403be64e207bdd25ca9010a3
|
File details
Details for the file rsloop-0.1.0-cp312-cp312-manylinux_2_39_x86_64.whl.
File metadata
- Download URL: rsloop-0.1.0-cp312-cp312-manylinux_2_39_x86_64.whl
- Upload date:
- Size: 853.7 kB
- Tags: CPython 3.12, manylinux: glibc 2.39+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a88efe264470a2e813af38413b75e55f7efb66beff42a95490c8bbe9d48ba929
|
|
| MD5 |
15cd761f8368b7815c8351d5d55ad6e6
|
|
| BLAKE2b-256 |
26c630601d3ffc7469d4010c640bf10cb62793f6fbcd807dc69f7ba3542f566a
|
File details
Details for the file rsloop-0.1.0-cp311-cp311-manylinux_2_39_x86_64.whl.
File metadata
- Download URL: rsloop-0.1.0-cp311-cp311-manylinux_2_39_x86_64.whl
- Upload date:
- Size: 845.3 kB
- Tags: CPython 3.11, manylinux: glibc 2.39+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
011d9551ed231c18b3ef0b5bd8dcf006305e1458d243d0db3eba4c2a2b11c7b7
|
|
| MD5 |
75fc1324c98f3e60e2d6910d40501461
|
|
| BLAKE2b-256 |
1c8e901c1e885b3fc7423816d90cb4dbfa6284d25d92c4a17b11366f1d56069a
|
File details
Details for the file rsloop-0.1.0-cp310-cp310-manylinux_2_39_x86_64.whl.
File metadata
- Download URL: rsloop-0.1.0-cp310-cp310-manylinux_2_39_x86_64.whl
- Upload date:
- Size: 846.1 kB
- Tags: CPython 3.10, manylinux: glibc 2.39+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
16e591bef265e7ec264032a56f03397d720d5eec5d64b1bd674ea28ceedc3836
|
|
| MD5 |
87a5ee272da822fb5efd262c0d5b1eb9
|
|
| BLAKE2b-256 |
defb07cda8c7749c054fc9880e4e1de7237ba1af7246d9ec5e9d4b889cd93d23
|
File details
Details for the file rsloop-0.1.0-cp39-cp39-manylinux_2_39_x86_64.whl.
File metadata
- Download URL: rsloop-0.1.0-cp39-cp39-manylinux_2_39_x86_64.whl
- Upload date:
- Size: 849.3 kB
- Tags: CPython 3.9, manylinux: glibc 2.39+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
651ca119e33f0f0c514596d47d41be7fb48f3dd3895c2edee9744407f63d280d
|
|
| MD5 |
f63a62cee7dbef60c612e0694d65ce71
|
|
| BLAKE2b-256 |
57d946dc17286a98509c2fa9a5e6d88ffe750b35a7dff05488e15cce1b8b276f
|
File details
Details for the file rsloop-0.1.0-cp38-cp38-manylinux_2_39_x86_64.whl.
File metadata
- Download URL: rsloop-0.1.0-cp38-cp38-manylinux_2_39_x86_64.whl
- Upload date:
- Size: 849.2 kB
- Tags: CPython 3.8, manylinux: glibc 2.39+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17b0aab3113ea8a1dad45f2aec37f1fff664eba5105931e56f29d9897eb5d948
|
|
| MD5 |
ca95432cdd4c90a27c116f9ae8afddb5
|
|
| BLAKE2b-256 |
270d63e9b3616f990f15a853a272f981bf9dddb4f7ca0839cfc3415d3690514e
|