How to call Python's async callback function in Rust?

⚓ Rust    📅 2025-07-28    👤 surdeus    👁️ 11      

surdeus

Warning

This post was published 128 days ago. The information described in this article may have changed.

Shows RuntimeError: no running event loop

    pub fn start_notify<'py>(
        &self,
        py: Python<'py>,
        character: Bound<'py, PyString>,
        callback: PyObject, // Py<PyFunction>,
    ) -> PyResult<Bound<'py, PyAny>> {
        let character = character.extract::<&str>()?;
        let uuid = Uuid::try_from(character)
            .map_err(|e| PyValueError::new_err(e.to_string()))?;
        let device = self.device.clone();
        let context = self.context.clone();
        pyo3_async_runtimes::tokio::future_into_py(py, async move {
            let character = device
                .characteristic(uuid)
                .await
                .map_err(|e| PyRuntimeError::new_err(e.to_string()))?
                .ok_or(PyValueError::new_err(format!(
                    "Characteristic not found: {}",
                    uuid
                )))?;

            let mut stream = character
                .subscribe()
                .await
                .map_err(|e| PyRuntimeError::new_err(e.to_string()))?;

            let handle = spawn(async move {
                while let Some(data) = stream.next().await {
                    rsutil::info!("received: {}", data.len());
                    match Python::with_gil(|py| {
                        let asyncio = py.import("asyncio")?;
                        let has_loop = asyncio.call_method0("get_event_loop").is_ok();
                        if !has_loop {
                            let new_loop = asyncio.call_method0("new_event_loop")?;
                            asyncio.call_method1("set_event_loop", (new_loop, ))?;
                        }

                        let py_data = PyByteArray::new(py, &data);
                        let coroutine = callback.call1(py, (py_data, ))?;
                        pyo3_async_runtimes::tokio::into_future(
                            asyncio.call_method1("ensure_future", (coroutine, ))?
                        )
                    }) {
                        Ok(fut) => if let Err(e) = fut.await {
                            Python::with_gil(|py| {
                                e.display(py);
                            });
                        },
                        Err(e) => Python::with_gil(|py| {
                            e.display(py);
                        }),
                    }
                }
            });

            context.lock().await.replace(Context {
                notify_character: uuid,
                subscribe_task: handle,
            });

            Ok(())
        })
    }

1 post - 1 participant

Read full topic

🏷️ Rust_feed