diff -r fbbf9ed47913 -r 44d859269132 src/direct_product.rs --- a/src/direct_product.rs Sun May 18 19:56:28 2025 -0500 +++ b/src/direct_product.rs Sun May 18 23:15:50 2025 -0500 @@ -566,3 +566,63 @@ Pair(self.0.dual_origin(), self.1.dual_origin()) } } + +#[cfg(feature = "pyo3")] +mod python { + use super::Pair; + use pyo3::conversion::FromPyObject; + use pyo3::types::{PyAny, PyTuple}; + use pyo3::{Bound, IntoPyObject, PyErr, PyResult, Python}; + + impl<'py, A, B> IntoPyObject<'py> for Pair + where + A: IntoPyObject<'py>, + B: IntoPyObject<'py>, + { + type Target = PyTuple; + type Error = PyErr; + type Output = Bound<'py, Self::Target>; + + fn into_pyobject(self, py: Python<'py>) -> Result { + (self.0, self.1).into_pyobject(py) + } + } + + impl<'a, 'py, A, B> IntoPyObject<'py> for &'a mut Pair + where + &'a mut A: IntoPyObject<'py>, + &'a mut B: IntoPyObject<'py>, + { + type Target = PyTuple; + type Error = PyErr; + type Output = Bound<'py, Self::Target>; + + fn into_pyobject(self, py: Python<'py>) -> Result { + (&mut self.0, &mut self.1).into_pyobject(py) + } + } + + impl<'a, 'py, A, B> IntoPyObject<'py> for &'a Pair + where + &'a A: IntoPyObject<'py>, + &'a B: IntoPyObject<'py>, + { + type Target = PyTuple; + type Error = PyErr; + type Output = Bound<'py, Self::Target>; + + fn into_pyobject(self, py: Python<'py>) -> Result { + (&self.0, &self.1).into_pyobject(py) + } + } + + impl<'py, A, B> FromPyObject<'py> for Pair + where + A: Clone + FromPyObject<'py>, + B: Clone + FromPyObject<'py>, + { + fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + FromPyObject::extract_bound(ob).map(|(a, b)| Pair(a, b)) + } + } +}