-
Notifications
You must be signed in to change notification settings - Fork 22
Description
In the current architecture, to support specific Rust runtimes, each runtime interface requires explicitly defining a free function in its respective module that calls the identically named function in the generic module, passing the corresponding runtime generic parameter R.
// src/async_std.rs
pub fn future_into_py<F, T>(py: Python, fut: F) -> PyResult<Bound<PyAny>>
where
F: Future<Output = PyResult<T>> + Send + 'static,
T: for<'py> IntoPyObject<'py> + Send + 'static,
{
generic::future_into_py::<AsyncStdRuntime, _, T>(py, fut)
}
// src/generic.rs
pub fn future_into_py<R, F, T>(py: Python, fut: F) -> PyResult<Bound<PyAny>>
where
R: Runtime + ContextExt,
F: Future<Output = PyResult<T>> + Send + 'static,
T: for<'py> IntoPyObject<'py> + Send + 'static,
{
future_into_py_with_locals::<R, F, T>(py, get_current_locals::<R>(py)?, fut)
}This design results in significant code duplication across runtime modules, differing only in the runtime generic parameter R passed. Additionally, supporting new runtimes requires copying this code. To eliminate this duplication, I propose using default trait implementations to encapsulate the free functions within the generic module.
pub trait HybridRuntimeMethods<R> {
...
fn future_into_py<F, T>(py: Python, fut: F) -> PyResult<Bound<PyAny>>
where
R: Runtime + ContextExt,
F: Future<Output = PyResult<T>> + Send + 'static,
T: for<'py> IntoPyObject<'py> + Send + 'static,
{
Self::future_into_py_with_locals::<F, T>(py, Self::get_current_locals(py)?, fut)
}
...
}
pub struct HybridRuntime<R>(PhantomData<R>);
impl<R> HybridRuntimeMethods<R> for HybridRuntime<R> {}By using the blanket implementation impl<R> HybridRuntimeMethods<R> for HybridRuntime<R> {} to provide the default implementation, runtime modules no longer need to define these functions explicitly. They can be used directly as shown below:
use pyo3::{prelude::*, wrap_pyfunction};
use pyo3_async_runtimes::generic::HybridRuntimeMethods;
type HybridRuntime = pyo3_async_runtimes::generic::HybridRuntime<async_std_runtime::AsyncStdRuntime>;
#[pyfunction]
fn rust_sleep(py: Python) -> PyResult<Bound<PyAny>> {
HybridRuntime::future_into_py(py, async {
println!("sleeping for 1s");
async_std::task::sleep(std::time::Duration::from_secs(1)).await;
println!("done");
Ok(())
})
}
#[pymodule]
fn async_std_rs_sleep(py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(rust_sleep, m)?)?;
Ok(())
}