rsloop 0.1.0__tar.gz → 0.1.2__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.
Files changed (38) hide show
  1. {rsloop-0.1.0 → rsloop-0.1.2}/.github/workflows/wheels.yml +7 -2
  2. {rsloop-0.1.0 → rsloop-0.1.2}/Cargo.lock +1 -1
  3. {rsloop-0.1.0 → rsloop-0.1.2}/Cargo.toml +1 -1
  4. {rsloop-0.1.0 → rsloop-0.1.2}/LICENSE +1 -1
  5. {rsloop-0.1.0 → rsloop-0.1.2}/PKG-INFO +36 -22
  6. {rsloop-0.1.0 → rsloop-0.1.2}/README.md +35 -21
  7. {rsloop-0.1.0 → rsloop-0.1.2}/examples/03_streams.py +2 -0
  8. {rsloop-0.1.0 → rsloop-0.1.2}/examples/04_unix_and_accepted_socket.py +2 -0
  9. {rsloop-0.1.0 → rsloop-0.1.2}/examples/05_pipes_signals_subprocesses.py +2 -0
  10. {rsloop-0.1.0 → rsloop-0.1.2}/pyproject.toml +1 -1
  11. {rsloop-0.1.0 → rsloop-0.1.2}/python/rsloop/__init__.py +25 -0
  12. {rsloop-0.1.0 → rsloop-0.1.2}/scripts/build-wheels.sh +32 -10
  13. {rsloop-0.1.0 → rsloop-0.1.2}/src/callbacks.rs +0 -17
  14. {rsloop-0.1.0 → rsloop-0.1.2}/src/python_api.rs +2 -15
  15. rsloop-0.1.2/tests/test_run.py +25 -0
  16. {rsloop-0.1.0 → rsloop-0.1.2}/uv.lock +1 -1
  17. {rsloop-0.1.0 → rsloop-0.1.2}/.gitignore +0 -0
  18. {rsloop-0.1.0 → rsloop-0.1.2}/benchmarks/README.md +0 -0
  19. {rsloop-0.1.0 → rsloop-0.1.2}/benchmarks/compare_event_loops.py +0 -0
  20. {rsloop-0.1.0 → rsloop-0.1.2}/build.rs +0 -0
  21. {rsloop-0.1.0 → rsloop-0.1.2}/demo/README.md +0 -0
  22. {rsloop-0.1.0 → rsloop-0.1.2}/demo/fastapi_service.py +0 -0
  23. {rsloop-0.1.0 → rsloop-0.1.2}/examples/01_basics.py +0 -0
  24. {rsloop-0.1.0 → rsloop-0.1.2}/examples/02_fd_and_sockets.py +0 -0
  25. {rsloop-0.1.0 → rsloop-0.1.2}/rsloop.png +0 -0
  26. {rsloop-0.1.0 → rsloop-0.1.2}/src/async_event.rs +0 -0
  27. {rsloop-0.1.0 → rsloop-0.1.2}/src/blocking.rs +0 -0
  28. {rsloop-0.1.0 → rsloop-0.1.2}/src/context.rs +0 -0
  29. {rsloop-0.1.0 → rsloop-0.1.2}/src/errors.rs +0 -0
  30. {rsloop-0.1.0 → rsloop-0.1.2}/src/fast_streams.rs +0 -0
  31. {rsloop-0.1.0 → rsloop-0.1.2}/src/fd_ops.rs +0 -0
  32. {rsloop-0.1.0 → rsloop-0.1.2}/src/lib.rs +0 -0
  33. {rsloop-0.1.0 → rsloop-0.1.2}/src/loop_core.rs +0 -0
  34. {rsloop-0.1.0 → rsloop-0.1.2}/src/process_transport.rs +0 -0
  35. {rsloop-0.1.0 → rsloop-0.1.2}/src/profiler.rs +0 -0
  36. {rsloop-0.1.0 → rsloop-0.1.2}/src/python_names.rs +0 -0
  37. {rsloop-0.1.0 → rsloop-0.1.2}/src/runtime.rs +0 -0
  38. {rsloop-0.1.0 → rsloop-0.1.2}/src/stream_transport.rs +0 -0
@@ -19,8 +19,13 @@ jobs:
19
19
  include:
