use crate::err::PyResult;
use crate::impl_::pyclass::ExtractPyClassWithClone;
#[cfg(feature = "experimental-inspect")]
use crate::inspect::types::TypeInfo;
#[cfg(feature = "experimental-inspect")]
use crate::inspect::{type_hint_identifier, type_hint_subscript, PyStaticExpr};
use crate::pyclass::boolean_struct::False;
use crate::pyclass::{PyClassGuardError, PyClassGuardMutError};
#[cfg(feature = "experimental-inspect")]
use crate::types::PyList;
use crate::types::PyTuple;
use crate::{
Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyClassGuard, PyErr, PyRef, PyRefMut,
PyTypeCheck, Python,
};
use std::convert::Infallible;
use std::marker::PhantomData;
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be converted to a Python object",
note = "`IntoPyObject` is automatically implemented by the `#[pyclass]` macro",
note = "if you do not wish to have a corresponding Python type, implement it manually",
note = "if you do not own `{Self}` you can perform a manual conversion to one of the types in `pyo3::types::*`"
)]
pub trait IntoPyObject<'py>: Sized {
type Target;
type Output: BoundObject<'py, Self::Target>;
type Error: Into<PyErr>;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = type_hint_identifier!("_typeshed", "Incomplete");
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error>;
#[cfg(feature = "experimental-inspect")]
fn type_output() -> TypeInfo {
TypeInfo::Any
}
#[doc(hidden)]
fn owned_sequence_into_pyobject<I>(
iter: I,
py: Python<'py>,
_: private::Token,
) -> Result<Bound<'py, PyAny>, PyErr>
where
I: IntoIterator<Item = Self> + AsRef<[Self]>,
I::IntoIter: ExactSizeIterator<Item = Self>,
{
let mut iter = iter.into_iter().map(|e| e.into_bound_py_any(py));
let list = crate::types::list::try_new_from_iter(py, &mut iter);
list.map(Bound::into_any)
}
#[doc(hidden)]
fn borrowed_sequence_into_pyobject<I>(
iter: I,
py: Python<'py>,
_: private::Token,
) -> Result<Bound<'py, PyAny>, PyErr>
where
Self: private::Reference,
I: IntoIterator<Item = Self> + AsRef<[<Self as private::Reference>::BaseType]>,
I::IntoIter: ExactSizeIterator<Item = Self>,
{
let mut iter = iter.into_iter().map(|e| e.into_bound_py_any(py));
let list = crate::types::list::try_new_from_iter(py, &mut iter);
list.map(Bound::into_any)
}
#[cfg(feature = "experimental-inspect")]
#[doc(hidden)]
const SEQUENCE_OUTPUT_TYPE: PyStaticExpr =
type_hint_subscript!(PyList::TYPE_HINT, Self::OUTPUT_TYPE);
}
pub(crate) mod private {
pub struct Token;
pub trait Reference {
type BaseType;
}
impl<T> Reference for &'_ T {
type BaseType = T;
}
}
impl<'py, T: PyTypeCheck> IntoPyObject<'py> for Bound<'py, T> {
type Target = T;
type Output = Bound<'py, Self::Target>;
type Error = Infallible;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(self)
}
}
impl<'a, 'py, T: PyTypeCheck> IntoPyObject<'py> for &'a Bound<'py, T> {
type Target = T;
type Output = Borrowed<'a, 'py, Self::Target>;
type Error = Infallible;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(self.as_borrowed())
}
}
impl<'a, 'py, T: PyTypeCheck> IntoPyObject<'py> for Borrowed<'a, 'py, T> {
type Target = T;
type Output = Borrowed<'a, 'py, Self::Target>;
type Error = Infallible;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(self)
}
}
impl<'a, 'py, T: PyTypeCheck> IntoPyObject<'py> for &Borrowed<'a, 'py, T> {
type Target = T;
type Output = Borrowed<'a, 'py, Self::Target>;
type Error = Infallible;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(*self)
}
}
impl<'py, T: PyTypeCheck> IntoPyObject<'py> for Py<T> {
type Target = T;
type Output = Bound<'py, Self::Target>;
type Error = Infallible;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(self.into_bound(py))
}
}
impl<'a, 'py, T: PyTypeCheck> IntoPyObject<'py> for &'a Py<T> {
type Target = T;
type Output = Borrowed<'a, 'py, Self::Target>;
type Error = Infallible;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(self.bind_borrowed(py))
}
}
impl<'a, 'py, T> IntoPyObject<'py> for &&'a T
where
&'a T: IntoPyObject<'py>,
{
type Target = <&'a T as IntoPyObject<'py>>::Target;
type Output = <&'a T as IntoPyObject<'py>>::Output;
type Error = <&'a T as IntoPyObject<'py>>::Error;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr = <&'a T as IntoPyObject<'py>>::OUTPUT_TYPE;
#[inline]
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
(*self).into_pyobject(py)
}
}
mod into_pyobject_ext {
pub trait Sealed {}
impl<'py, T> Sealed for T where T: super::IntoPyObject<'py> {}
}
pub trait IntoPyObjectExt<'py>: IntoPyObject<'py> + into_pyobject_ext::Sealed {
#[inline]
fn into_bound_py_any(self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
match self.into_pyobject(py) {
Ok(obj) => Ok(obj.into_any().into_bound()),
Err(err) => Err(err.into()),
}
}
#[inline]
fn into_py_any(self, py: Python<'py>) -> PyResult<Py<PyAny>> {
match self.into_pyobject(py) {
Ok(obj) => Ok(obj.into_any().unbind()),
Err(err) => Err(err.into()),
}
}
#[inline]
fn into_pyobject_or_pyerr(self, py: Python<'py>) -> PyResult<Self::Output> {
match self.into_pyobject(py) {
Ok(obj) => Ok(obj),
Err(err) => Err(err.into()),
}
}
}
impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {}
pub trait FromPyObject<'a, 'py>: Sized {
type Error: Into<PyErr>;
#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: PyStaticExpr = type_hint_identifier!("_typeshed", "Incomplete");
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error>;
#[cfg(feature = "experimental-inspect")]
fn type_input() -> TypeInfo {
TypeInfo::Any
}
#[doc(hidden)]
#[inline(always)]
fn sequence_extractor(
_obj: Borrowed<'_, 'py, PyAny>,
_: private::Token,
) -> Option<impl FromPyObjectSequence<Target = Self>> {
struct NeverASequence<T>(PhantomData<T>);
impl<T> FromPyObjectSequence for NeverASequence<T> {
type Target = T;
fn to_vec(&self) -> Vec<Self::Target> {
unreachable!()
}
fn to_array<const N: usize>(&self) -> PyResult<[Self::Target; N]> {
unreachable!()
}
}
Option::<NeverASequence<Self>>::None
}
#[cfg(feature = "chrono-local")]
#[inline]
fn as_local_tz(_: private::Token) -> Option<Self> {
None
}
}
mod from_py_object_sequence {
use crate::PyResult;
#[doc(hidden)]
pub trait FromPyObjectSequence {
type Target;
fn to_vec(&self) -> Vec<Self::Target>;
fn to_array<const N: usize>(&self) -> PyResult<[Self::Target; N]>;
}
}
pub(crate) use from_py_object_sequence::FromPyObjectSequence;
pub trait FromPyObjectOwned<'py>: for<'a> FromPyObject<'a, 'py> {}
impl<'py, T> FromPyObjectOwned<'py> for T where T: for<'a> FromPyObject<'a, 'py> {}
impl<'a, 'py, T> FromPyObject<'a, 'py> for T
where
T: PyClass + Clone + ExtractPyClassWithClone,
{
type Error = PyClassGuardError<'a, 'py>;
#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: PyStaticExpr = <T as crate::PyTypeInfo>::TYPE_HINT;
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
Ok(obj.extract::<PyClassGuard<'_, T>>()?.clone())
}
}
impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRef<'py, T>
where
T: PyClass,
{
type Error = PyClassGuardError<'a, 'py>;
#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: PyStaticExpr = <T as crate::PyTypeInfo>::TYPE_HINT;
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
obj.cast::<T>()
.map_err(|e| PyClassGuardError(Some(e)))?
.try_borrow()
.map_err(|_| PyClassGuardError(None))
}
}
impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRefMut<'py, T>
where
T: PyClass<Frozen = False>,
{
type Error = PyClassGuardMutError<'a, 'py>;
#[cfg(feature = "experimental-inspect")]
const INPUT_TYPE: PyStaticExpr = <T as crate::PyTypeInfo>::TYPE_HINT;
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
obj.cast::<T>()
.map_err(|e| PyClassGuardMutError(Some(e)))?
.try_borrow_mut()
.map_err(|_| PyClassGuardMutError(None))
}
}
impl<'py> IntoPyObject<'py> for () {
type Target = PyTuple;
type Output = Bound<'py, Self::Target>;
type Error = Infallible;
#[cfg(feature = "experimental-inspect")]
const OUTPUT_TYPE: PyStaticExpr =
type_hint_subscript!(PyTuple::TYPE_HINT, PyStaticExpr::Tuple { elts: &[] });
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
Ok(PyTuple::empty(py))
}
}
mod test_no_clone {}
#[cfg(test)]
mod tests {
#[test]
#[cfg(feature = "macros")]
fn test_pyclass_skip_from_py_object() {
use crate::{types::PyAnyMethods, FromPyObject, IntoPyObject, PyErr, Python};
#[crate::pyclass(crate = "crate", skip_from_py_object)]
#[derive(Clone)]
struct Foo(i32);
impl<'py> FromPyObject<'_, 'py> for Foo {
type Error = PyErr;
fn extract(obj: crate::Borrowed<'_, 'py, crate::PyAny>) -> Result<Self, Self::Error> {
if let Ok(obj) = obj.cast::<Self>() {
Ok(obj.borrow().clone())
} else {
obj.extract::<i32>().map(Self)
}
}
}
Python::attach(|py| {
let foo1 = 42i32.into_pyobject(py)?;
assert_eq!(foo1.extract::<Foo>()?.0, 42);
let foo2 = Foo(0).into_pyobject(py)?;
assert_eq!(foo2.extract::<Foo>()?.0, 0);
Ok::<_, PyErr>(())
})
.unwrap();
}
#[test]
#[cfg(feature = "macros")]
fn test_pyclass_from_py_object() {
use crate::{types::PyAnyMethods, IntoPyObject, PyErr, Python};
#[crate::pyclass(crate = "crate", from_py_object)]
#[derive(Clone)]
struct Foo(i32);
Python::attach(|py| {
let foo1 = 42i32.into_pyobject(py)?;
assert!(foo1.extract::<Foo>().is_err());
let foo2 = Foo(0).into_pyobject(py)?;
assert_eq!(foo2.extract::<Foo>()?.0, 0);
Ok::<_, PyErr>(())
})
.unwrap();
}
}