pyo3/conversions/
either.rs#![cfg(feature = "either")]
#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"either\"] }")]
#[cfg(feature = "experimental-inspect")]
use crate::inspect::types::TypeInfo;
use crate::{
exceptions::PyTypeError, types::any::PyAnyMethods, Bound, FromPyObject, IntoPyObject,
IntoPyObjectExt, PyAny, PyErr, PyObject, PyResult, Python,
};
#[allow(deprecated)]
use crate::{IntoPy, ToPyObject};
use either::Either;
#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
#[allow(deprecated)]
impl<L, R> IntoPy<PyObject> for Either<L, R>
where
L: IntoPy<PyObject>,
R: IntoPy<PyObject>,
{
#[inline]
fn into_py(self, py: Python<'_>) -> PyObject {
match self {
Either::Left(l) => l.into_py(py),
Either::Right(r) => r.into_py(py),
}
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
impl<'py, L, R> IntoPyObject<'py> for Either<L, R>
where
L: IntoPyObject<'py>,
R: IntoPyObject<'py>,
{
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
match self {
Either::Left(l) => l.into_bound_py_any(py),
Either::Right(r) => r.into_bound_py_any(py),
}
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
impl<'a, 'py, L, R> IntoPyObject<'py> for &'a Either<L, R>
where
&'a L: IntoPyObject<'py>,
&'a R: IntoPyObject<'py>,
{
type Target = PyAny;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
match self {
Either::Left(l) => l.into_bound_py_any(py),
Either::Right(r) => r.into_bound_py_any(py),
}
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
#[allow(deprecated)]
impl<L, R> ToPyObject for Either<L, R>
where
L: ToPyObject,
R: ToPyObject,
{
#[inline]
fn to_object(&self, py: Python<'_>) -> PyObject {
match self {
Either::Left(l) => l.to_object(py),
Either::Right(r) => r.to_object(py),
}
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "either")))]
impl<'py, L, R> FromPyObject<'py> for Either<L, R>
where
L: FromPyObject<'py>,
R: FromPyObject<'py>,
{
#[inline]
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
if let Ok(l) = obj.extract::<L>() {
Ok(Either::Left(l))
} else if let Ok(r) = obj.extract::<R>() {
Ok(Either::Right(r))
} else {
let err_msg = format!(
"failed to convert the value to 'Union[{}, {}]'",
std::any::type_name::<L>(),
std::any::type_name::<R>()
);
Err(PyTypeError::new_err(err_msg))
}
}
#[cfg(feature = "experimental-inspect")]
fn type_input() -> TypeInfo {
TypeInfo::union_of(&[L::type_input(), R::type_input()])
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use crate::exceptions::PyTypeError;
use crate::{IntoPyObject, Python};
use crate::types::PyAnyMethods;
use either::Either;
#[test]
fn test_either_conversion() {
type E = Either<i32, String>;
type E1 = Either<i32, f32>;
type E2 = Either<f32, i32>;
Python::with_gil(|py| {
let l = E::Left(42);
let obj_l = (&l).into_pyobject(py).unwrap();
assert_eq!(obj_l.extract::<i32>().unwrap(), 42);
assert_eq!(obj_l.extract::<E>().unwrap(), l);
let r = E::Right("foo".to_owned());
let obj_r = (&r).into_pyobject(py).unwrap();
assert_eq!(obj_r.extract::<Cow<'_, str>>().unwrap(), "foo");
assert_eq!(obj_r.extract::<E>().unwrap(), r);
let obj_s = "foo".into_pyobject(py).unwrap();
let err = obj_s.extract::<E1>().unwrap_err();
assert!(err.is_instance_of::<PyTypeError>(py));
assert_eq!(
err.to_string(),
"TypeError: failed to convert the value to 'Union[i32, f32]'"
);
let obj_i = 42i32.into_pyobject(py).unwrap();
assert_eq!(obj_i.extract::<E1>().unwrap(), E1::Left(42));
assert_eq!(obj_i.extract::<E2>().unwrap(), E2::Left(42.0));
let obj_f = 42.0f64.into_pyobject(py).unwrap();
assert_eq!(obj_f.extract::<E1>().unwrap(), E1::Right(42.0));
assert_eq!(obj_f.extract::<E2>().unwrap(), E2::Left(42.0));
});
}
}