use std::any::Any; use std::panic::{AssertUnwindSafe, catch_unwind}; use pyo3::exceptions::PyValueError; use pyo3::panic::PanicException; use pyo3::prelude::*; use serde::Serialize; use serde::de::DeserializeOwned; pub fn from_py(value: &Bound<'_, PyAny>) -> PyResult where T: DeserializeOwned, { pythonize::depythonize(value).map_err(|error| PyValueError::new_err(error.to_string())) } pub fn to_py(py: Python<'_>, value: &T) -> PyResult> where T: Serialize + ?Sized, { pythonize::pythonize(py, value) .map(Bound::unbind) .map_err(|error| PyValueError::new_err(error.to_string())) } pub struct Pythonized(pub T); impl<'py, T> IntoPyObject<'py> for Pythonized where T: Serialize, { type Target = PyAny; type Output = Bound<'py, PyAny>; type Error = PyErr; fn into_pyobject(self, py: Python<'py>) -> PyResult { catch_unwind(AssertUnwindSafe(|| pythonize::pythonize(py, &self.0))) .map_err(panic_to_pyerr)? .map_err(|error| PyValueError::new_err(error.to_string())) } } pub fn panic_to_pyerr(payload: Box) -> PyErr { let message = payload .downcast_ref::() .map(String::as_str) .or_else(|| payload.downcast_ref::<&str>().copied()) .unwrap_or("panic from Rust code"); PanicException::new_err(message.to_string()) } #[cfg(test)] mod tests { use serde::Serializer; use super::*; struct PanickingSerializer; impl Serialize for PanickingSerializer { fn serialize(&self, _serializer: S) -> Result where S: Serializer, { panic!("serializer panicked") } } #[test] fn pythonized_converts_on_the_attached_thread() { Python::initialize(); Python::attach(|py| { let value: Vec = Pythonized(vec![1, 2, 3]) .into_pyobject(py) .and_then(|value| value.extract()) .expect("value should convert"); assert_eq!(value, vec![1, 2, 3]); }); } #[test] fn pythonized_maps_serializer_panics_to_a_base_exception() { Python::initialize(); Python::attach(|py| { let error = Pythonized(PanickingSerializer) .into_pyobject(py) .expect_err("serializer panic should become a Python exception"); assert!(error.is_instance_of::(py)); assert_eq!(error.to_string(), "PanicException: serializer panicked"); }); } }