Skip to content

Commit 390a785

Browse files
author
Andrew J Westlake
committed
Added example for non-standard Python event loops
1 parent 9daa361 commit 390a785

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,121 @@ async fn main() -> PyResult<()> {
361361
}
362362
```
363363
364+
### Non-standard Python Event Loops
365+
366+
Python allows you to use alternatives to the default `asyncio` event loop. One
367+
popular alternative is `uvloop`. In `v0.13` using non-standard event loops was
368+
a bit of an ordeal, but in `v0.14` it's trivial.
369+
370+
#### Using `uvloop` in a PyO3 Asyncio Native Extensions
371+
372+
```toml
373+
# Cargo.toml
374+
375+
[lib]
376+
name = "my_async_module"
377+
crate-type = ["cdylib"]
378+
379+
[dependencies]
380+
pyo3 = { version = "0.14", features = ["extension-module", "auto-initialize"] }
381+
pyo3-asyncio = { version = "0.14", features = ["tokio-runtime"] }
382+
async-std = "1.9"
383+
tokio = "1.4"
384+
```
385+
386+
```rust
387+
//! lib.rs
388+
389+
use pyo3::{prelude::*, wrap_pyfunction};
390+
391+
#[pyfunction]
392+
fn rust_sleep(py: Python) -> PyResult<&PyAny> {
393+
pyo3_asyncio::tokio::future_into_py(py, async {
394+
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
395+
Ok(Python::with_gil(|py| py.None()))
396+
})
397+
}
398+
399+
#[pymodule]
400+
fn my_async_module(_py: Python, m: &PyModule) -> PyResult<()> {
401+
m.add_function(wrap_pyfunction!(rust_sleep, m)?)?;
402+
403+
Ok(())
404+
}
405+
```
406+
407+
```bash
408+
$ cargo build --release && mv target/release/libmy_async_module.so my_async_module.so
409+
Compiling pyo3-asyncio-lib v0.1.0 (pyo3-asyncio-lib)
410+
Finished release [optimized] target(s) in 1.00s
411+
$ PYTHONPATH=target/release/ python3
412+
Python 3.8.8 (default, Apr 13 2021, 19:58:26)
413+
[GCC 7.3.0] :: Anaconda, Inc. on linux
414+
Type "help", "copyright", "credits" or "license" for more information.
415+
>>> import asyncio
416+
>>> import uvloop
417+
>>>
418+
>>> import my_async_module
419+
>>>
420+
>>> uvloop.install()
421+
>>>
422+
>>> async def main():
423+
... await my_async_module.rust_sleep()
424+
...
425+
>>> asyncio.run(main())
426+
>>>
427+
```
428+
429+
#### Using `uvloop` in Rust Applications
430+
431+
Using `uvloop` in Rust applications is a bit trickier, but it's still possible
432+
with relatively few modifications.
433+
434+
> Unfortunately, we can't make use of the `#[pyo3_asyncio::<runtime>::main]` attribute with non-standard event loops. This is because the `#[pyo3_asyncio::<runtime>::main]` proc macro has to interact with the Python
435+
event loop before we can install the `uvloop` policy.
436+
437+
```toml
438+
[dependencies]
439+
async-std = "1.9"
440+
pyo3 = "0.14"
441+
pyo3-asyncio = { version = "0.14", features = ["async-std-runtime"] }
442+
```
443+
444+
```rust
445+
//! main.rs
446+
447+
use pyo3::{prelude::*, types::PyType};
448+
449+
fn main() -> PyResult<()> {
450+
pyo3::prepare_freethreaded_python();
451+
452+
Python::with_gil(|py| {
453+
let uvloop = py.import("uvloop")?;
454+
uvloop.call_method0("install")?;
455+
456+
// store a reference for the assertion
457+
let uvloop = PyObject::from(uvloop);
458+
459+
pyo3_asyncio::async_std::run(py, async move {
460+
// verify that we are on a uvloop.Loop
461+
Python::with_gil(|py| -> PyResult<()> {
462+
assert!(uvloop
463+
.as_ref(py)
464+
.getattr("Loop")?
465+
.downcast::<PyType>()
466+
.unwrap()
467+
.is_instance(pyo3_asyncio::async_std::get_current_loop(py)?)?);
468+
Ok(())
469+
})?;
470+
471+
async_std::task::sleep(std::time::Duration::from_secs(1)).await;
472+
473+
Ok(())
474+
})
475+
})
476+
}
477+
```
478+
364479
### Event Loop References and Thread-awareness
365480

366481
One problem that arises when interacting with Python's asyncio library is that the functions we use to get a reference to the Python event loop can only be called in certain contexts. Since PyO3 Asyncio needs to interact with Python's event loop during conversions, the context of these conversions can matter a lot.

0 commit comments

Comments
 (0)