Skip to content

Commit 6f39da9

Browse files
author
Andrew J Westlake
committed
Merge branch 'master' of https://github.com/awestlake87/pyo3-asyncio into stream-conversions
2 parents 090ebdd + e78aaa8 commit 6f39da9

File tree

3 files changed

+168
-8
lines changed

3 files changed

+168
-8
lines changed

.github/workflows/ci.yml

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,16 @@ jobs:
5757
- python-version: pypy-3.6
5858
platform: { os: "windows-latest", python-architecture: "x64" }
5959
include:
60-
# Test minimal supported Rust version
60+
# Test minimal supported Rust version (no async-std)
6161
- rust: 1.45.0
6262
python-version: 3.9
6363
platform: { os: "ubuntu-latest", python-architecture: "x64", rust-target: "x86_64-unknown-linux-gnu" }
6464
msrv: "MSRV"
65+
# Test minimal supported Rust version (with async-std)
66+
- rust: 1.46.0
67+
python-version: 3.9
68+
platform: { os: "ubuntu-latest", python-architecture: "x64", rust-target: "x86_64-unknown-linux-gnu" }
69+
msrv: "MSRV"
6570

6671
steps:
6772
- uses: actions/checkout@v2
@@ -87,13 +92,23 @@ jobs:
8792
- name: Build (no features)
8893
run: cargo build --no-default-features --verbose --target ${{ matrix.platform.rust-target }}
8994

90-
- name: Build (all additive features)
91-
run: cargo build --all-features --verbose --target ${{ matrix.platform.rust-target }}
95+
# Omit async-std-runtime feature for MSRV 1.45.0
96+
- if: matrix.rust == '1.45.0'
97+
name: Prepare 1.45.0 features
98+
run: echo features=testing,attributes,tokio-runtime >> $GITHUB_ENV
99+
100+
# Use all features for MSRV 1.46.0 and above
101+
- if: matrix.rust != '1.45.0'
102+
name: Prepare all features
103+
run: echo features=testing,attributes,tokio-runtime,async-std-runtime >> $GITHUB_ENV
104+
105+
- name: Build
106+
run: cargo build --features=${{env.features}} --verbose --target ${{ matrix.platform.rust-target }}
92107

93108
# Run tests (except on PyPy, because no embedding API).
94109
- if: matrix.python-version != 'pypy-3.6'
95110
name: Test
96-
run: cargo test --all-features --target ${{ matrix.platform.rust-target }}
111+
run: cargo test --features=${{env.features}} --target ${{ matrix.platform.rust-target }}
97112

98113
- name: Install python test dependencies
99114
run: |

README.md