20
20
  - os_name: linux
21
21
  runner: ubuntu-latest
22
- - os_name: macos
22
+ rust_target: x86_64-unknown-linux-gnu
23
+ - os_name: macos-intel
23
24
  runner: macos-15-intel
25
+ rust_target: x86_64-apple-darwin
26
+ - os_name: macos-arm64
27
+ runner: macos-15
28
+ rust_target: aarch64-apple-darwin
24
29
 
25
30
  steps:
26
31
  - name: Check out repository
@@ -45,7 +50,7 @@ jobs:
45
50
  uses: Swatinem/rust-cache@v2
46
51
 
47
52
  - name: Build wheels
48
- run: scripts/build-wheels.sh --out dist
53
+ run: scripts/build-wheels.sh --out dist --target "${{ matrix.rust_target }}"
49
54
 
50
55
  - name: Upload wheel artifacts
51
56
  uses: actions/upload-artifact@v4
@@ -943,7 +943,7 @@ dependencies = [
943
943
 
944
944
  [[package]]
945
945
  name = "rsloop"
946
- version = "0.1.0"
946
+ version = "0.1.1"
947
947
  dependencies = [
948
948
  "crossbeam-channel",
949
949
  "futures",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rsloop"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  edition = "2021"
5
5
  description = "An event loop for asyncio written in Rust"
6
6
  license = "Apache-2.0"
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright [yyyy] [name of copyright owner]
189
+ Copyright 2026 Yehor Smoliakov
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rsloop
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: Apache Software License
@@ -32,16 +32,35 @@ Project-URL: Repository, https://github.com/RustedBytes/rsloop
32
32
 
33
33
  <img src="./rsloop.png" alt="rsloop logo" align="center">
34
34
 
35
- # `rsloop`: An event loop for asyncio written in Rust
35
+ # An event loop for asyncio written in Rust
36
36
 
37
- `rsloop` is a PyO3-based `asyncio` event loop. Each `rsloop.Loop` still owns a
38
- dedicated Rust runtime thread for timers, readiness polling, signal watchers,
39
- and transport coordination, but Python callbacks, tasks, and coroutines are
40
- driven on the thread that calls `run_forever()` / `run_until_complete()`
41
- (typically the main thread). The package exposes a Python module at
37
+ [![PyPI - Version](https://img.shields.io/pypi/v/rsloop)](https://pypi.org/project/rsloop/)
38
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/rsloop?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/rsloop)
39
+
40
+ `rsloop` is a PyO3-based `asyncio` event loop. Each `rsloop.Loop` owns a
41
+ dedicated Rust runtime thread that handles loop coordination and I/O-related
42
+ runtime work, while Python callbacks, tasks, and coroutines are executed on
43
+ the thread that calls `run_forever()` / `run_until_complete()` (typically the
44
+ main thread). The package exposes a Python module at
42
45
  `rsloop._loop` plus a small Python wrapper in `python/rsloop/__init__.py`.
43
46
 
44
- The project metadata currently targets Python `>=3.8`.
47
+ The project metadata currently targets Python `>=3.8`. The current
48
+ implementation is Unix-focused and intended for Linux and macOS; Windows is
49
+ not supported by this codebase.
50
+
51
+ ## Install
52
+
53
+ Install the published package from PyPI:
54
+
55
+ ```bash
56
+ pip install rsloop
57
+ ```
58
+
59
+ With `uv`:
60
+
61
+ ```bash
62
+ uv add rsloop
63
+ ```
45
64
 
46
65
  ## Current Surface Area
47
66
 
@@ -93,7 +112,8 @@ Today’s codebase provides:
93
112
  - `create_unix_connection`
94
113
  - `connect_accepted_socket`
95
114
  - returned `Server` and `StreamTransport` objects
96
- - `transport.close()` / `transport.abort()` fully close socket transports
115
+ - `transport.close()` / `transport.abort()` are implemented for socket
116
+ transports
97
117
  - pipe transports:
98
118
  - `connect_read_pipe`
99
119
  - `connect_write_pipe`
@@ -145,6 +165,12 @@ level transport implementation lives in `src/stream_transport.rs`.
145
165
  - `get_write_buffer_size()` returns `0`
146
166
  - `get_write_buffer_limits()` returns `(0, 0)`
147
167
  - `set_write_buffer_limits()` is a no-op
168
+ - several asyncio compatibility parameters are currently accepted only for API
169
+ shape and are not implemented:
170
+ - `create_connection(..., server_hostname=..., happy_eyeballs_delay=..., interleave=..., all_errors=...)`
171
+ - TLS timeout parameters such as `ssl_handshake_timeout` /
172
+ `ssl_shutdown_timeout`
173
+ - `shutdown_default_executor(timeout=...)`
148
174
  - subprocess support is intentionally incomplete:
149
175
  - `preexec_fn` is unsupported
150
176
  - text mode is rejected for higher-level
@@ -180,19 +206,6 @@ Build release wheels for CPython 3.8 through 3.14, plus the free-threaded
180
206
  scripts/build-wheels.sh
181
207
  ```
182
208
 
183
- ## Publishing
184
-
185
- PyPI releases are published from Git tags that start with `v`, for example
186
- `v0.1.0`. The GitHub Actions workflow builds wheels by calling
187
- `scripts/build-wheels.sh`, builds an `sdist`, and then publishes the combined
188
- artifacts to PyPI with GitHub trusted publishing.
189
-
190
- Before the first release, configure the `rsloop` project on PyPI as a trusted
191
- publisher for this repository/workflow.
192
-
193
- Use a release build for benchmarks and runtime comparisons. A debug extension
194
- will make the Rust loop look much slower than it really is.
195
-
196
209
  ## Profiling
197
210
 
198
211
  Profiling support is behind the Cargo feature `profiler` and is disabled by
@@ -228,6 +241,7 @@ This writes an SVG flamegraph that you can open directly in a browser. Run the
228
241
  profile against a release build or the stack samples will be dominated by debug
229
242
  overhead. If the extension was built without `--features profiler`,
230
243
  `start_profiler()` and `profile()` will raise a runtime error.
244
+ Only `format="flamegraph"` is currently implemented.
231
245
 
232
246
  ## Usage
233
247
 
@@ -1,15 +1,34 @@
1
1
  <img src="./rsloop.png" alt="rsloop logo" align="center">
2
2
 
3
- # `rsloop`: An event loop for asyncio written in Rust
3
+ # An event loop for asyncio written in Rust
4
4
 
5
- `rsloop` is a PyO3-based `asyncio` event loop. Each `rsloop.Loop` still owns a
6
- dedicated Rust runtime thread for timers, readiness polling, signal watchers,
7
- and transport coordination, but Python callbacks, tasks, and coroutines are
8
- driven on the thread that calls `run_forever()` / `run_until_complete()`
9
- (typically the main thread). The package exposes a Python module at
5
+ [![PyPI - Version](https://img.shields.io/pypi/v/rsloop)](https://pypi.org/project/rsloop/)
6
+ [![PyPI Downloads](https://static.pepy.tech/personalized-badge/rsloop?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/rsloop)
7
+
8
+ `rsloop` is a PyO3-based `asyncio` event loop. Each `rsloop.Loop` owns a
9
+ dedicated Rust runtime thread that handles loop coordination and I/O-related
10
+ runtime work, while Python callbacks, tasks, and coroutines are executed on
11
+ the thread that calls `run_forever()` / `run_until_complete()` (typically the
12
+ main thread). The package exposes a Python module at
10
13
  `rsloop._loop` plus a small Python wrapper in `python/rsloop/__init__.py`.
11
14
 
12
- The project metadata currently targets Python `>=3.8`.
15
+ The project metadata currently targets Python `>=3.8`. The current
16
+ implementation is Unix-focused and intended for Linux and macOS; Windows is
17
+ not supported by this codebase.
18
+
19
+ ## Install
20
+
21
+ Install the published package from PyPI:
22
+
23
+ ```bash
24
+ pip install rsloop
25
+ ```
26
+
27
+ With `uv`:
28
+
29
+ ```bash
30
+ uv add rsloop
31
+ ```
13
32
 
14
33
  ## Current Surface Area
15
34
 
@@ -61,7 +80,8 @@ Today’s codebase provides:
61
80
  - `create_unix_connection`
62
81
  - `connect_accepted_socket`
63
82
  - returned `Server` and `StreamTransport` objects
64
- - `transport.close()` / `transport.abort()` fully close socket transports
83
+ - `transport.close()` / `transport.abort()` are implemented for socket
84
+ transports
65
85
  - pipe transports:
66
86
  - `connect_read_pipe`
67
87
  - `connect_write_pipe`
@@ -113,6 +133,12 @@ level transport implementation lives in `src/stream_transport.rs`.
113
133
  - `get_write_buffer_size()` returns `0`
114
134
  - `get_write_buffer_limits()` returns `(0, 0)`
115
135
  - `set_write_buffer_limits()` is a no-op
136
+ - several asyncio compatibility parameters are currently accepted only for API
137
+ shape and are not implemented:
138
+ - `create_connection(..., server_hostname=..., happy_eyeballs_delay=..., interleave=..., all_errors=...)`
139
+ - TLS timeout parameters such as `ssl_handshake_timeout` /
140
+ `ssl_shutdown_timeout`
141
+ - `shutdown_default_executor(timeout=...)`
116
142
  - subprocess support is intentionally incomplete:
117
143
  - `preexec_fn` is unsupported
118
144
  - text mode is rejected for higher-level
@@ -148,19 +174,6 @@ Build release wheels for CPython 3.8 through 3.14, plus the free-threaded
148
174
  scripts/build-wheels.sh
149
175
  ```
150
176
 
151
- ## Publishing
152
-
153
- PyPI releases are published from Git tags that start with `v`, for example
154
- `v0.1.0`. The GitHub Actions workflow builds wheels by calling
155
- `scripts/build-wheels.sh`, builds an `sdist`, and then publishes the combined
156
- artifacts to PyPI with GitHub trusted publishing.
157
-
158
- Before the first release, configure the `rsloop` project on PyPI as a trusted
159
- publisher for this repository/workflow.
160
-
161
- Use a release build for benchmarks and runtime comparisons. A debug extension
162
- will make the Rust loop look much slower than it really is.
163
-
164
177
  ## Profiling
165
178
 
166
179
  Profiling support is behind the Cargo feature `profiler` and is disabled by
@@ -196,6 +209,7 @@ This writes an SVG flamegraph that you can open directly in a browser. Run the
196
209
  profile against a release build or the stack samples will be dominated by debug
197
210
  overhead. If the extension was built without `--features profiler`,
198
211
  `start_profiler()` and `profile()` will raise a runtime error.
212
+ Only `format="flamegraph"` is currently implemented.
199
213
 
200
214
  ## Usage
201
215
 
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
 
3
5
  import rsloop
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import os
3
5
  import socket
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import os
3
5
  import signal
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "rsloop"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "An event loop for asyncio written in Rust"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -25,6 +25,31 @@ __all__: tuple[str, ...] = (
25
25
  "stop_profiler",
26
26
  )
27
27
  _T = __typing.TypeVar("_T")
28
+ __ORIG_SET_EVENT_LOOP = __asyncio.set_event_loop
29
+
30
+
31
+ def __set_event_loop(loop: Loop | None) -> None:
32
+ try:
33
+ __ORIG_SET_EVENT_LOOP(loop)
34
+ return
35
+ except (AssertionError, TypeError):
36
+ if loop is None or not isinstance(loop, Loop):
37
+ raise
38
+
39
+ # Python 3.8 rejects non-stdlib loop objects in set_event_loop() with a
40
+ # hard isinstance() assertion. Mirror the stdlib policy bookkeeping so
41
+ # get_event_loop() still returns the current rsloop instance.
42
+ policy = __asyncio.get_event_loop_policy()
43
+ local = getattr(policy, "_local", None)
44
+ if local is None:
45
+ raise
46
+ local._set_called = True
47
+ local._loop = loop
48
+
49
+
50
+ if __asyncio.set_event_loop is __ORIG_SET_EVENT_LOOP:
51
+ __asyncio.set_event_loop = __set_event_loop
52
+ __asyncio.events.set_event_loop = __set_event_loop
28
53
 
29
54
 
30
55
  def new_event_loop() -> Loop:
@@ -10,10 +10,11 @@ usage() {
10
10
  Build release wheels for rsloop with uv-managed Python interpreters.
11
11
 
12
12
  Usage:
13
- scripts/build-wheels.sh [--out DIR] [--skip-python-install] [-- maturin args...]
13
+ scripts/build-wheels.sh [--out DIR] [--target TRIPLE] [--skip-python-install] [-- maturin args...]
14
14
 
15
15
  Options:
16
16
  -o, --out DIR Output directory for built wheels (default: dist/wheels)
17
+ -t, --target TRIPLE Rust compilation target to pass to maturin
17
18
  --skip-python-install
18
19
  Reuse installed interpreters instead of running `uv python install`
19
20
  -h, --help Show this help
@@ -21,10 +22,12 @@ Options:
21
22
  Environment:
22
23
  RSLOOP_PYTHON_VERSIONS Space-separated version list to override the defaults
23
24
  (default: 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.14t)
25
+ RSLOOP_RUST_TARGET Rust compilation target to pass to maturin
24
26
 
25
27
  Examples:
26
28
  scripts/build-wheels.sh
27
29
  scripts/build-wheels.sh --out wheelhouse
30
+ scripts/build-wheels.sh --target aarch64-apple-darwin
28
31
  RSLOOP_PYTHON_VERSIONS="3.12 3.13 3.14t" scripts/build-wheels.sh -- --features profiler
29
32
  EOF
30
33
  }
@@ -40,6 +43,7 @@ resolve_path() {
40
43
 
41
44
  OUTPUT_DIR="$DEFAULT_OUTPUT_DIR"
42
45
  INSTALL_PYTHONS=1
46
+ RUST_TARGET="${RSLOOP_RUST_TARGET:-}"
43
47
  MATURIN_ARGS=()
44
48
 
45
49
  while (($#)); do
@@ -52,6 +56,14 @@ while (($#)); do
52
56
  OUTPUT_DIR="$2"
53
57
  shift 2
54
58
  ;;
59
+ -t|--target)
60
+ if (($# < 2)); then
61
+ echo "missing value for $1" >&2
62
+ exit 1
63
+ fi
64
+ RUST_TARGET="$2"
65
+ shift 2
66
+ ;;
55
67
  --skip-python-install)
56
68
  INSTALL_PYTHONS=0
57
69
  shift
@@ -92,15 +104,25 @@ fi
92
104
  for version in "${PYTHON_VERSIONS[@]}"; do
93
105
  interpreter="$(uv python find "$version")"
94
106
  echo "Building release wheel for Python ${version} (${interpreter})"
95
- uv run \
96
- --no-project \
97
- --python "$interpreter" \
98
- --with maturin \
99
- maturin build \
100
- --release \
101
- --interpreter "$interpreter" \
102
- --out "$OUTPUT_DIR" \
103
- "${MATURIN_ARGS[@]}"
107
+ maturin_cmd=(
108
+ uv run
109
+ --no-project
110
+ --python "$interpreter"
111
+ --with maturin
112
+ maturin build
113
+ --release
114
+ --interpreter "$interpreter"
115
+ --out "$OUTPUT_DIR"
116
+ )
117
+ if [[ -n "$RUST_TARGET" ]]; then
118
+ maturin_cmd+=(
119
+ --target "$RUST_TARGET"
120
+ )
121
+ fi
122
+ if ((${#MATURIN_ARGS[@]})); then
123
+ maturin_cmd+=("${MATURIN_ARGS[@]}")
124
+ fi
125
+ "${maturin_cmd[@]}"
104
126
  done
105
127
 
106
128
  echo "Wheels written to $OUTPUT_DIR"
@@ -13,7 +13,6 @@ pub type CallbackId = u64;
13
13
 
14
14
  enum CallbackArgs {
15
15
  None,
16
- One(Py<PyAny>),
17
16
  Many(Py<PyTuple>),
18
17
  }
19
18
 
@@ -50,12 +49,6 @@ impl ReadyCallback {
50
49
  ) -> Self {
51
50
  let args = match args.bind(py).len() {
52
51
  0 => CallbackArgs::None,
53
- 1 => CallbackArgs::One(
54
- args.bind(py)
55
- .get_item(0)
56
- .expect("single callback arg missing")
57
- .unbind(),
58
- ),
59
52
  _ => CallbackArgs::Many(args),
60
53
  };
61
54
 
@@ -127,13 +120,6 @@ impl ReadyCallback {
127
120
  )
128
121
  .map(Bound::unbind)
129
122
  },
130
- CallbackArgs::One(arg) => unsafe {
131
- Bound::from_owned_ptr_or_err(
132
- py,
133
- ffi::PyObject_CallOneArg(self.callback.as_ptr(), arg.as_ptr()),
134
- )
135
- .map(Bound::unbind)
136
- },
137
123
  CallbackArgs::Many(args) => self.callback.call1(py, args.clone_ref(py)),
138
124
  }
139
125
  }
@@ -141,9 +127,6 @@ impl ReadyCallback {
141
127
  pub fn clone_args_tuple(&self, py: Python<'_>) -> Py<PyTuple> {
142
128
  match &self.args {
143
129
  CallbackArgs::None => PyTuple::empty(py).unbind(),
144
- CallbackArgs::One(arg) => PyTuple::new(py, [arg.clone_ref(py)])
145
- .expect("failed to clone single callback arg")
146
- .unbind(),
147
130
  CallbackArgs::Many(args) => args.clone_ref(py),
148
131
  }
149
132
  }
@@ -8,7 +8,6 @@ use std::sync::OnceLock;
8
8
  use std::time::Duration;
9
9
 
10
10
  use pyo3::exceptions::{PyNotImplementedError, PyRuntimeError, PyTypeError, PyValueError};
11
- use pyo3::ffi;
12
11
  use pyo3::prelude::*;
13
12
  use pyo3::types::{PyDict, PyModule, PySlice, PyTuple};
14
13
  use pyo3_async_runtimes::TaskLocals;
@@ -123,13 +122,7 @@ fn create_asyncio_future_for_loop(py: Python<'_>, loop_obj: &Py<PyAny>) -> PyRes
123
122
  }
124
123
 
125
124
  fn create_asyncio_future_for_running_loop(py: Python<'_>) -> PyResult<Py<PyAny>> {
126
- unsafe {
127
- Bound::from_owned_ptr_or_err(
128
- py,
129
- ffi::compat::PyObject_CallNoArgs(asyncio_future_cls(py)?.as_ptr()),
130
- )
131
- .map(Bound::unbind)
132
- }
125
+ asyncio_future_cls(py)?.call0(py)
133
126
  }
134
127
 
135
128
  fn create_asyncio_task_for_loop(
@@ -151,13 +144,7 @@ fn create_asyncio_task_for_loop(
151
144
  }
152
145
 
153
146
  fn create_asyncio_task_for_running_loop(py: Python<'_>, coro: Py<PyAny>) -> PyResult<Py<PyAny>> {
154
- unsafe {
155
- Bound::from_owned_ptr_or_err(
156
- py,
157
- ffi::PyObject_CallOneArg(asyncio_task_cls(py)?.as_ptr(), coro.as_ptr()),
158
- )
159
- .map(Bound::unbind)
160
- }
147
+ asyncio_task_cls(py)?.call1(py, (coro,))
161
148
  }
162
149
 
163
150
  fn call_protocol_factory(
@@ -0,0 +1,25 @@
1
+ import asyncio
2
+ import unittest
3
+
4
+ import rsloop
5
+
6
+
7
+ class RunTests(unittest.TestCase):
8
+ def test_set_event_loop_accepts_rsloop_loop(self) -> None:
9
+ loop = rsloop.new_event_loop()
10
+ try:
11
+ asyncio.set_event_loop(loop)
12
+ self.assertIs(asyncio.get_event_loop(), loop)
13
+ finally:
14
+ asyncio.set_event_loop(None)
15
+ loop.close()
16
+
17
+ def test_run_executes_coroutine(self) -> None:
18
+ async def main() -> str:
19
+ return "ok"
20
+
21
+ self.assertEqual(rsloop.run(main()), "ok")
22
+
23
+
24
+ if __name__ == "__main__":
25
+ unittest.main()
@@ -4,5 +4,5 @@ requires-python = ">=3.8"
4
4
 
5
5
  [[package]]
6
6
  name = "rsloop"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  source = { editable = "." }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes