Skip to content

Commit 1c4f520

Browse files
author
Andrew J Westlake
committed
Added test for running asyncio.run multiple times, found new issue that needs to be documented
1 parent ebbde13 commit 1c4f520

File tree

6 files changed

+149
-33
lines changed

6 files changed

+149
-33
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ once_cell = "1.5"
9292
pyo3 = "0.14"
9393
pyo3-asyncio-macros = { path = "pyo3-asyncio-macros", version = "=0.14.0", optional = true }
9494

95+
[dev-dependencies]
96+
pyo3 = { version = "0.14", features = ["macros"] }
97+
9598
[dependencies.async-std]
9699
version = "1.9"
97100
features = ["unstable"]

pytests/test_async_std_asyncio.rs

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ mod common;
33
use std::{rc::Rc, time::Duration};
44

55
use async_std::task;
6-
use pyo3::{prelude::*, types::PyType, wrap_pyfunction};
6+
use pyo3::{
7+
prelude::*,
8+
proc_macro::pymodule,
9+
types::{IntoPyDict, PyType},
10+
wrap_pyfunction, wrap_pymodule,
11+
};
712

813
#[pyfunction]
914
#[allow(deprecated)]
@@ -27,8 +32,9 @@ fn sleep<'p>(py: Python<'p>, secs: &'p PyAny) -> PyResult<&'p PyAny> {
2732
}
2833