Lines changed: 144 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
[![Actions Status](https://github.com/awestlake87/pyo3-asyncio/workflows/CI/badge.svg)](https://github.com/awestlake87/pyo3-asyncio/actions)
44
[![codecov](https://codecov.io/gh/awestlake87/pyo3-asyncio/branch/master/graph/badge.svg)](https://codecov.io/gh/awestlake87/pyo3-asyncio)
55
[![crates.io](http://meritbadge.herokuapp.com/pyo3-asyncio)](https://crates.io/crates/pyo3-asyncio)
6-
[![minimum rustc 1.45](https://img.shields.io/badge/rustc-1.45+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
6+
[![minimum rustc 1.46](https://img.shields.io/badge/rustc-1.46+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
77

88
[Rust](http://www.rust-lang.org/) bindings for [Python](https://www.python.org/)'s [Asyncio Library](https://docs.python.org/3/library/asyncio.html). This crate facilitates interactions between Rust Futures and Python Coroutines and manages the lifecycle of their corresponding event loops.
99

10-
* API Documentation: [stable](https://docs.rs/pyo3-asyncio/) | [master](https://awestlake87.github.io/pyo3-asyncio/master/doc)
10+
* PyO3 Project: [Homepage](https://pyo3.rs/) | [GitHub](https://github.com/PyO3/pyo3)
11+
12+
* PyO3 Asyncio API Documentation: [stable](https://docs.rs/pyo3-asyncio/) | [master](https://awestlake87.github.io/pyo3-asyncio/master/doc)
13+
14+
* Guide for Async / Await [stable](https://pyo3.rs/v0.13.2/ecosystem/async-await.html) | [main](https://pyo3.rs/main/ecosystem/async-await.html)
1115

1216
* Contributing Notes: [github](https://github.com/awestlake87/pyo3-asyncio/blob/master/Contributing.md)
1317

@@ -19,6 +23,7 @@ This library can give spurious failures during finalization prior to PyO3 releas
1923

2024
## Quickstart
2125

26+
### Rust Applications
2227
Here we initialize the runtime, import Python's `asyncio` library and run the given future to completion using Python's default `EventLoop` and `async-std`. Inside the future, we convert `asyncio` sleep into a Rust future and await it.
2328

2429
More details on the usage of this library can be found in the [API docs](https://awestlake87.github.io/pyo3-asyncio/master/doc).
@@ -39,4 +44,140 @@ async fn main() -> PyResult<()> {
3944

4045
Ok(())
4146
}
42-
```
47+
```
48+
49+
The same application can be written to use `tokio` instead using the `#[pyo3_asyncio::tokio::main]`
50+
attribute.
51+
52+
```rust
53+
use pyo3::prelude::*;
54+
55+
#[pyo3_asyncio::tokio::main]
56+
async fn main() -> PyResult<()> {
57+
let fut = Python::with_gil(|py| {
58+
let asyncio = py.import("asyncio")?;
59+
60+
// convert asyncio.sleep into a Rust Future
61+
pyo3_asyncio::into_future(asyncio.call_method1("sleep", (1.into_py(py),))?)
62+
})?;
63+
64+
fut.await?;
65+
66+
Ok(())
67+
}
68+
```
69+
70+
### PyO3 Native Rust Modules
71+
72+
PyO3 Asyncio can also be used to write native modules with async functions.
73+
74+
Add the `[lib]` section to `Cargo.toml` to make your library a `cdylib` that Python can import.
75+
```toml
76+
[lib]
77+
name = "my_async_module"
78+
crate-type = ["cdylib"]
79+
```
80+
81+
Make your project depend on `pyo3` with the `extension-module` feature enabled and select your
82+
`pyo3-asyncio` runtime:
83+
84+
For `async-std`:
85+
```toml
86+
[dependencies]
87+
pyo3 = { version = "0.13", features = ["extension-module"] }
88+
pyo3-asyncio = { version = "0.13", features = ["async-std-runtime"] }
89+
async-std = "1.9"
90+
```
91+
92+
For `tokio`:
93+
```toml
94+
[dependencies]
95+
pyo3 = { version = "0.13", features = ["extension-module"] }
96+
pyo3-asyncio = { version = "0.13", features = ["tokio-runtime"] }
97+
tokio = "1.4"
98+
```
99+
100+
Export an async function that makes use of `async-std`:
101+
102+
```rust
103+
//! lib.rs
104+
105+
use pyo3::{prelude::*, wrap_pyfunction};
106+
107+
#[pyfunction]
108+
fn rust_sleep(py: Python) -> PyResult<PyObject> {
109+
pyo3_asyncio::async_std::into_coroutine(py, async {
110+
async_std::task::sleep(std::time::Duration::from_secs(1)).await;
111+
Ok(Python::with_gil(|py| py.None()))
112+
})
113+
}
114+
115+
#[pymodule]
116+
fn my_async_module(py: Python, m: &PyModule) -> PyResult<()> {
117+
pyo3_asyncio::try_init(py)?;
118+
119+
m.add_function(wrap_pyfunction!(rust_sleep, m)?)?;
120+
121+
Ok(())
122+
}
123+
124+
```
125+
126+
If you want to use `tokio` instead, here's what your module should look like:
127+
128+
```rust
129+
//! lib.rs
130+
131+
use pyo3::{prelude::*, wrap_pyfunction};
132+
133+
#[pyfunction]
134+
fn rust_sleep(py: Python) -> PyResult<PyObject> {
135+
pyo3_asyncio::tokio::into_coroutine(py, async {
136+
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
137+
Ok(Python::with_gil(|py| py.None()))
138+
})
139+
}
140+
141+
#[pymodule]
142+
fn my_async_module(py: Python, m: &PyModule) -> PyResult<()> {
143+
pyo3_asyncio::try_init(py)?;
144+
// Tokio needs explicit initialization before any pyo3-asyncio conversions.
145+
// The module import is a prime place to do this.
146+
pyo3_asyncio::tokio::init_multi_thread_once();
147+
148+
m.add_function(wrap_pyfunction!(rust_sleep, m)?)?;
149+
150+
Ok(())
151+
}
152+
153+
```
154+
155+
Build your module and rename `libmy_async_module.so` to `my_async_module.so`
156+
```bash
157+
cargo build --release && mv target/release/libmy_async_module.so target/release/my_async_module.so
158+
```
159+
160+
Now, point your `PYTHONPATH` to the directory containing `my_async_module.so`, then you'll be able
161+
to import and use it:
162+
163+
```bash
164+
$ PYTHONPATH=target/release python3
165+
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
166+
[GCC 9.3.0] on linux
167+
Type "help", "copyright", "credits" or "license" for more information.
168+
>>> import asyncio
169+
>>> from my_async_module import rust_sleep
170+
>>>
171+
>>> # should sleep for 1s
172+
>>> asyncio.get_event_loop().run_until_complete(rust_sleep())
173+
>>>
174+
```
175+
176+
> Note that we are using `EventLoop.run_until_complete` here instead of the newer `asyncio.run`. That is because `asyncio.run` will set up its own internal event loop that `pyo3_asyncio` will not be aware of. For this reason, running `pyo3_asyncio` conversions through `asyncio.run` is not currently supported.
177+
>
178+
> This restriction may be lifted in a future release.
179+
180+
## MSRV
181+
Currently the MSRV for this library is 1.46.0, _but_ if you don't need to use the `async-std-runtime`
182+
feature, you can use rust 1.45.0.
183+
> `async-std` depends on `socket2` which fails to compile under 1.45.0.

src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ pub mod doc_test {
134134
};
135135
}
136136

137-
#[cfg(all(feature = "async-std-runtime", feature = "attributes"))]
137+
#[cfg(all(
138+
feature = "async-std-runtime",
139+
feature = "tokio-runtime",
140+
feature = "attributes"
141+
))]
138142
doctest!("../README.md", readme_md);
139143
}
140144

0 commit comments

Comments
 (0)