Skip to main content

An event loop for asyncio written in Rust

Project description

rsloop logo

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(), and rsloop.run(...)
  • loop lifecycle:
    • run_forever
    • run_until_complete
    • stop
    • close
    • time
    • is_running
    • is_closed
    • debug flag getters/setters
  • callback scheduling:
    • call_soon
    • call_soon_threadsafe
    • call_later
    • call_at
    • returned Handle and TimerHandle objects
  • future/task helpers:
    • create_future
    • create_task
    • set_task_factory / get_task_factory
    • exception handler storage and dispatch
    • set_default_executor
    • run_in_executor
    • shutdown_asyncgens
    • shutdown_default_executor
  • callback execution inside captured contextvars.Context
  • asyncio.get_running_loop() support for coroutines driven by rsloop
  • asyncio.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_recv
    • sock_recv_into
    • sock_sendall
    • sock_accept
    • sock_connect
  • DNS awaitables:
    • getaddrinfo
    • getnameinfo
  • stream transports and servers:
    • create_server
    • create_connection
    • create_unix_server
    • create_unix_connection
    • connect_accepted_socket
    • returned Server and StreamTransport objects
    • transport.close() / transport.abort() fully close socket transports
  • pipe transports:
    • connect_read_pipe
    • connect_write_pipe
  • subprocess transports:
    • subprocess_exec
    • subprocess_shell
    • returned ProcessTransport and ProcessPipeTransport objects
    • 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, and restore_signals
  • Unix signal handlers via add_signal_handler / remove_signal_handler
  • a free-threaded module declaration via #[pymodule(gil_used = false)]
  • profiler entry points:
    • profile
    • profiler_running
    • start_profiler
    • stop_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
  • ssl is unset or None

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 stubbed
    • ssl=... is rejected for create_server, create_connection, create_unix_server, create_unix_connection, and connect_accepted_socket
  • stream transport flow control is still partial:
    • pause_reading() / resume_reading() work
    • get_write_buffer_size() returns 0
    • get_write_buffer_limits() returns (0, 0)
    • set_write_buffer_limits() is a no-op
  • subprocess support is intentionally incomplete:
    • preexec_fn is 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_server
    • create_unix_connection
    • add_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.py for running the same FastAPI app on stdlib asyncio, uvloop, or rsloop
  • benchmarks/compare_event_loops.py for 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rsloop-0.1.0.tar.gz (265.6 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

rsloop-0.1.0-cp314-cp314t-manylinux_2_39_x86_64.whl (842.6 kB view details)

Uploaded CPython 3.14tmanylinux: glibc 2.39+ x86-64

rsloop-0.1.0-cp314-cp314-manylinux_2_39_x86_64.whl (851.2 kB view details)

Uploaded CPython 3.14manylinux: glibc 2.39+ x86-64

rsloop-0.1.0-cp313-cp313-manylinux_2_39_x86_64.whl (853.2 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.39+ x86-64

rsloop-0.1.0-cp312-cp312-manylinux_2_39_x86_64.whl (853.7 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.39+ x86-64

rsloop-0.1.0-cp311-cp311-manylinux_2_39_x86_64.whl (845.3 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.39+ x86-64

rsloop-0.1.0-cp310-cp310-manylinux_2_39_x86_64.whl (846.1 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.39+ x86-64

rsloop-0.1.0-cp39-cp39-manylinux_2_39_x86_64.whl (849.3 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.39+ x86-64

rsloop-0.1.0-cp38-cp38-manylinux_2_39_x86_64.whl (849.2 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.39+ x86-64

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

Hashes for rsloop-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7a1a1fa5a1e764ad3eb3a123ca97019392ccccccf9386ab9062af9345de6811f
MD5 a1fac2e0b36f926e61fdde2c7ea87914
BLAKE2b-256 768115c4ee14518e4dea30ad916e16f4f2f7ec4cbbb2dbece5fd4baf63a8d2c9

See more details on using hashes here.

File details

Details for the file rsloop-0.1.0-cp314-cp314t-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for rsloop-0.1.0-cp314-cp314t-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 4d60eacfbd98a84131addda3d6f220cffb76505a03ae7269c8df796057e4b6c1
MD5 f58df43befd4edb761214d6dd5e62a12
BLAKE2b-256 000691b317a823c04301f8e8c5ea271027213d73015687daff9a21835cea7552

See more details on using hashes here.

File details

Details for the file rsloop-0.1.0-cp314-cp314-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for rsloop-0.1.0-cp314-cp314-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 c24819a74c0e544767b48ca4176d99e11f593a7b2bbedee2967fd8a3b5fa0655
MD5 091074e1ee13646f0daea7028f369edf
BLAKE2b-256 74c7c98360dfd05763e6aedab7100dd7434fc84e0df392784d2cda1c294dc7d8

See more details on using hashes here.

File details

Details for the file rsloop-0.1.0-cp313-cp313-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for rsloop-0.1.0-cp313-cp313-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 437618c03107d6e80a77f555755f4381e4faef0d45286786321c10b314e42bc4
MD5 980330e6c0d80e8c78d11fe45c46d775
BLAKE2b-256 35a92d8f956f8545f01047515894f32913e53a69403be64e207bdd25ca9010a3

See more details on using hashes here.

File details

Details for the file rsloop-0.1.0-cp312-cp312-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for rsloop-0.1.0-cp312-cp312-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 a88efe264470a2e813af38413b75e55f7efb66beff42a95490c8bbe9d48ba929
MD5 15cd761f8368b7815c8351d5d55ad6e6
BLAKE2b-256 26c630601d3ffc7469d4010c640bf10cb62793f6fbcd807dc69f7ba3542f566a

See more details on using hashes here.

File details

Details for the file rsloop-0.1.0-cp311-cp311-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for rsloop-0.1.0-cp311-cp311-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 011d9551ed231c18b3ef0b5bd8dcf006305e1458d243d0db3eba4c2a2b11c7b7
MD5 75fc1324c98f3e60e2d6910d40501461
BLAKE2b-256 1c8e901c1e885b3fc7423816d90cb4dbfa6284d25d92c4a17b11366f1d56069a

See more details on using hashes here.

File details

Details for the file rsloop-0.1.0-cp310-cp310-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for rsloop-0.1.0-cp310-cp310-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 16e591bef265e7ec264032a56f03397d720d5eec5d64b1bd674ea28ceedc3836
MD5 87a5ee272da822fb5efd262c0d5b1eb9
BLAKE2b-256 defb07cda8c7749c054fc9880e4e1de7237ba1af7246d9ec5e9d4b889cd93d23

See more details on using hashes here.

File details

Details for the file rsloop-0.1.0-cp39-cp39-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for rsloop-0.1.0-cp39-cp39-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 651ca119e33f0f0c514596d47d41be7fb48f3dd3895c2edee9744407f63d280d
MD5 f63a62cee7dbef60c612e0694d65ce71
BLAKE2b-256 57d946dc17286a98509c2fa9a5e6d88ffe750b35a7dff05488e15cce1b8b276f

See more details on using hashes here.

File details

Details for the file rsloop-0.1.0-cp38-cp38-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for rsloop-0.1.0-cp38-cp38-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 17b0aab3113ea8a1dad45f2aec37f1fff664eba5105931e56f29d9897eb5d948
MD5 ca95432cdd4c90a27c116f9ae8afddb5
BLAKE2b-256 270d63e9b3616f990f15a853a272f981bf9dddb4f7ca0839cfc3415d3690514e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page