Skip to content

Commit

Permalink
Merge pull request #1678 from sansyrox/main
Browse files Browse the repository at this point in the history
Add documentation to call async python from rust
  • Loading branch information
davidhewitt committed Aug 12, 2021
2 parents 4c734ef + aac3d1d commit 0b269c4
Showing 1 changed file with 58 additions and 1 deletion.
59 changes: 58 additions & 1 deletion guide/src/ecosystem/async-await.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,10 @@ Normally in Python, that something special is the `await` keyword, but in order
coroutine in Rust, we first need to convert it into Rust's version of a `coroutine`: a `Future`.
That's where `pyo3-asyncio` comes in.
[`pyo3_asyncio::into_future`](https://docs.rs/pyo3-asyncio/latest/pyo3_asyncio/fn.into_future.html)
performs this conversion for us:
performs this conversion for us.

The following example uses `into_future` to call the `py_sleep` function shown above and then await the
coroutine object returned from the call:

```rust
use pyo3::prelude::*;
Expand All @@ -225,6 +227,61 @@ async fn main() -> PyResult<()> {
}
```

Alternatively, the below example shows how to write a `#[pyfunction]` which uses `into_future` to receive and await
a coroutine argument:

```rust
#[pyfunction]
fn await_coro(coro: &PyAny) -> PyResult<()> {
// convert the coroutine into a Rust future using the
// async_std runtime
let f = pyo3_asyncio::async_std::into_future(coro)?;

pyo3_asyncio::async_std::run_until_complete(coro.py(), async move {
// await the future
f.await?;
Ok(())
})
}
```

This could be called from Python as:

```python
import asyncio

async def py_sleep():
asyncio.sleep(1)

await_coro(py_sleep())
```

If for you wanted to pass a callable function to the `#[pyfunction]` instead, (i.e. the last line becomes `await_coro(py_sleep))`, then the above example needs to be tweaked to first call the callable to get the coroutine:

```rust
#[pyfunction]
fn await_coro(callable: &PyAny) -> PyResult<()> {
// get the coroutine by calling the callable
let coro = callable.call0()?;

// convert the coroutine into a Rust future using the
// async_std runtime
let f = pyo3_asyncio::async_std::into_future(coro)?;

pyo3_asyncio::async_std::run_until_complete(coro.py(), async move {
// await the future
f.await?;
Ok(())
})
}
```

This can be particularly helpful where you need to repeatedly create and await a coroutine. Trying to await the same coroutine multiple times will raise an error:

```python
RuntimeError: cannot reuse already awaited coroutine
```

> If you're interested in learning more about `coroutines` and `awaitables` in general, check out the
> [Python 3 `asyncio` docs](https://docs.python.org/3/library/asyncio-task.html) for more information.
Expand Down

0 comments on commit 0b269c4

Please sign in to comment.