2934
#[pyo3_asyncio::async_std::test]
30-
async fn test_into_coroutine() -> PyResult<()> {
31-
let fut = Python::with_gil(|py| {
35+
fn test_into_coroutine() -> PyResult<()> {
36+
#[allow(deprecated)]
37+
Python::with_gil(|py| {
3238
let sleeper_mod = PyModule::new(py, "rust_sleeper")?;
3339

3440
sleeper_mod.add_wrapped(wrap_pyfunction!(sleep_into_coroutine))?;
@@ -40,15 +46,21 @@ async fn test_into_coroutine() -> PyResult<()> {
4046
"test_into_coroutine_mod",
4147
)?;
4248

43-
pyo3_asyncio::async_std::into_future(test_mod.call_method1(
49+
let fut = pyo3_asyncio::into_future(test_mod.call_method1(
4450
"sleep_for_1s",
4551
(sleeper_mod.getattr("sleep_into_coroutine")?,),
46-
)?)
47-
})?;
52+
)?)?;
4853

49-
fut.await?;
54+
pyo3_asyncio::async_std::run_until_complete(
55+
pyo3_asyncio::get_event_loop(py),
56+
async move {
57+
fut.await?;
58+
Ok(())
59+
},
60+
)?;
5061

51-
Ok(())
62+
Ok(())
63+
})
5264
}
5365

5466
#[pyo3_asyncio::async_std::test]
@@ -189,6 +201,52 @@ async fn test_cancel() -> PyResult<()> {
189201
Ok(())
190202
}
191203

204+
/// This module is implemented in Rust.
205+
#[pymodule]
206+
fn test_mod(_py: Python, m: &PyModule) -> PyResult<()> {
207+
#![allow(deprecated)]
208+
#[pyfn(m, "sleep")]
209+
fn sleep(py: Python) -> PyResult<&PyAny> {
210+
pyo3_asyncio::async_std::future_into_py(py, async move {
211+
async_std::task::sleep(Duration::from_millis(500)).await;
212+
Ok(Python::with_gil(|py| py.None()))
213+
})
214+
}
215+
216+
Ok(())
217+
}
218+
219+
const TEST_CODE: &str = r#"
220+
async def main():
221+
return await test_mod.sleep()
222+
223+
asyncio.run(main())
224+
"#;
225+
226+
#[pyo3_asyncio::async_std::test]
227+
fn test_multiple_asyncio_run() -> PyResult<()> {
228+
Python::with_gil(|py| {
229+
pyo3_asyncio::async_std::run(py, async move {
230+
async_std::task::sleep(Duration::from_millis(500)).await;
231+
Ok(())
232+
})?;
233+
pyo3_asyncio::async_std::run(py, async move {
234+
async_std::task::sleep(Duration::from_millis(500)).await;
235+
Ok(())
236+
})?;
237+
238+
let d = [
239+
("asyncio", py.import("asyncio")?.into()),
240+
("test_mod", wrap_pymodule!(test_mod)(py)),
241+
]
242+
.into_py_dict(py);
243+
244+
py.run(TEST_CODE, Some(d), None)?;
245+
py.run(TEST_CODE, Some(d), None)?;
246+
Ok(())
247+
})
248+
}
249+
192250
#[allow(deprecated)]
193251
fn main() -> pyo3::PyResult<()> {
194252
pyo3::prepare_freethreaded_python();

pytests/tokio_asyncio/mod.rs

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
use std::{rc::Rc, time::Duration};
22

3-
use pyo3::{prelude::*, types::PyType, wrap_pyfunction};
3+
use pyo3::{
4+
prelude::*,
5+
proc_macro::pymodule,
6+
types::{IntoPyDict, PyType},
7+
wrap_pyfunction, wrap_pymodule,
8+
};
49

510
use crate::common;
611

@@ -26,8 +31,9 @@ fn sleep<'p>(py: Python<'p>, secs: &'p PyAny) -> PyResult<&'p PyAny> {
2631
}
2732

2833
#[pyo3_asyncio::tokio::test]
29-
async fn test_into_coroutine() -> PyResult<()> {
30-
let fut = Python::with_gil(|py| {
34+
fn test_into_coroutine() -> PyResult<()> {
35+
#[allow(deprecated)]
36+
Python::with_gil(|py| {
3137
let sleeper_mod = PyModule::new(py, "rust_sleeper")?;
3238

3339
sleeper_mod.add_wrapped(wrap_pyfunction!(sleep_into_coroutine))?;
@@ -39,15 +45,18 @@ async fn test_into_coroutine() -> PyResult<()> {
3945
"test_into_coroutine_mod",
4046
)?;
4147

42-
pyo3_asyncio::tokio::into_future(test_mod.call_method1(
48+
let fut = pyo3_asyncio::into_future(test_mod.call_method1(
4349
"sleep_for_1s",
4450
(sleeper_mod.getattr("sleep_into_coroutine")?,),
45-
)?)
46-
})?;
51+
)?)?;
4752

48-
fut.await?;
53+
pyo3_asyncio::tokio::run_until_complete(pyo3_asyncio::get_event_loop(py), async move {
54+
fut.await?;
55+
Ok(())
56+
})?;
4957

50-
Ok(())
58+
Ok(())
59+
})
5160
}
5261

5362
#[pyo3_asyncio::tokio::test]
@@ -198,3 +207,48 @@ async fn test_cancel() -> PyResult<()> {
198207

199208
Ok(())
200209
}
210+
/// This module is implemented in Rust.
211+
#[pymodule]
212+
fn test_mod(_py: Python, m: &PyModule) -> PyResult<()> {
213+
#![allow(deprecated)]
214+
#[pyfn(m, "sleep")]
215+
fn sleep(py: Python) -> PyResult<&PyAny> {
216+
pyo3_asyncio::async_std::future_into_py(py, async move {
217+
async_std::task::sleep(Duration::from_millis(500)).await;
218+
Ok(Python::with_gil(|py| py.None()))
219+
})
220+
}
221+
222+
Ok(())
223+
}
224+
225+
const TEST_CODE: &str = r#"
226+
async def main():
227+
return await test_mod.sleep()
228+
229+
asyncio.run(main())
230+
"#;
231+
232+
#[pyo3_asyncio::tokio::test]
233+
fn test_multiple_asyncio_run() -> PyResult<()> {
234+
Python::with_gil(|py| {
235+
pyo3_asyncio::tokio::run(py, async move {
236+
tokio::time::sleep(Duration::from_millis(500)).await;
237+
Ok(())
238+
})?;
239+
pyo3_asyncio::tokio::run(py, async move {
240+
tokio::time::sleep(Duration::from_millis(500)).await;
241+
Ok(())
242+
})?;
243+
244+
let d = [
245+
("asyncio", py.import("asyncio")?.into()),
246+
("test_mod", wrap_pymodule!(test_mod)(py)),
247+
]
248+
.into_py_dict(py);
249+
250+
py.run(TEST_CODE, Some(d), None)?;
251+
py.run(TEST_CODE, Some(d), None)?;
252+
Ok(())
253+
})
254+
}

src/async_std.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ pub fn get_current_loop(py: Python) -> PyResult<&PyAny> {
134134
/// [`run_forever`](`crate::run_forever`)
135135
///
136136
/// # Arguments
137-
/// * `py` - The current PyO3 GIL guard
137+
/// * `event_loop` - The Python event loop that should run the future
138138
/// * `fut` - The future to drive to completion
139139
///
140140
/// # Examples
@@ -148,7 +148,8 @@ pub fn get_current_loop(py: Python) -> PyResult<&PyAny> {
148148
/// #
149149
/// # Python::with_gil(|py| {
150150
/// # pyo3_asyncio::with_runtime(py, || {
151-
/// pyo3_asyncio::async_std::run_until_complete(py, async move {
151+
/// # let event_loop = py.import("asyncio")?.call_method0("new_event_loop")?;
152+
/// pyo3_asyncio::async_std::run_until_complete(event_loop, async move {
152153
/// async_std::task::sleep(Duration::from_secs(1)).await;
153154
/// Ok(())
154155
/// })?;
@@ -160,11 +161,11 @@ pub fn get_current_loop(py: Python) -> PyResult<&PyAny> {
160161
/// # .unwrap();
161162
/// # });
162163
/// ```
163-
pub fn run_until_complete<F>(py: Python, fut: F) -> PyResult<()>
164+
pub fn run_until_complete<F>(event_loop: &PyAny, fut: F) -> PyResult<()>
164165
where
165166
F: Future<Output = PyResult<()>> + Send + 'static,
166167
{
167-
generic::run_until_complete::<AsyncStdRuntime, _>(py, fut)
168+
generic::run_until_complete::<AsyncStdRuntime, _>(event_loop, fut)
168169
}
169170

170171
/// Run the event loop until the given Future completes

src/generic.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use pyo3::{prelude::*, PyNativeType};
44

55
#[allow(deprecated)]
66
use crate::{
7-
asyncio_get_event_loop, call_soon_threadsafe, close, create_future, dump_err, err::RustPanic,
8-
get_event_loop, get_running_loop, into_future_with_loop,
7+
asyncio, call_soon_threadsafe, close, create_future, dump_err, err::RustPanic, get_event_loop,
8+
get_running_loop, into_future_with_loop,
99
};
1010

1111
/// Generic utilities for a JoinError
@@ -69,7 +69,7 @@ where
6969
/// [`run_forever`](`crate::run_forever`)
7070
///
7171
/// # Arguments
72-
/// * `py` - The current PyO3 GIL guard
72+
/// * `event_loop` - The Python event loop that should run the future
7373
/// * `fut` - The future to drive to completion
7474
///
7575
/// # Examples
@@ -127,8 +127,9 @@ where
127127
/// #
128128
/// # Python::with_gil(|py| {
129129
/// # pyo3_asyncio::with_runtime(py, || {
130+
/// # let event_loop = py.import("asyncio")?.call_method0("new_event_loop")?;
130131
/// # #[cfg(feature = "tokio-runtime")]
131-
/// pyo3_asyncio::generic::run_until_complete::<MyCustomRuntime, _>(py, async move {
132+
/// pyo3_asyncio::generic::run_until_complete::<MyCustomRuntime, _>(event_loop, async move {
132133
/// tokio::time::sleep(Duration::from_secs(1)).await;
133134
/// Ok(())
134135
/// })?;
@@ -140,13 +141,11 @@ where
140141
/// # .unwrap();
141142
/// # });
142143
/// ```
143-
pub fn run_until_complete<R, F>(py: Python, fut: F) -> PyResult<()>
144+
pub fn run_until_complete<R, F>(event_loop: &PyAny, fut: F) -> PyResult<()>
144145
where
145146
R: Runtime,
146147
F: Future<Output = PyResult<()>> + Send + 'static,
147148
{
148-
let event_loop = asyncio_get_event_loop(py)?;
149-
150149
let coro = future_into_py_with_loop::<R, _>(event_loop, async move {
151150
fut.await?;
152151
Ok(Python::with_gil(|py| py.None()))
@@ -235,9 +234,9 @@ where
235234
R: Runtime,
236235
F: Future<Output = PyResult<()>> + Send + 'static,
237236
{
238-
let event_loop = asyncio_get_event_loop(py)?;
237+
let event_loop = asyncio(py)?.call_method0("new_event_loop")?;
239238

240-
let result = run_until_complete::<R, F>(py, fut);
239+
let result = run_until_complete::<R, F>(event_loop, fut);
241240

242241
close(event_loop)?;
243242

src/tokio.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ pub fn init_current_thread_once() {
204204
/// [`crate::run_forever`]
205205
///
206206
/// # Arguments
207-
/// * `py` - The current PyO3 GIL guard
207+
/// * `event_loop` - The Python event loop that should run the future
208208
/// * `fut` - The future to drive to completion
209209
///
210210
/// # Examples
@@ -224,7 +224,8 @@ pub fn init_current_thread_once() {
224224
/// # Python::with_gil(|py| {
225225
/// # pyo3_asyncio::with_runtime(py, || {
226226
/// # pyo3_asyncio::tokio::init_current_thread();
227-
/// pyo3_asyncio::tokio::run_until_complete(py, async move {
227+
/// # let event_loop = py.import("asyncio")?.call_method0("new_event_loop")?;
228+
/// pyo3_asyncio::tokio::run_until_complete(event_loop, async move {
228229
/// tokio::time::sleep(Duration::from_secs(1)).await;
229230
/// Ok(())
230231
/// })?;
@@ -236,11 +237,11 @@ pub fn init_current_thread_once() {
236237
/// # .unwrap();
237238
/// # });
238239
/// ```
239-
pub fn run_until_complete<F>(py: Python, fut: F) -> PyResult<()>
240+
pub fn run_until_complete<F>(event_loop: &PyAny, fut: F) -> PyResult<()>
240241
where
241242
F: Future<Output = PyResult<()>> + Send + 'static,
242243
{
243-
generic::run_until_complete::<TokioRuntime, _>(py, fut)
244+
generic::run_until_complete::<TokioRuntime, _>(event_loop, fut)
244245
}
245246

246247
/// Run the event loop until the given Future completes

0 commit comments

Comments
 (0)