pyo3/conversion.rs
1//! Defines conversions between Rust and Python types.
2use crate::err::PyResult;
3use crate::impl_::pyclass::ExtractPyClassWithClone;
4#[cfg(feature = "experimental-inspect")]
5use crate::inspect::{type_hint_identifier, type_hint_subscript, PyStaticExpr};
6use crate::pyclass::boolean_struct::False;
7use crate::pyclass::{PyClassGuardError, PyClassGuardMutError};
8use crate::types::PyList;
9use crate::types::PyTuple;
10use crate::{
11 Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyClassGuard, PyErr, PyRef, PyRefMut,
12 PyTypeCheck, Python,
13};
14use std::convert::Infallible;
15use std::marker::PhantomData;
16
17/// Defines a conversion from a Rust type to a Python object, which may fail.
18///
19/// This trait has `#[derive(IntoPyObject)]` to automatically implement it for simple types and
20/// `#[derive(IntoPyObjectRef)]` to implement the same for references.
21///
22/// It functions similarly to std's [`TryInto`] trait, but requires a [`Python<'py>`] token
23/// as an argument.
24///
25/// The [`into_pyobject`][IntoPyObject::into_pyobject] method is designed for maximum flexibility and efficiency; it
26/// - allows for a concrete Python type to be returned (the [`Target`][IntoPyObject::Target] associated type)
27/// - allows for the smart pointer containing the Python object to be either `Bound<'py, Self::Target>` or `Borrowed<'a, 'py, Self::Target>`
28/// to avoid unnecessary reference counting overhead
29/// - allows for a custom error type to be returned in the event of a conversion error to avoid
30/// unnecessarily creating a Python exception
31///
32/// # See also
33///
34/// - The [`IntoPyObjectExt`] trait, which provides convenience methods for common usages of
35/// `IntoPyObject` which erase type information and convert errors to `PyErr`.
36#[diagnostic::on_unimplemented(
37 message = "`{Self}` cannot be converted to a Python object",
38 note = "`IntoPyObject` is automatically implemented by the `#[pyclass]` macro",
39 note = "if you do not wish to have a corresponding Python type, implement it manually",
40 note = "if you do not own `{Self}` you can perform a manual conversion to one of the types in `pyo3::types::*`"
41)]
42pub trait IntoPyObject<'py>: Sized {
43 /// The Python output type
44 type Target;
45 /// The smart pointer type to use.
46 ///
47 /// This will usually be [`Bound<'py, Target>`], but in special cases [`Borrowed<'a, 'py, Target>`] can be
48 /// used to minimize reference counting overhead.
49 type Output: BoundObject<'py, Self::Target>;
50 /// The type returned in the event of a conversion error.
51 type Error: Into<PyErr>;
52
53 /// Extracts the type hint information for this type when it appears as a return value.
54 ///
55 /// For example, `Vec<u32>` would return `List[int]`.
56 /// The default implementation returns `Any`, which is correct for any type.
57 ///
58 /// For most types, the return value for this method will be identical to that of [`FromPyObject::INPUT_TYPE`].
59 /// It may be different for some types, such as `Dict`, to allow duck-typing: functions return `Dict` but take `Mapping` as argument.
60 #[cfg(feature = "experimental-inspect")]
61 const OUTPUT_TYPE: PyStaticExpr = type_hint_identifier!("_typeshed", "Incomplete");
62
63 /// Performs the conversion.
64 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error>;
65
66 /// Converts sequence of Self into a Python object. Used to specialize `Vec<u8>`, `[u8; N]`
67 /// and `SmallVec<[u8; N]>` as a sequence of bytes into a `bytes` object.
68 #[doc(hidden)]
69 fn owned_sequence_into_pyobject<I>(
70 iter: I,
71 py: Python<'py>,
72 _: private::Token,
73 ) -> Result<Bound<'py, PyAny>, PyErr>
74 where
75 I: IntoIterator<Item = Self> + AsRef<[Self]>,
76 I::IntoIter: ExactSizeIterator<Item = Self>,
77 {
78 Ok(PyList::new(py, iter)?.into_any())
79 }
80
81 /// Converts sequence of Self into a Python object. Used to specialize `&[u8]` and `Cow<[u8]>`
82 /// as a sequence of bytes into a `bytes` object.
83 #[doc(hidden)]
84 fn borrowed_sequence_into_pyobject<I>(
85 iter: I,
86 py: Python<'py>,
87 _: private::Token,
88 ) -> Result<Bound<'py, PyAny>, PyErr>
89 where
90 Self: private::Reference,
91 I: IntoIterator<Item = Self> + AsRef<[<Self as private::Reference>::BaseType]>,
92 I::IntoIter: ExactSizeIterator<Item = Self>,
93 {
94 Ok(PyList::new(py, iter)?.into_any())
95 }
96
97 /// The output type of [`IntoPyObject::owned_sequence_into_pyobject`] and [`IntoPyObject::borrowed_sequence_into_pyobject`]
98 #[cfg(feature = "experimental-inspect")]
99 #[doc(hidden)]
100 const SEQUENCE_OUTPUT_TYPE: PyStaticExpr =
101 type_hint_subscript!(PyList::TYPE_HINT, Self::OUTPUT_TYPE);
102}
103
104pub(crate) mod private {
105 pub struct Token;
106
107 pub trait Reference {
108 type BaseType;
109 }
110
111 impl<T> Reference for &'_ T {
112 type BaseType = T;
113 }
114}
115
116impl<'py, T: PyTypeCheck> IntoPyObject<'py> for Bound<'py, T> {
117 type Target = T;
118 type Output = Bound<'py, Self::Target>;
119 type Error = Infallible;
120
121 #[cfg(feature = "experimental-inspect")]
122 const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
123
124 fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
125 Ok(self)
126 }
127}
128
129impl<'a, 'py, T: PyTypeCheck> IntoPyObject<'py> for &'a Bound<'py, T> {
130 type Target = T;
131 type Output = Borrowed<'a, 'py, Self::Target>;
132 type Error = Infallible;
133
134 #[cfg(feature = "experimental-inspect")]
135 const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
136
137 fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
138 Ok(self.as_borrowed())
139 }
140}
141
142impl<'a, 'py, T: PyTypeCheck> IntoPyObject<'py> for Borrowed<'a, 'py, T> {
143 type Target = T;
144 type Output = Borrowed<'a, 'py, Self::Target>;
145 type Error = Infallible;
146
147 #[cfg(feature = "experimental-inspect")]
148 const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
149
150 fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
151 Ok(self)
152 }
153}
154
155impl<'a, 'py, T: PyTypeCheck> IntoPyObject<'py> for &Borrowed<'a, 'py, T> {
156 type Target = T;
157 type Output = Borrowed<'a, 'py, Self::Target>;
158 type Error = Infallible;
159
160 #[cfg(feature = "experimental-inspect")]
161 const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
162
163 fn into_pyobject(self, _py: Python<'py>) -> Result<Self::Output, Self::Error> {
164 Ok(*self)
165 }
166}
167
168impl<'py, T: PyTypeCheck> IntoPyObject<'py> for Py<T> {
169 type Target = T;
170 type Output = Bound<'py, Self::Target>;
171 type Error = Infallible;
172
173 #[cfg(feature = "experimental-inspect")]
174 const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
175
176 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
177 Ok(self.into_bound(py))
178 }
179}
180
181impl<'a, 'py, T: PyTypeCheck> IntoPyObject<'py> for &'a Py<T> {
182 type Target = T;
183 type Output = Borrowed<'a, 'py, Self::Target>;
184 type Error = Infallible;
185
186 #[cfg(feature = "experimental-inspect")]
187 const OUTPUT_TYPE: PyStaticExpr = T::TYPE_HINT;
188
189 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
190 Ok(self.bind_borrowed(py))
191 }
192}
193
194impl<'a, 'py, T> IntoPyObject<'py> for &&'a T
195where
196 &'a T: IntoPyObject<'py>,
197{
198 type Target = <&'a T as IntoPyObject<'py>>::Target;
199 type Output = <&'a T as IntoPyObject<'py>>::Output;
200 type Error = <&'a T as IntoPyObject<'py>>::Error;
201
202 #[cfg(feature = "experimental-inspect")]
203 const OUTPUT_TYPE: PyStaticExpr = <&'a T as IntoPyObject<'py>>::OUTPUT_TYPE;
204
205 #[inline]
206 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
207 (*self).into_pyobject(py)
208 }
209}
210
211mod into_pyobject_ext {
212 pub trait Sealed {}
213 impl<'py, T> Sealed for T where T: super::IntoPyObject<'py> {}
214}
215
216/// Convenience methods for common usages of [`IntoPyObject`]. Every type that implements
217/// [`IntoPyObject`] also implements this trait.
218///
219/// These methods:
220/// - Drop type information from the output, returning a `PyAny` object.
221/// - Always convert the `Error` type to `PyErr`, which may incur a performance penalty but it
222/// more convenient in contexts where the `?` operator would produce a `PyErr` anyway.
223pub trait IntoPyObjectExt<'py>: IntoPyObject<'py> + into_pyobject_ext::Sealed {
224 /// Converts `self` into an owned Python object, dropping type information.
225 #[inline]
226 fn into_bound_py_any(self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
227 match self.into_pyobject(py) {
228 Ok(obj) => Ok(obj.into_any().into_bound()),
229 Err(err) => Err(err.into()),
230 }
231 }
232
233 /// Converts `self` into an owned Python object, dropping type information and unbinding it
234 /// from the `'py` lifetime.
235 #[inline]
236 fn into_py_any(self, py: Python<'py>) -> PyResult<Py<PyAny>> {
237 match self.into_pyobject(py) {
238 Ok(obj) => Ok(obj.into_any().unbind()),
239 Err(err) => Err(err.into()),
240 }
241 }
242
243 /// Converts `self` into a Python object.
244 ///
245 /// This is equivalent to calling [`into_pyobject`][IntoPyObject::into_pyobject] followed
246 /// with `.map_err(Into::into)` to convert the error type to [`PyErr`]. This is helpful
247 /// for generic code which wants to make use of the `?` operator.
248 #[inline]
249 fn into_pyobject_or_pyerr(self, py: Python<'py>) -> PyResult<Self::Output> {
250 match self.into_pyobject(py) {
251 Ok(obj) => Ok(obj),
252 Err(err) => Err(err.into()),
253 }
254 }
255}
256
257impl<'py, T> IntoPyObjectExt<'py> for T where T: IntoPyObject<'py> {}
258
259/// Extract a type from a Python object.
260///
261///
262/// Normal usage is through the `extract` methods on [`Bound`], [`Borrowed`] and [`Py`], which
263/// forward to this trait.
264///
265/// # Examples
266///
267/// ```rust
268/// use pyo3::prelude::*;
269/// use pyo3::types::PyString;
270///
271/// # fn main() -> PyResult<()> {
272/// Python::attach(|py| {
273/// // Calling `.extract()` on a `Bound` smart pointer
274/// let obj: Bound<'_, PyString> = PyString::new(py, "blah");
275/// let s: String = obj.extract()?;
276/// # assert_eq!(s, "blah");
277///
278/// // Calling `.extract(py)` on a `Py` smart pointer
279/// let obj: Py<PyString> = obj.unbind();
280/// let s: String = obj.extract(py)?;
281/// # assert_eq!(s, "blah");
282/// # Ok(())
283/// })
284/// # }
285/// ```
286///
287/// Note: Depending on the Python version and implementation, some [`FromPyObject`] implementations
288/// may produce a result that borrows into the Python type. This is described by the input lifetime
289/// `'a` of `obj`.
290///
291/// Types that must not borrow from the input can use [`FromPyObjectOwned`] as a restriction. This
292/// is most often the case for collection types. See its documentation for more details.
293///
294/// # How to implement [`FromPyObject`]?
295/// ## `#[derive(FromPyObject)]`
296/// The simplest way to implement [`FromPyObject`] for a custom type is to make use of our derive
297/// macro.
298/// ```rust,no_run
299/// # #![allow(dead_code)]
300/// use pyo3::prelude::*;
301///
302/// #[derive(FromPyObject)]
303/// struct MyObject {
304/// msg: String,
305/// list: Vec<u32>
306/// }
307/// # fn main() {}
308/// ```
309/// By default this will try to extract each field from the Python object by attribute access, but
310/// this can be customized. For more information about the derive macro, its configuration as well
311/// as its working principle for other types, take a look at the [guide].
312///
313/// In case the derive macro is not sufficient or can not be used for some other reason,
314/// [`FromPyObject`] can be implemented manually. In the following types without lifetime parameters
315/// are handled first, because they are a little bit simpler. Types with lifetime parameters are
316/// explained below.
317///
318/// ## Manual implementation for types without lifetime
319/// Types that do not contain lifetime parameters are unable to borrow from the Python object, so
320/// the lifetimes of [`FromPyObject`] can be elided:
321/// ```rust,no_run
322/// # #![allow(dead_code)]
323/// use pyo3::prelude::*;
324///
325/// struct MyObject {
326/// msg: String,
327/// list: Vec<u32>
328/// }
329///
330/// impl FromPyObject<'_, '_> for MyObject {
331/// type Error = PyErr;
332///
333/// fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
334/// Ok(MyObject {
335/// msg: obj.getattr("msg")?.extract()?,
336/// list: obj.getattr("list")?.extract()?,
337/// })
338/// }
339/// }
340///
341/// # fn main() {}
342/// ```
343/// This is basically what the derive macro above expands to.
344///
345/// ## Manual implementation for types with lifetime parameters
346/// For types that contain lifetimes, these lifetimes need to be bound to the corresponding
347/// [`FromPyObject`] lifetime. This is roughly how the extraction of a typed [`Bound`] is
348/// implemented within PyO3.
349///
350/// ```rust,no_run
351/// # #![allow(dead_code)]
352/// use pyo3::prelude::*;
353/// use pyo3::types::PyString;
354///
355/// struct MyObject<'py>(Bound<'py, PyString>);
356///
357/// impl<'py> FromPyObject<'_, 'py> for MyObject<'py> {
358/// type Error = PyErr;
359///
360/// fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
361/// Ok(MyObject(obj.cast()?.to_owned()))
362/// }
363/// }
364///
365/// # fn main() {}
366/// ```
367///
368/// # Details
369/// [`Cow<'a, str>`] is an example of an output type that may or may not borrow from the input
370/// lifetime `'a`. Which variant will be produced depends on the runtime type of the Python object.
371/// For a Python byte string, the existing string data can be borrowed for `'a` into a
372/// [`Cow::Borrowed`]. For a Python Unicode string, the data may have to be reencoded to UTF-8, and
373/// copied into a [`Cow::Owned`]. It does _not_ depend on the Python lifetime `'py`.
374///
375/// The output type may also depend on the Python lifetime `'py`. This allows the output type to
376/// keep interacting with the Python interpreter. See also [`Bound<'py, T>`].
377///
378/// [`Cow<'a, str>`]: std::borrow::Cow
379/// [`Cow::Borrowed`]: std::borrow::Cow::Borrowed
380/// [`Cow::Owned`]: std::borrow::Cow::Owned
381/// [guide]: https://pyo3.rs/latest/conversions/traits.html#deriving-frompyobject
382pub trait FromPyObject<'a, 'py>: Sized {
383 /// The type returned in the event of a conversion error.
384 ///
385 /// For most use cases defaulting to [PyErr] here is perfectly acceptable. Using a custom error
386 /// type can be used to avoid having to create a Python exception object in the case where that
387 /// exception never reaches Python. This may lead to slightly better performance under certain
388 /// conditions.
389 ///
390 /// # Note
391 /// Unfortunately `Try` and thus `?` is based on [`From`], not [`Into`], so implementations may
392 /// need to use `.map_err(Into::into)` sometimes to convert a generic `Error` into a [`PyErr`].
393 type Error: Into<PyErr>;
394
395 /// Provides the type hint information for this type when it appears as an argument.
396 ///
397 /// For example, `Vec<u32>` would be `collections.abc.Sequence[int]`.
398 /// The default value is `typing.Any`, which is correct for any type.
399 #[cfg(feature = "experimental-inspect")]
400 const INPUT_TYPE: PyStaticExpr = type_hint_identifier!("_typeshed", "Incomplete");
401
402 /// Extracts `Self` from the bound smart pointer `obj`.
403 ///
404 /// Users are advised against calling this method directly: instead, use this via
405 /// [`Bound<'_, PyAny>::extract`](crate::types::any::PyAnyMethods::extract) or [`Py::extract`].
406 fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error>;
407
408 /// Specialization hook for extracting sequences for types like `Vec<u8>` and `[u8; N]`,
409 /// where the bytes can be directly copied from some python objects without going through
410 /// iteration.
411 #[doc(hidden)]
412 #[inline(always)]
413 fn sequence_extractor(
414 _obj: Borrowed<'_, 'py, PyAny>,
415 _: private::Token,
416 ) -> Option<impl FromPyObjectSequence<Target = Self>> {
417 struct NeverASequence<T>(PhantomData<T>);
418
419 impl<T> FromPyObjectSequence for NeverASequence<T> {
420 type Target = T;
421
422 fn to_vec(&self) -> Vec<Self::Target> {
423 unreachable!()
424 }
425
426 fn to_array<const N: usize>(&self) -> PyResult<[Self::Target; N]> {
427 unreachable!()
428 }
429 }
430
431 Option::<NeverASequence<Self>>::None
432 }
433
434 /// Helper used to make a specialized path in extracting `DateTime<Tz>` where `Tz` is
435 /// `chrono::Local`, which will accept "naive" datetime objects as being in the local timezone.
436 #[cfg(feature = "chrono-local")]
437 #[inline]
438 fn as_local_tz(_: private::Token) -> Option<Self> {
439 None
440 }
441}
442
443mod from_py_object_sequence {
444 use crate::PyResult;
445
446 /// Private trait for implementing specialized sequence extraction for `Vec<u8>` and `[u8; N]`
447 #[doc(hidden)]
448 pub trait FromPyObjectSequence {
449 type Target;
450
451 fn to_vec(&self) -> Vec<Self::Target>;
452
453 fn to_array<const N: usize>(&self) -> PyResult<[Self::Target; N]>;
454 }
455}
456
457// Only reachable / implementable inside PyO3 itself.
458pub(crate) use from_py_object_sequence::FromPyObjectSequence;
459
460/// A data structure that can be extracted without borrowing any data from the input.
461///
462/// This is primarily useful for trait bounds. For example a [`FromPyObject`] implementation of a
463/// wrapper type may be able to borrow data from the input, but a [`FromPyObject`] implementation of
464/// a collection type may only extract owned data.
465///
466/// For example [`PyList`] will not hand out references tied to its own lifetime, but "owned"
467/// references independent of it. (Similar to [`Vec<Arc<T>>`] where you clone the [`Arc<T>`] out).
468/// This makes it impossible to collect borrowed types in a collection, since they would not borrow
469/// from the original [`PyList`], but the much shorter lived element reference. See the example
470/// below.
471///
472/// ```,no_run
473/// # use pyo3::prelude::*;
474/// # #[allow(dead_code)]
475/// pub struct MyWrapper<T>(T);
476///
477/// impl<'a, 'py, T> FromPyObject<'a, 'py> for MyWrapper<T>
478/// where
479/// T: FromPyObject<'a, 'py>
480/// {
481/// type Error = T::Error;
482///
483/// fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
484/// obj.extract().map(MyWrapper)
485/// }
486/// }
487///
488/// # #[allow(dead_code)]
489/// pub struct MyVec<T>(Vec<T>);
490///
491/// impl<'py, T> FromPyObject<'_, 'py> for MyVec<T>
492/// where
493/// T: FromPyObjectOwned<'py> // 👈 can only extract owned values, because each `item` below
494/// // is a temporary short lived owned reference
495/// {
496/// type Error = PyErr;
497///
498/// fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
499/// let mut v = MyVec(Vec::new());
500/// for item in obj.try_iter()? {
501/// v.0.push(item?.extract::<T>().map_err(Into::into)?);
502/// }
503/// Ok(v)
504/// }
505/// }
506/// ```
507///
508/// [`PyList`]: crate::types::PyList
509/// [`Arc<T>`]: std::sync::Arc
510pub trait FromPyObjectOwned<'py>: for<'a> FromPyObject<'a, 'py> {}
511impl<'py, T> FromPyObjectOwned<'py> for T where T: for<'a> FromPyObject<'a, 'py> {}
512
513impl<'a, 'py, T> FromPyObject<'a, 'py> for T
514where
515 T: PyClass + Clone + ExtractPyClassWithClone,
516{
517 type Error = PyClassGuardError<'a, 'py>;
518
519 #[cfg(feature = "experimental-inspect")]
520 const INPUT_TYPE: PyStaticExpr = <T as crate::PyTypeInfo>::TYPE_HINT;
521
522 fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
523 Ok(obj.extract::<PyClassGuard<'_, T>>()?.clone())
524 }
525}
526
527impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRef<'py, T>
528where
529 T: PyClass,
530{
531 type Error = PyClassGuardError<'a, 'py>;
532
533 #[cfg(feature = "experimental-inspect")]
534 const INPUT_TYPE: PyStaticExpr = <T as crate::PyTypeInfo>::TYPE_HINT;
535
536 fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
537 obj.cast::<T>()
538 .map_err(|e| PyClassGuardError(Some(e)))?
539 .try_borrow()
540 .map_err(|_| PyClassGuardError(None))
541 }
542}
543
544impl<'a, 'py, T> FromPyObject<'a, 'py> for PyRefMut<'py, T>
545where
546 T: PyClass<Frozen = False>,
547{
548 type Error = PyClassGuardMutError<'a, 'py>;
549
550 #[cfg(feature = "experimental-inspect")]
551 const INPUT_TYPE: PyStaticExpr = <T as crate::PyTypeInfo>::TYPE_HINT;
552
553 fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
554 obj.cast::<T>()
555 .map_err(|e| PyClassGuardMutError(Some(e)))?
556 .try_borrow_mut()
557 .map_err(|_| PyClassGuardMutError(None))
558 }
559}
560
561impl<'py> IntoPyObject<'py> for () {
562 type Target = PyTuple;
563 type Output = Bound<'py, Self::Target>;
564 type Error = Infallible;
565
566 #[cfg(feature = "experimental-inspect")]
567 const OUTPUT_TYPE: PyStaticExpr =
568 type_hint_subscript!(PyTuple::TYPE_HINT, PyStaticExpr::Tuple { elts: &[] });
569
570 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
571 Ok(PyTuple::empty(py))
572 }
573}
574
575/// ```rust,compile_fail
576/// use pyo3::prelude::*;
577///
578/// #[pyclass]
579/// struct TestClass {
580/// num: u32,
581/// }
582///
583/// let t = TestClass { num: 10 };
584///
585/// Python::attach(|py| {
586/// let pyvalue = Py::new(py, t).unwrap().to_object(py);
587/// let t: TestClass = pyvalue.extract(py).unwrap();
588/// })
589/// ```
590mod test_no_clone {}
591
592#[cfg(test)]
593mod tests {
594 #[test]
595 #[cfg(feature = "macros")]
596 fn test_pyclass_skip_from_py_object() {
597 use crate::{types::PyAnyMethods, FromPyObject, IntoPyObject, PyErr, Python};
598
599 #[crate::pyclass(crate = "crate", skip_from_py_object)]
600 #[derive(Clone)]
601 struct Foo(i32);
602
603 impl<'py> FromPyObject<'_, 'py> for Foo {
604 type Error = PyErr;
605
606 fn extract(obj: crate::Borrowed<'_, 'py, crate::PyAny>) -> Result<Self, Self::Error> {
607 if let Ok(obj) = obj.cast::<Self>() {
608 Ok(obj.borrow().clone())
609 } else {
610 obj.extract::<i32>().map(Self)
611 }
612 }
613 }
614 Python::attach(|py| {
615 let foo1 = 42i32.into_pyobject(py)?;
616 assert_eq!(foo1.extract::<Foo>()?.0, 42);
617
618 let foo2 = Foo(0).into_pyobject(py)?;
619 assert_eq!(foo2.extract::<Foo>()?.0, 0);
620
621 Ok::<_, PyErr>(())
622 })
623 .unwrap();
624 }
625
626 #[test]
627 #[cfg(feature = "macros")]
628 fn test_pyclass_from_py_object() {
629 use crate::{types::PyAnyMethods, IntoPyObject, PyErr, Python};
630
631 #[crate::pyclass(crate = "crate", from_py_object)]
632 #[derive(Clone)]
633 struct Foo(i32);
634
635 Python::attach(|py| {
636 let foo1 = 42i32.into_pyobject(py)?;
637 assert!(foo1.extract::<Foo>().is_err());
638
639 let foo2 = Foo(0).into_pyobject(py)?;
640 assert_eq!(foo2.extract::<Foo>()?.0, 0);
641
642 Ok::<_, PyErr>(())
643 })
644 .unwrap();
645 }
646}