rsloop 0.1.2__tar.gz → 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rsloop-0.1.2 → rsloop-0.1.3}/.github/workflows/wheels.yml +10 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/.gitignore +2 -1
- {rsloop-0.1.2 → rsloop-0.1.3}/Cargo.lock +4 -3
- {rsloop-0.1.2 → rsloop-0.1.3}/Cargo.toml +5 -2
- rsloop-0.1.3/PKG-INFO +303 -0
- rsloop-0.1.3/README.md +270 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/benchmarks/compare_event_loops.py +32 -4
- {rsloop-0.1.2 → rsloop-0.1.3}/demo/fastapi_service.py +28 -4
- {rsloop-0.1.2 → rsloop-0.1.3}/examples/04_unix_and_accepted_socket.py +4 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/examples/05_pipes_signals_subprocesses.py +38 -8
- {rsloop-0.1.2 → rsloop-0.1.3}/pyproject.toml +1 -1
- rsloop-0.1.3/scripts/build-wheels.sh +232 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/src/callbacks.rs +3 -2
- rsloop-0.1.3/src/fd_ops.rs +231 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/src/loop_core.rs +7 -6
- {rsloop-0.1.2 → rsloop-0.1.3}/src/process_transport.rs +43 -6
- {rsloop-0.1.2 → rsloop-0.1.3}/src/python_api.rs +176 -76
- {rsloop-0.1.2 → rsloop-0.1.3}/src/runtime.rs +15 -2
- {rsloop-0.1.2 → rsloop-0.1.3}/src/stream_transport.rs +341 -97
- rsloop-0.1.3/tests/test_run.py +165 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/uv.lock +1 -1
- rsloop-0.1.2/PKG-INFO +0 -299
- rsloop-0.1.2/README.md +0 -266
- rsloop-0.1.2/scripts/build-wheels.sh +0 -128
- rsloop-0.1.2/src/fd_ops.rs +0 -109
- rsloop-0.1.2/tests/test_run.py +0 -25
- {rsloop-0.1.2 → rsloop-0.1.3}/LICENSE +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/benchmarks/README.md +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/build.rs +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/demo/README.md +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/examples/01_basics.py +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/examples/02_fd_and_sockets.py +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/examples/03_streams.py +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/python/rsloop/__init__.py +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/rsloop.png +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/src/async_event.rs +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/src/blocking.rs +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/src/context.rs +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/src/errors.rs +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/src/fast_streams.rs +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/src/lib.rs +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/src/profiler.rs +0 -0
- {rsloop-0.1.2 → rsloop-0.1.3}/src/python_names.rs +0 -0
|
@@ -20,6 +20,13 @@ jobs:
|
|
|
20
20
|
- os_name: linux
|
|
21
21
|
runner: ubuntu-latest
|
|
22
22
|
rust_target: x86_64-unknown-linux-gnu
|
|
23
|
+
- os_name: windows-amd64
|
|
24
|
+
runner: windows-latest
|
|
25
|
+
rust_target: x86_64-pc-windows-msvc
|
|
26
|
+
- os_name: windows-arm64
|
|
27
|
+
runner: windows-11-arm
|
|
28
|
+
rust_target: aarch64-pc-windows-msvc
|
|
29
|
+
python_versions: "3.11 3.12 3.13 3.14 3.14t"
|
|
23
30
|
- os_name: macos-intel
|
|
24
31
|
runner: macos-15-intel
|
|
25
32
|
rust_target: x86_64-apple-darwin
|
|
@@ -50,6 +57,9 @@ jobs:
|
|
|
50
57
|
uses: Swatinem/rust-cache@v2
|
|
51
58
|
|
|
52
59
|
- name: Build wheels
|
|
60
|
+
shell: bash
|
|
61
|
+
env:
|
|
62
|
+
RSLOOP_PYTHON_VERSIONS: ${{ matrix.python_versions }}
|
|
53
63
|
run: scripts/build-wheels.sh --out dist --target "${{ matrix.rust_target }}"
|
|
54
64
|
|
|
55
65
|
- name: Upload wheel artifacts
|
|
@@ -943,7 +943,7 @@ dependencies = [
|
|
|
943
943
|
|
|
944
944
|
[[package]]
|
|
945
945
|
name = "rsloop"
|
|
946
|
-
version = "0.1.
|
|
946
|
+
version = "0.1.3"
|
|
947
947
|
dependencies = [
|
|
948
948
|
"crossbeam-channel",
|
|
949
949
|
"futures",
|
|
@@ -954,6 +954,7 @@ dependencies = [
|
|
|
954
954
|
"pyo3-build-config",
|
|
955
955
|
"signal-hook",
|
|
956
956
|
"socket2",
|
|
957
|
+
"windows-sys",
|
|
957
958
|
]
|
|
958
959
|
|
|
959
960
|
[[package]]
|
|
@@ -1043,9 +1044,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
|
1043
1044
|
|
|
1044
1045
|
[[package]]
|
|
1045
1046
|
name = "signal-hook"
|
|
1046
|
-
version = "0.3
|
|
1047
|
+
version = "0.4.3"
|
|
1047
1048
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1048
|
-
checksum = "
|
|
1049
|
+
checksum = "3b57709da74f9ff9f4a27dce9526eec25ca8407c45a7887243b031a58935fb8e"
|
|
1049
1050
|
dependencies = [
|
|
1050
1051
|
"libc",
|
|
1051
1052
|
"signal-hook-registry",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rsloop"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.3"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "An event loop for asyncio written in Rust"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -24,9 +24,12 @@ libc = "0.2"
|
|
|
24
24
|
pprof = { version = "0.15.0", default-features = false, features = ["flamegraph"], optional = true }
|
|
25
25
|
pyo3 = { version = "0.28", features = ["extension-module"] }
|
|
26
26
|
pyo3-async-runtimes = { version = "0.28", features = ["async-std-runtime"] }
|
|
27
|
-
signal-hook = "0.
|
|
27
|
+
signal-hook = "0.4"
|
|
28
28
|
socket2 = "0.6"
|
|
29
29
|
|
|
30
|
+
[target.'cfg(windows)'.dependencies]
|
|
31
|
+
windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_Networking_WinSock", "Win32_System_Pipes", "Win32_System_Threading"] }
|
|
32
|
+
|
|
30
33
|
[build-dependencies]
|
|
31
34
|
pyo3-build-config = "0.28"
|
|
32
35
|
|
rsloop-0.1.3/PKG-INFO
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rsloop
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
7
|
+
Classifier: Operating System :: MacOS
|
|
8
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
9
|
+
Classifier: Programming Language :: Python
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Rust
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Classifier: Topic :: System :: Networking
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Summary: An event loop for asyncio written in Rust
|
|
24
|
+
Keywords: asyncio,event-loop,pyo3,python,rust
|
|
25
|
+
Home-Page: https://github.com/RustedBytes/rsloop
|
|
26
|
+
Author: RustedBytes
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
29
|
+
Project-URL: Homepage, https://github.com/RustedBytes/rsloop
|
|
30
|
+
Project-URL: Issues, https://github.com/RustedBytes/rsloop/issues
|
|
31
|
+
Project-URL: Repository, https://github.com/RustedBytes/rsloop
|
|
32
|
+
|
|
33
|
+
<img src="./rsloop.png" alt="rsloop logo" align="center">
|
|
34
|
+
|
|
35
|
+
# An event loop for asyncio written in Rust
|
|
36
|
+
|
|
37
|
+
[](https://pypi.org/project/rsloop/)
|
|
38
|
+
[](https://pepy.tech/projects/rsloop)
|
|
39
|
+
|
|
40
|
+
`rsloop` is a PyO3-based `asyncio` event loop implemented in Rust.
|
|
41
|
+
|
|
42
|
+
Each `rsloop.Loop` owns a dedicated Rust runtime thread for loop coordination
|
|
43
|
+
and I/O work. Python callbacks, tasks, and coroutines still run on the thread
|
|
44
|
+
that calls `run_forever()` or `run_until_complete()` (usually the main Python
|
|
45
|
+
thread).
|
|
46
|
+
|
|
47
|
+
The package exposes:
|
|
48
|
+
|
|
49
|
+
- a native extension module at `rsloop._loop`
|
|
50
|
+
- a Python wrapper in `python/rsloop/__init__.py`
|
|
51
|
+
- `rsloop.Loop`, `rsloop.new_event_loop()`, and `rsloop.run(...)`
|
|
52
|
+
|
|
53
|
+
Repository metadata currently targets Python `>=3.8`. The packaged project now
|
|
54
|
+
supports the core event-loop surface on Linux, macOS, and Windows, including
|
|
55
|
+
Windows pipe transports and subprocess workflows.
|
|
56
|
+
|
|
57
|
+
## Install
|
|
58
|
+
|
|
59
|
+
From PyPI:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install rsloop
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
With `uv`:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uv add rsloop
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
Simple entry point:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import rsloop
|
|
77
|
+
|
|
78
|
+
async def main():
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
rsloop.run(main())
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Manual loop creation also works:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
import asyncio
|
|
88
|
+
import rsloop
|
|
89
|
+
|
|
90
|
+
loop = rsloop.new_event_loop()
|
|
91
|
+
asyncio.set_event_loop(loop)
|
|
92
|
+
try:
|
|
93
|
+
loop.run_until_complete(...)
|
|
94
|
+
finally:
|
|
95
|
+
asyncio.set_event_loop(None)
|
|
96
|
+
loop.close()
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Importing `rsloop` also patches `asyncio.set_event_loop()` so Python 3.8 can
|
|
100
|
+
accept an `rsloop.Loop` instance, matching the behavior exercised by
|
|
101
|
+
`tests/test_run.py`.
|
|
102
|
+
|
|
103
|
+
## Verified Surface Area
|
|
104
|
+
|
|
105
|
+
The current codebase implements these user-facing areas.
|
|
106
|
+
|
|
107
|
+
Loop lifecycle and scheduling:
|
|
108
|
+
|
|
109
|
+
- `run_forever`, `run_until_complete`, `stop`, `close`
|
|
110
|
+
- `time`, `is_running`, `is_closed`
|
|
111
|
+
- `get_debug`, `set_debug`
|
|
112
|
+
- `call_soon`, `call_soon_threadsafe`, `call_later`, `call_at`
|
|
113
|
+
- returned `Handle` and `TimerHandle` objects with `cancel()` / `cancelled()`
|
|
114
|
+
|
|
115
|
+
Tasks, futures, and execution helpers:
|
|
116
|
+
|
|
117
|
+
- `create_future`, `create_task`
|
|
118
|
+
- `set_task_factory`, `get_task_factory`
|
|
119
|
+
- `set_exception_handler`, `get_exception_handler`,
|
|
120
|
+
`call_exception_handler`, `default_exception_handler`
|
|
121
|
+
- `set_default_executor`, `run_in_executor`
|
|
122
|
+
- `shutdown_asyncgens`, `shutdown_default_executor`
|
|
123
|
+
- callback execution under captured `contextvars.Context`
|
|
124
|
+
- `asyncio.get_running_loop()` support while running on `rsloop`
|
|
125
|
+
- `rsloop.run(...)` helper, with `asyncio.run(..., loop_factory=...)`
|
|
126
|
+
integration on Python 3.12+
|
|
127
|
+
|
|
128
|
+
I/O and networking:
|
|
129
|
+
|
|
130
|
+
- `add_reader`, `remove_reader`, `add_writer`, `remove_writer`
|
|
131
|
+
- `sock_recv`, `sock_recv_into`, `sock_sendall`, `sock_accept`, `sock_connect`
|
|
132
|
+
- `getaddrinfo`, `getnameinfo`
|
|
133
|
+
- `create_server`, `create_connection`
|
|
134
|
+
- `create_unix_server`, `create_unix_connection`
|
|
135
|
+
- `connect_accepted_socket`
|
|
136
|
+
- returned `Server` objects with `close()`, `is_serving()`, `get_loop()`,
|
|
137
|
+
and `sockets()`
|
|
138
|
+
- returned `StreamTransport` objects with `write()`, `writelines()`, `close()`,
|
|
139
|
+
`abort()`, `is_closing()`, `write_eof()`, `can_write_eof()`,
|
|
140
|
+
`get_extra_info()`, `get_protocol()`, `set_protocol()`,
|
|
141
|
+
`pause_reading()`, `resume_reading()`, `is_reading()`
|
|
142
|
+
|
|
143
|
+
Pipes, subprocesses, and signals:
|
|
144
|
+
|
|
145
|
+
- `connect_read_pipe`, `connect_write_pipe`
|
|
146
|
+
- `subprocess_exec`, `subprocess_shell`
|
|
147
|
+
- returned `ProcessTransport` and `ProcessPipeTransport` objects
|
|
148
|
+
- higher-level compatibility with `asyncio.create_subprocess_exec()` and
|
|
149
|
+
`asyncio.create_subprocess_shell()`
|
|
150
|
+
- Unix subprocess options including `cwd`, `env`, `executable`, `pass_fds`,
|
|
151
|
+
`start_new_session`, `process_group`, `user`, `group`, `extra_groups`,
|
|
152
|
+
`umask`, and `restore_signals`
|
|
153
|
+
- `add_signal_handler`, `remove_signal_handler`
|
|
154
|
+
|
|
155
|
+
Profiling:
|
|
156
|
+
|
|
157
|
+
- `profile(...)`, `profiler_running()`, `start_profiler()`, `stop_profiler()`
|
|
158
|
+
|
|
159
|
+
## Fast Streams
|
|
160
|
+
|
|
161
|
+
Importing `rsloop` patches `asyncio.open_connection()` and
|
|
162
|
+
`asyncio.start_server()` by default.
|
|
163
|
+
|
|
164
|
+
That import-time behavior is controlled by `RSLOOP_USE_FAST_STREAMS` and can be
|
|
165
|
+
disabled with:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
export RSLOOP_USE_FAST_STREAMS=0
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The native fast-stream path is used only when:
|
|
172
|
+
|
|
173
|
+
- the running loop is an `rsloop.Loop`
|
|
174
|
+
- `ssl` is unset or `None`
|
|
175
|
+
|
|
176
|
+
Otherwise `rsloop` falls back to the stdlib `asyncio.streams` helpers.
|
|
177
|
+
|
|
178
|
+
The implementation lives in `src/fast_streams.rs` and is backed by the lower
|
|
179
|
+
level transport code in `src/stream_transport.rs`.
|
|
180
|
+
|
|
181
|
+
## Current Limitations
|
|
182
|
+
|
|
183
|
+
These gaps are visible in the current implementation.
|
|
184
|
+
|
|
185
|
+
- TLS is not implemented.
|
|
186
|
+
- `start_tls()` is stubbed.
|
|
187
|
+
- `ssl=...` is rejected for `create_server`, `create_connection`,
|
|
188
|
+
`create_unix_server`, `create_unix_connection`, and
|
|
189
|
+
`connect_accepted_socket`.
|
|
190
|
+
- Stream transport flow control is partial:
|
|
191
|
+
`pause_reading()` and `resume_reading()` work, but
|
|
192
|
+
`get_write_buffer_size()` returns `0`,
|
|
193
|
+
`get_write_buffer_limits()` returns `(0, 0)`, and
|
|
194
|
+
`set_write_buffer_limits()` is a no-op.
|
|
195
|
+
- Several compatibility parameters are currently accepted only to preserve API
|
|
196
|
+
shape, not to provide full behavior:
|
|
197
|
+
`server_hostname`, `happy_eyeballs_delay`, `interleave`, `all_errors`,
|
|
198
|
+
`ssl_handshake_timeout`, `ssl_shutdown_timeout`, and
|
|
199
|
+
`shutdown_default_executor(timeout=...)`.
|
|
200
|
+
- Subprocess support is intentionally incomplete:
|
|
201
|
+
`preexec_fn` is unsupported, and text mode is rejected for
|
|
202
|
+
`asyncio.create_subprocess_exec()` /
|
|
203
|
+
`asyncio.create_subprocess_shell()` when using the stdlib subprocess stream
|
|
204
|
+
protocol.
|
|
205
|
+
- Low-level subprocess transport APIs do support text decoding when used with a
|
|
206
|
+
custom subprocess protocol.
|
|
207
|
+
- Unix-specific APIs remain Unix-specific:
|
|
208
|
+
`create_unix_server`, `create_unix_connection`,
|
|
209
|
+
`add_signal_handler`, `remove_signal_handler`.
|
|
210
|
+
- Platform-specific limitations still apply:
|
|
211
|
+
Unix socket APIs and Unix signal handlers remain Unix-only, and several
|
|
212
|
+
subprocess options such as `pass_fds`, `user`, `group`, and `umask` are
|
|
213
|
+
still specific to Unix process spawning.
|
|
214
|
+
|
|
215
|
+
## Build
|
|
216
|
+
|
|
217
|
+
Quick check:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
cargo check
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Release build and editable install:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
cargo build --release
|
|
227
|
+
uv run --with maturin maturin develop --release
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Build release wheels into `dist/wheels`:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
scripts/build-wheels.sh
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
That script currently defaults to CPython `3.8 3.9 3.10 3.11 3.12 3.13 3.14`
|
|
237
|
+
plus free-threaded `3.14t`, and uses `uv python install` / `uv python find` to
|
|
238
|
+
locate interpreters.
|
|
239
|
+
|
|
240
|
+
## Profiling
|
|
241
|
+
|
|
242
|
+
Profiling is behind the Cargo feature `profiler` and is disabled by default.
|
|
243
|
+
Build or install with that feature first:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
cargo build --release --features profiler
|
|
247
|
+
uv run --with maturin maturin develop --release --features profiler
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Then wrap the code you want to inspect:
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
import rsloop
|
|
254
|
+
|
|
255
|
+
with rsloop.profile("rsloop-flamegraph.svg", frequency=999):
|
|
256
|
+
rsloop.run(main())
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Or manage the session manually:
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
import rsloop
|
|
263
|
+
|
|
264
|
+
rsloop.start_profiler(frequency=999)
|
|
265
|
+
try:
|
|
266
|
+
rsloop.run(main())
|
|
267
|
+
finally:
|
|
268
|
+
rsloop.stop_profiler("rsloop-flamegraph.svg")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Only `format="flamegraph"` is currently implemented. If the extension was built
|
|
272
|
+
without `--features profiler`, `profile()` and `start_profiler()` raise a
|
|
273
|
+
runtime error.
|
|
274
|
+
|
|
275
|
+
## Examples
|
|
276
|
+
|
|
277
|
+
Run the repository examples from the project root:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
uv run python examples/01_basics.py
|
|
281
|
+
uv run python examples/02_fd_and_sockets.py
|
|
282
|
+
uv run python examples/03_streams.py
|
|
283
|
+
uv run python examples/04_unix_and_accepted_socket.py
|
|
284
|
+
uv run python examples/05_pipes_signals_subprocesses.py
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
The repository also includes:
|
|
288
|
+
|
|
289
|
+
- `demo/fastapi_service.py` for running the same FastAPI app on stdlib
|
|
290
|
+
`asyncio`, `uvloop`, or `rsloop`
|
|
291
|
+
- `benchmarks/compare_event_loops.py` for callback, task, and TCP stream
|
|
292
|
+
comparisons
|
|
293
|
+
|
|
294
|
+
## Benchmark
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
uv run --with maturin maturin develop --release
|
|
298
|
+
uv run --with uvloop python benchmarks/compare_event_loops.py
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
See `benchmarks/README.md` for workload details and extra flags, and
|
|
302
|
+
`demo/README.md` for the FastAPI loop comparison demo.
|
|
303
|
+
|
rsloop-0.1.3/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
<img src="./rsloop.png" alt="rsloop logo" align="center">
|
|
2
|
+
|
|
3
|
+
# An event loop for asyncio written in Rust
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/rsloop/)
|
|
6
|
+
[](https://pepy.tech/projects/rsloop)
|
|
7
|
+
|
|
8
|
+
`rsloop` is a PyO3-based `asyncio` event loop implemented in Rust.
|
|
9
|
+
|
|
10
|
+
Each `rsloop.Loop` owns a dedicated Rust runtime thread for loop coordination
|
|
11
|
+
and I/O work. Python callbacks, tasks, and coroutines still run on the thread
|
|
12
|
+
that calls `run_forever()` or `run_until_complete()` (usually the main Python
|
|
13
|
+
thread).
|
|
14
|
+
|
|
15
|
+
The package exposes:
|
|
16
|
+
|
|
17
|
+
- a native extension module at `rsloop._loop`
|
|
18
|
+
- a Python wrapper in `python/rsloop/__init__.py`
|
|
19
|
+
- `rsloop.Loop`, `rsloop.new_event_loop()`, and `rsloop.run(...)`
|
|
20
|
+
|
|
21
|
+
Repository metadata currently targets Python `>=3.8`. The packaged project now
|
|
22
|
+
supports the core event-loop surface on Linux, macOS, and Windows, including
|
|
23
|
+
Windows pipe transports and subprocess workflows.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
From PyPI:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install rsloop
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
With `uv`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv add rsloop
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
Simple entry point:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import rsloop
|
|
45
|
+
|
|
46
|
+
async def main():
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
rsloop.run(main())
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Manual loop creation also works:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import asyncio
|
|
56
|
+
import rsloop
|
|
57
|
+
|
|
58
|
+
loop = rsloop.new_event_loop()
|
|
59
|
+
asyncio.set_event_loop(loop)
|
|
60
|
+
try:
|
|
61
|
+
loop.run_until_complete(...)
|
|
62
|
+
finally:
|
|
63
|
+
asyncio.set_event_loop(None)
|
|
64
|
+
loop.close()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Importing `rsloop` also patches `asyncio.set_event_loop()` so Python 3.8 can
|
|
68
|
+
accept an `rsloop.Loop` instance, matching the behavior exercised by
|
|
69
|
+
`tests/test_run.py`.
|
|
70
|
+
|
|
71
|
+
## Verified Surface Area
|
|
72
|
+
|
|
73
|
+
The current codebase implements these user-facing areas.
|
|
74
|
+
|
|
75
|
+
Loop lifecycle and scheduling:
|
|
76
|
+
|
|
77
|
+
- `run_forever`, `run_until_complete`, `stop`, `close`
|
|
78
|
+
- `time`, `is_running`, `is_closed`
|
|
79
|
+
- `get_debug`, `set_debug`
|
|
80
|
+
- `call_soon`, `call_soon_threadsafe`, `call_later`, `call_at`
|
|
81
|
+
- returned `Handle` and `TimerHandle` objects with `cancel()` / `cancelled()`
|
|
82
|
+
|
|
83
|
+
Tasks, futures, and execution helpers:
|
|
84
|
+
|
|
85
|
+
- `create_future`, `create_task`
|
|
86
|
+
- `set_task_factory`, `get_task_factory`
|
|
87
|
+
- `set_exception_handler`, `get_exception_handler`,
|
|
88
|
+
`call_exception_handler`, `default_exception_handler`
|
|
89
|
+
- `set_default_executor`, `run_in_executor`
|
|
90
|
+
- `shutdown_asyncgens`, `shutdown_default_executor`
|
|
91
|
+
- callback execution under captured `contextvars.Context`
|
|
92
|
+
- `asyncio.get_running_loop()` support while running on `rsloop`
|
|
93
|
+
- `rsloop.run(...)` helper, with `asyncio.run(..., loop_factory=...)`
|
|
94
|
+
integration on Python 3.12+
|
|
95
|
+
|
|
96
|
+
I/O and networking:
|
|
97
|
+
|
|
98
|
+
- `add_reader`, `remove_reader`, `add_writer`, `remove_writer`
|
|
99
|
+
- `sock_recv`, `sock_recv_into`, `sock_sendall`, `sock_accept`, `sock_connect`
|
|
100
|
+
- `getaddrinfo`, `getnameinfo`
|
|
101
|
+
- `create_server`, `create_connection`
|
|
102
|
+
- `create_unix_server`, `create_unix_connection`
|
|
103
|
+
- `connect_accepted_socket`
|
|
104
|
+
- returned `Server` objects with `close()`, `is_serving()`, `get_loop()`,
|
|
105
|
+
and `sockets()`
|
|
106
|
+
- returned `StreamTransport` objects with `write()`, `writelines()`, `close()`,
|
|
107
|
+
`abort()`, `is_closing()`, `write_eof()`, `can_write_eof()`,
|
|
108
|
+
`get_extra_info()`, `get_protocol()`, `set_protocol()`,
|
|
109
|
+
`pause_reading()`, `resume_reading()`, `is_reading()`
|
|
110
|
+
|
|
111
|
+
Pipes, subprocesses, and signals:
|
|
112
|
+
|
|
113
|
+
- `connect_read_pipe`, `connect_write_pipe`
|
|
114
|
+
- `subprocess_exec`, `subprocess_shell`
|
|
115
|
+
- returned `ProcessTransport` and `ProcessPipeTransport` objects
|
|
116
|
+
- higher-level compatibility with `asyncio.create_subprocess_exec()` and
|
|
117
|
+
`asyncio.create_subprocess_shell()`
|
|
118
|
+
- Unix subprocess options including `cwd`, `env`, `executable`, `pass_fds`,
|
|
119
|
+
`start_new_session`, `process_group`, `user`, `group`, `extra_groups`,
|
|
120
|
+
`umask`, and `restore_signals`
|
|
121
|
+
- `add_signal_handler`, `remove_signal_handler`
|
|
122
|
+
|
|
123
|
+
Profiling:
|
|
124
|
+
|
|
125
|
+
- `profile(...)`, `profiler_running()`, `start_profiler()`, `stop_profiler()`
|
|
126
|
+
|
|
127
|
+
## Fast Streams
|
|
128
|
+
|
|
129
|
+
Importing `rsloop` patches `asyncio.open_connection()` and
|
|
130
|
+
`asyncio.start_server()` by default.
|
|
131
|
+
|
|
132
|
+
That import-time behavior is controlled by `RSLOOP_USE_FAST_STREAMS` and can be
|
|
133
|
+
disabled with:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
export RSLOOP_USE_FAST_STREAMS=0
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The native fast-stream path is used only when:
|
|
140
|
+
|
|
141
|
+
- the running loop is an `rsloop.Loop`
|
|
142
|
+
- `ssl` is unset or `None`
|
|
143
|
+
|
|
144
|
+
Otherwise `rsloop` falls back to the stdlib `asyncio.streams` helpers.
|
|
145
|
+
|
|
146
|
+
The implementation lives in `src/fast_streams.rs` and is backed by the lower
|
|
147
|
+
level transport code in `src/stream_transport.rs`.
|
|
148
|
+
|
|
149
|
+
## Current Limitations
|
|
150
|
+
|
|
151
|
+
These gaps are visible in the current implementation.
|
|
152
|
+
|
|
153
|
+
- TLS is not implemented.
|
|
154
|
+
- `start_tls()` is stubbed.
|
|
155
|
+
- `ssl=...` is rejected for `create_server`, `create_connection`,
|
|
156
|
+
`create_unix_server`, `create_unix_connection`, and
|
|
157
|
+
`connect_accepted_socket`.
|
|
158
|
+
- Stream transport flow control is partial:
|
|
159
|
+
`pause_reading()` and `resume_reading()` work, but
|
|
160
|
+
`get_write_buffer_size()` returns `0`,
|
|
161
|
+
`get_write_buffer_limits()` returns `(0, 0)`, and
|
|
162
|
+
`set_write_buffer_limits()` is a no-op.
|
|
163
|
+
- Several compatibility parameters are currently accepted only to preserve API
|
|
164
|
+
shape, not to provide full behavior:
|
|
165
|
+
`server_hostname`, `happy_eyeballs_delay`, `interleave`, `all_errors`,
|
|
166
|
+
`ssl_handshake_timeout`, `ssl_shutdown_timeout`, and
|
|
167
|
+
`shutdown_default_executor(timeout=...)`.
|
|
168
|
+
- Subprocess support is intentionally incomplete:
|
|
169
|
+
`preexec_fn` is unsupported, and text mode is rejected for
|
|
170
|
+
`asyncio.create_subprocess_exec()` /
|
|
171
|
+
`asyncio.create_subprocess_shell()` when using the stdlib subprocess stream
|
|
172
|
+
protocol.
|
|
173
|
+
- Low-level subprocess transport APIs do support text decoding when used with a
|
|
174
|
+
custom subprocess protocol.
|
|
175
|
+
- Unix-specific APIs remain Unix-specific:
|
|
176
|
+
`create_unix_server`, `create_unix_connection`,
|
|
177
|
+
`add_signal_handler`, `remove_signal_handler`.
|
|
178
|
+
- Platform-specific limitations still apply:
|
|
179
|
+
Unix socket APIs and Unix signal handlers remain Unix-only, and several
|
|
180
|
+
subprocess options such as `pass_fds`, `user`, `group`, and `umask` are
|
|
181
|
+
still specific to Unix process spawning.
|
|
182
|
+
|
|
183
|
+
## Build
|
|
184
|
+
|
|
185
|
+
Quick check:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
cargo check
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Release build and editable install:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
cargo build --release
|
|
195
|
+
uv run --with maturin maturin develop --release
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Build release wheels into `dist/wheels`:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
scripts/build-wheels.sh
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
That script currently defaults to CPython `3.8 3.9 3.10 3.11 3.12 3.13 3.14`
|
|
205
|
+
plus free-threaded `3.14t`, and uses `uv python install` / `uv python find` to
|
|
206
|
+
locate interpreters.
|
|
207
|
+
|
|
208
|
+
## Profiling
|
|
209
|
+
|
|
210
|
+
Profiling is behind the Cargo feature `profiler` and is disabled by default.
|
|
211
|
+
Build or install with that feature first:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
cargo build --release --features profiler
|
|
215
|
+
uv run --with maturin maturin develop --release --features profiler
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Then wrap the code you want to inspect:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
import rsloop
|
|
222
|
+
|
|
223
|
+
with rsloop.profile("rsloop-flamegraph.svg", frequency=999):
|
|
224
|
+
rsloop.run(main())
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Or manage the session manually:
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
import rsloop
|
|
231
|
+
|
|
232
|
+
rsloop.start_profiler(frequency=999)
|
|
233
|
+
try:
|
|
234
|
+
rsloop.run(main())
|
|
235
|
+
finally:
|
|
236
|
+
rsloop.stop_profiler("rsloop-flamegraph.svg")
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Only `format="flamegraph"` is currently implemented. If the extension was built
|
|
240
|
+
without `--features profiler`, `profile()` and `start_profiler()` raise a
|
|
241
|
+
runtime error.
|
|
242
|
+
|
|
243
|
+
## Examples
|
|
244
|
+
|
|
245
|
+
Run the repository examples from the project root:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
uv run python examples/01_basics.py
|
|
249
|
+
uv run python examples/02_fd_and_sockets.py
|
|
250
|
+
uv run python examples/03_streams.py
|
|
251
|
+
uv run python examples/04_unix_and_accepted_socket.py
|
|
252
|
+
uv run python examples/05_pipes_signals_subprocesses.py
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The repository also includes:
|
|
256
|
+
|
|
257
|
+
- `demo/fastapi_service.py` for running the same FastAPI app on stdlib
|
|
258
|
+
`asyncio`, `uvloop`, or `rsloop`
|
|
259
|
+
- `benchmarks/compare_event_loops.py` for callback, task, and TCP stream
|
|
260
|
+
comparisons
|
|
261
|
+
|
|
262
|
+
## Benchmark
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
uv run --with maturin maturin develop --release
|
|
266
|
+
uv run --with uvloop python benchmarks/compare_event_loops.py
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
See `benchmarks/README.md` for workload details and extra flags, and
|
|
270
|
+
`demo/README.md` for the FastAPI loop comparison demo.
|