Function pyo3_asyncio::tokio::local_future_into_py_with_loop[][src]

pub fn local_future_into_py_with_loop<'p, F>(
    event_loop: &'p PyAny,
    fut: F
) -> PyResult<&PyAny> where
    F: Future<Output = PyResult<PyObject>> + 'static, 
Expand description

Convert a !Send Rust Future into a Python awaitable

Arguments

  • event_loop - The Python event loop that the awaitable should be attached to
  • fut - The Rust future to be converted

Examples

use std::{rc::Rc, time::Duration};

use pyo3::prelude::*;

/// Awaitable non-send sleep function
#[pyfunction]
fn sleep_for(py: Python, secs: u64) -> PyResult<&PyAny> {
    // Rc is non-send so it cannot be passed into pyo3_asyncio::tokio::future_into_py
    let secs = Rc::new(secs);

    pyo3_asyncio::tokio::local_future_into_py_with_loop(
        pyo3_asyncio::tokio::get_current_loop(py)?,
        async move {
            tokio::time::sleep(Duration::from_secs(*secs)).await;
            Python::with_gil(|py| Ok(py.None()))
        }
    )
}

#[pyo3_asyncio::tokio::main]
async fn main() -> PyResult<()> {
    let event_loop = Python::with_gil(|py| -> PyResult<PyObject> {
        Ok(pyo3_asyncio::tokio::get_current_loop(py)?.into())
    })?;

    // the main coroutine is running in a Send context, so we cannot use LocalSet here. Instead
    // we use spawn_blocking in order to use LocalSet::block_on
    tokio::task::spawn_blocking(move || {
        // LocalSet allows us to work with !Send futures within tokio. Without it, any calls to
        // pyo3_asyncio::tokio::local_future_into_py will panic.
        tokio::task::LocalSet::new().block_on(
            pyo3_asyncio::tokio::get_runtime(),  
            pyo3_asyncio::tokio::scope_local(event_loop, async {
                Python::with_gil(|py| {
                    let py_future = sleep_for(py, 1)?;
                    pyo3_asyncio::tokio::into_future(py_future)
                })?
                .await?;

                Ok(())
            })
        )
    }).await.unwrap()
}