feat: add async support to python binding (#185)

* feat: Add async support to pythong binding

* fix: return NodeResult instead of panic when future missing
This commit is contained in:
Scott Thompson
2024-07-04 12:10:20 +02:00
committed by GitHub
parent 59371349c3
commit 4c9c26b429
6 changed files with 139 additions and 10 deletions
+26 -5
View File
@@ -1,6 +1,7 @@
use anyhow::anyhow;
use pyo3::types::PyDict;
use pyo3::{PyObject, Python};
use pyo3::{PyObject, PyResult, Python};
use pyo3_asyncio::tokio::into_future;
use pythonize::depythonize;
use zen_engine::handler::custom_node_adapter::{CustomNodeAdapter, CustomNodeRequest};
@@ -23,20 +24,40 @@ impl From<Option<PyObject>> for PyCustomNode {
}
}
fn extract_custom_node_response(result: PyObject, py: Python<'_>) -> NodeResult {
let dict = result.extract::<&PyDict>(py)?;
let response: NodeResponse = depythonize(dict)?;
Ok(response)
}
impl CustomNodeAdapter for PyCustomNode {
async fn handle(&self, request: CustomNodeRequest<'_>) -> NodeResult {
let Some(callable) = &self.0 else {
return Err(anyhow!("Custom node handler not provided"));
};
let content: NodeResponse = Python::with_gil(|py| {
let (future, result) = Python::with_gil(|py| -> PyResult<_> {
let req = PyNodeRequest::from_request(py, request)?;
let result = callable.call1(py, (req,))?;
let dict = result.extract::<&PyDict>(py)?;
depythonize(dict)
let is_coroutine = result.getattr(py, "__await__").is_ok();
if is_coroutine {
return Ok((Some(into_future(result.as_ref(py))), None));
}
Ok((None, Some(extract_custom_node_response(result, py))))
})?;
if let Some(result) = result {
return result;
}
let result = future
.ok_or_else(|| anyhow!("Future or result must be present"))??
.await?;
let content = Python::with_gil(|py| -> PyResult<_> {
Ok(extract_custom_node_response(result, py))
})??;
Ok(content)
}
}