pyo3_async/
lib.rs

1//! PyO3 bindings to various Python asynchronous frameworks.
2use std::{
3    future::Future,
4    pin::Pin,
5    task::{Context, Poll},
6};
7
8use futures::Stream;
9use pyo3::prelude::*;
10
11#[cfg(feature = "allow-threads")]
12mod allow_threads;
13mod async_generator;
14pub mod asyncio;
15mod coroutine;
16pub mod sniffio;
17pub mod trio;
18mod utils;
19
20#[cfg(feature = "allow-threads")]
21pub use allow_threads::{AllowThreads, AllowThreadsExt};
22#[cfg(feature = "macros")]
23pub use pyo3_async_macros::{pyfunction, pymethods};
24
25/// GIL-bound [`Future`].
26///
27/// Provided with a blanket implementation for [`Future`]. GIL is maintained during polling
28/// operation. To release the GIL, see [`AllowThreads`].
29pub trait PyFuture: Send {
30    /// GIL-bound [`Future::poll`].
31    fn poll_py(self: Pin<&mut Self>, py: Python, cx: &mut Context) -> Poll<PyResult<PyObject>>;
32}
33
34impl<F, T, E> PyFuture for F
35where
36    F: Future<Output = Result<T, E>> + Send,
37    T: IntoPy<PyObject> + Send,
38    E: Send,
39    PyErr: From<E>,
40{
41    fn poll_py(self: Pin<&mut Self>, py: Python, cx: &mut Context) -> Poll<PyResult<PyObject>> {
42        let poll = self.poll(cx);
43        poll.map_ok(|ok| ok.into_py(py)).map_err(PyErr::from)
44    }
45}
46
47/// GIL-bound [`Stream`].
48///
49/// Provided with a blanket implementation for [`Stream`]. GIL is maintained during polling
50/// operation. To release the GIL, see [`AllowThreads`].
51///
52/// [`Stream`]: https://docs.rs/futures/latest/futures/stream/trait.Stream.html
53pub trait PyStream: Send {
54    /// GIL-bound [`Stream::poll_next`].
55    fn poll_next_py(
56        self: Pin<&mut Self>,
57        py: Python,
58        cx: &mut Context,
59    ) -> Poll<Option<PyResult<PyObject>>>;
60}
61
62impl<S, T, E> PyStream for S
63where
64    S: Stream<Item = Result<T, E>> + Send,
65    T: IntoPy<PyObject> + Send,
66    E: Send,
67    PyErr: From<E>,
68{
69    fn poll_next_py(
70        self: Pin<&mut Self>,
71        py: Python,
72        cx: &mut Context,
73    ) -> Poll<Option<PyResult<PyObject>>> {
74        let poll = self.poll_next(cx);
75        poll.map_ok(|ok| ok.into_py(py)).map_err(PyErr::from)
76    }
77}
78
79/// Callback for Python coroutine `throw` method (see [`asyncio::Coroutine::new`]) and
80/// async generator `athrow` method (see [`asyncio::AsyncGenerator::new`]).
81pub type ThrowCallback = Box<dyn FnMut(Python, Option<PyErr>) + Send>;