1 | //! Defines conversions between Rust and Python types. |
2 | use crate::err::{self, PyDowncastError, PyResult}; |
3 | #[cfg (feature = "experimental-inspect" )] |
4 | use crate::inspect::types::TypeInfo; |
5 | use crate::pyclass::boolean_struct::False; |
6 | use crate::type_object::PyTypeInfo; |
7 | use crate::types::PyTuple; |
8 | use crate::{ |
9 | ffi, gil, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python, |
10 | }; |
11 | use std::cell::Cell; |
12 | use std::ptr::NonNull; |
13 | |
14 | /// Returns a borrowed pointer to a Python object. |
15 | /// |
16 | /// The returned pointer will be valid for as long as `self` is. It may be null depending on the |
17 | /// implementation. |
18 | /// |
19 | /// # Examples |
20 | /// |
21 | /// ```rust |
22 | /// use pyo3::prelude::*; |
23 | /// use pyo3::types::PyString; |
24 | /// use pyo3::ffi; |
25 | /// |
26 | /// Python::with_gil(|py| { |
27 | /// let s: Py<PyString> = "foo" .into_py(py); |
28 | /// let ptr = s.as_ptr(); |
29 | /// |
30 | /// let is_really_a_pystring = unsafe { ffi::PyUnicode_CheckExact(ptr) }; |
31 | /// assert_eq!(is_really_a_pystring, 1); |
32 | /// }); |
33 | /// ``` |
34 | /// |
35 | /// # Safety |
36 | /// |
37 | /// For callers, it is your responsibility to make sure that the underlying Python object is not dropped too |
38 | /// early. For example, the following code will cause undefined behavior: |
39 | /// |
40 | /// ```rust,no_run |
41 | /// # use pyo3::prelude::*; |
42 | /// # use pyo3::ffi; |
43 | /// # |
44 | /// Python::with_gil(|py| { |
45 | /// let ptr: *mut ffi::PyObject = 0xabad1dea_u32.into_py(py).as_ptr(); |
46 | /// |
47 | /// let isnt_a_pystring = unsafe { |
48 | /// // `ptr` is dangling, this is UB |
49 | /// ffi::PyUnicode_CheckExact(ptr) |
50 | /// }; |
51 | /// # assert_eq!(isnt_a_pystring, 0); |
52 | /// }); |
53 | /// ``` |
54 | /// |
55 | /// This happens because the pointer returned by `as_ptr` does not carry any lifetime information |
56 | /// and the Python object is dropped immediately after the `0xabad1dea_u32.into_py(py).as_ptr()` |
57 | /// expression is evaluated. To fix the problem, bind Python object to a local variable like earlier |
58 | /// to keep the Python object alive until the end of its scope. |
59 | /// |
60 | /// Implementors must ensure this returns a valid pointer to a Python object, which borrows a reference count from `&self`. |
61 | pub unsafe trait AsPyPointer { |
62 | /// Returns the underlying FFI pointer as a borrowed pointer. |
63 | fn as_ptr(&self) -> *mut ffi::PyObject; |
64 | } |
65 | |
66 | /// Convert `None` into a null pointer. |
67 | unsafe impl<T> AsPyPointer for Option<T> |
68 | where |
69 | T: AsPyPointer, |
70 | { |
71 | #[inline ] |
72 | fn as_ptr(&self) -> *mut ffi::PyObject { |
73 | self.as_ref() |
74 | .map_or_else(default:std::ptr::null_mut, |t: &T| t.as_ptr()) |
75 | } |
76 | } |
77 | |
78 | /// Conversion trait that allows various objects to be converted into `PyObject`. |
79 | pub trait ToPyObject { |
80 | /// Converts self into a Python object. |
81 | fn to_object(&self, py: Python<'_>) -> PyObject; |
82 | } |
83 | |
84 | /// Defines a conversion from a Rust type to a Python object. |
85 | /// |
86 | /// It functions similarly to std's [`Into`] trait, but requires a [GIL token](Python) |
87 | /// as an argument. Many functions and traits internal to PyO3 require this trait as a bound, |
88 | /// so a lack of this trait can manifest itself in different error messages. |
89 | /// |
90 | /// # Examples |
91 | /// ## With `#[pyclass]` |
92 | /// The easiest way to implement `IntoPy` is by exposing a struct as a native Python object |
93 | /// by annotating it with [`#[pyclass]`](crate::prelude::pyclass). |
94 | /// |
95 | /// ```rust |
96 | /// use pyo3::prelude::*; |
97 | /// |
98 | /// #[pyclass] |
99 | /// struct Number { |
100 | /// #[pyo3(get, set)] |
101 | /// value: i32, |
102 | /// } |
103 | /// ``` |
104 | /// Python code will see this as an instance of the `Number` class with a `value` attribute. |
105 | /// |
106 | /// ## Conversion to a Python object |
107 | /// |
108 | /// However, it may not be desirable to expose the existence of `Number` to Python code. |
109 | /// `IntoPy` allows us to define a conversion to an appropriate Python object. |
110 | /// ```rust |
111 | /// use pyo3::prelude::*; |
112 | /// |
113 | /// struct Number { |
114 | /// value: i32, |
115 | /// } |
116 | /// |
117 | /// impl IntoPy<PyObject> for Number { |
118 | /// fn into_py(self, py: Python<'_>) -> PyObject { |
119 | /// // delegates to i32's IntoPy implementation. |
120 | /// self.value.into_py(py) |
121 | /// } |
122 | /// } |
123 | /// ``` |
124 | /// Python code will see this as an `int` object. |
125 | /// |
126 | /// ## Dynamic conversion into Python objects. |
127 | /// It is also possible to return a different Python object depending on some condition. |
128 | /// This is useful for types like enums that can carry different types. |
129 | /// |
130 | /// ```rust |
131 | /// use pyo3::prelude::*; |
132 | /// |
133 | /// enum Value { |
134 | /// Integer(i32), |
135 | /// String(String), |
136 | /// None, |
137 | /// } |
138 | /// |
139 | /// impl IntoPy<PyObject> for Value { |
140 | /// fn into_py(self, py: Python<'_>) -> PyObject { |
141 | /// match self { |
142 | /// Self::Integer(val) => val.into_py(py), |
143 | /// Self::String(val) => val.into_py(py), |
144 | /// Self::None => py.None(), |
145 | /// } |
146 | /// } |
147 | /// } |
148 | /// # fn main() { |
149 | /// # Python::with_gil(|py| { |
150 | /// # let v = Value::Integer(73).into_py(py); |
151 | /// # let v = v.extract::<i32>(py).unwrap(); |
152 | /// # |
153 | /// # let v = Value::String("foo" .into()).into_py(py); |
154 | /// # let v = v.extract::<String>(py).unwrap(); |
155 | /// # |
156 | /// # let v = Value::None.into_py(py); |
157 | /// # let v = v.extract::<Option<Vec<i32>>>(py).unwrap(); |
158 | /// # }); |
159 | /// # } |
160 | /// ``` |
161 | /// Python code will see this as any of the `int`, `string` or `None` objects. |
162 | #[doc (alias = "IntoPyCallbackOutput" )] |
163 | pub trait IntoPy<T>: Sized { |
164 | /// Performs the conversion. |
165 | fn into_py(self, py: Python<'_>) -> T; |
166 | |
167 | /// Extracts the type hint information for this type when it appears as a return value. |
168 | /// |
169 | /// For example, `Vec<u32>` would return `List[int]`. |
170 | /// The default implementation returns `Any`, which is correct for any type. |
171 | /// |
172 | /// For most types, the return value for this method will be identical to that of [`FromPyObject::type_input`]. |
173 | /// It may be different for some types, such as `Dict`, to allow duck-typing: functions return `Dict` but take `Mapping` as argument. |
174 | #[cfg (feature = "experimental-inspect" )] |
175 | fn type_output() -> TypeInfo { |
176 | TypeInfo::Any |
177 | } |
178 | } |
179 | |
180 | /// Extract a type from a Python object. |
181 | /// |
182 | /// |
183 | /// Normal usage is through the `extract` methods on [`Py`] and [`PyAny`], which forward to this trait. |
184 | /// |
185 | /// # Examples |
186 | /// |
187 | /// ```rust |
188 | /// use pyo3::prelude::*; |
189 | /// use pyo3::types::PyString; |
190 | /// |
191 | /// # fn main() -> PyResult<()> { |
192 | /// Python::with_gil(|py| { |
193 | /// let obj: Py<PyString> = PyString::new(py, "blah" ).into(); |
194 | /// |
195 | /// // Straight from an owned reference |
196 | /// let s: &str = obj.extract(py)?; |
197 | /// # assert_eq!(s, "blah" ); |
198 | /// |
199 | /// // Or from a borrowed reference |
200 | /// let obj: &PyString = obj.as_ref(py); |
201 | /// let s: &str = obj.extract()?; |
202 | /// # assert_eq!(s, "blah" ); |
203 | /// # Ok(()) |
204 | /// }) |
205 | /// # } |
206 | /// ``` |
207 | /// |
208 | /// Note: depending on the implementation, the lifetime of the extracted result may |
209 | /// depend on the lifetime of the `obj` or the `prepared` variable. |
210 | /// |
211 | /// For example, when extracting `&str` from a Python byte string, the resulting string slice will |
212 | /// point to the existing string data (lifetime: `'source`). |
213 | /// On the other hand, when extracting `&str` from a Python Unicode string, the preparation step |
214 | /// will convert the string to UTF-8, and the resulting string slice will have lifetime `'prepared`. |
215 | /// Since which case applies depends on the runtime type of the Python object, |
216 | /// both the `obj` and `prepared` variables must outlive the resulting string slice. |
217 | /// |
218 | /// The trait's conversion method takes a `&PyAny` argument but is called |
219 | /// `FromPyObject` for historical reasons. |
220 | pub trait FromPyObject<'source>: Sized { |
221 | /// Extracts `Self` from the source `PyObject`. |
222 | fn extract(ob: &'source PyAny) -> PyResult<Self>; |
223 | |
224 | /// Extracts the type hint information for this type when it appears as an argument. |
225 | /// |
226 | /// For example, `Vec<u32>` would return `Sequence[int]`. |
227 | /// The default implementation returns `Any`, which is correct for any type. |
228 | /// |
229 | /// For most types, the return value for this method will be identical to that of [`IntoPy::type_output`]. |
230 | /// It may be different for some types, such as `Dict`, to allow duck-typing: functions return `Dict` but take `Mapping` as argument. |
231 | #[cfg (feature = "experimental-inspect" )] |
232 | fn type_input() -> TypeInfo { |
233 | TypeInfo::Any |
234 | } |
235 | } |
236 | |
237 | /// Identity conversion: allows using existing `PyObject` instances where |
238 | /// `T: ToPyObject` is expected. |
239 | impl<T: ?Sized + ToPyObject> ToPyObject for &'_ T { |
240 | #[inline ] |
241 | fn to_object(&self, py: Python<'_>) -> PyObject { |
242 | <T as ToPyObject>::to_object(*self, py) |
243 | } |
244 | } |
245 | |
246 | /// `Option::Some<T>` is converted like `T`. |
247 | /// `Option::None` is converted to Python `None`. |
248 | impl<T> ToPyObject for Option<T> |
249 | where |
250 | T: ToPyObject, |
251 | { |
252 | fn to_object(&self, py: Python<'_>) -> PyObject { |
253 | self.as_ref() |
254 | .map_or_else(|| py.None(), |val: &T| val.to_object(py)) |
255 | } |
256 | } |
257 | |
258 | impl<T> IntoPy<PyObject> for Option<T> |
259 | where |
260 | T: IntoPy<PyObject>, |
261 | { |
262 | fn into_py(self, py: Python<'_>) -> PyObject { |
263 | self.map_or_else(|| py.None(), |val: T| val.into_py(py)) |
264 | } |
265 | } |
266 | |
267 | impl IntoPy<PyObject> for &'_ PyAny { |
268 | #[inline ] |
269 | fn into_py(self, py: Python<'_>) -> PyObject { |
270 | unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } |
271 | } |
272 | } |
273 | |
274 | impl<T> IntoPy<PyObject> for &'_ T |
275 | where |
276 | T: AsRef<PyAny>, |
277 | { |
278 | #[inline ] |
279 | fn into_py(self, py: Python<'_>) -> PyObject { |
280 | unsafe { PyObject::from_borrowed_ptr(py, self.as_ref().as_ptr()) } |
281 | } |
282 | } |
283 | |
284 | impl<T: Copy + ToPyObject> ToPyObject for Cell<T> { |
285 | fn to_object(&self, py: Python<'_>) -> PyObject { |
286 | self.get().to_object(py) |
287 | } |
288 | } |
289 | |
290 | impl<T: Copy + IntoPy<PyObject>> IntoPy<PyObject> for Cell<T> { |
291 | fn into_py(self, py: Python<'_>) -> PyObject { |
292 | self.get().into_py(py) |
293 | } |
294 | } |
295 | |
296 | impl<'a, T: FromPyObject<'a>> FromPyObject<'a> for Cell<T> { |
297 | fn extract(ob: &'a PyAny) -> PyResult<Self> { |
298 | T::extract(ob).map(op:Cell::new) |
299 | } |
300 | } |
301 | |
302 | impl<'a, T> FromPyObject<'a> for &'a PyCell<T> |
303 | where |
304 | T: PyClass, |
305 | { |
306 | fn extract(obj: &'a PyAny) -> PyResult<Self> { |
307 | PyTryFrom::try_from(obj).map_err(op:Into::into) |
308 | } |
309 | } |
310 | |
311 | impl<'a, T> FromPyObject<'a> for T |
312 | where |
313 | T: PyClass + Clone, |
314 | { |
315 | fn extract(obj: &'a PyAny) -> PyResult<Self> { |
316 | let cell: &PyCell<Self> = PyTryFrom::try_from(obj)?; |
317 | Ok(unsafe { cell.try_borrow_unguarded()?.clone() }) |
318 | } |
319 | } |
320 | |
321 | impl<'a, T> FromPyObject<'a> for PyRef<'a, T> |
322 | where |
323 | T: PyClass, |
324 | { |
325 | fn extract(obj: &'a PyAny) -> PyResult<Self> { |
326 | let cell: &PyCell<T> = PyTryFrom::try_from(obj)?; |
327 | cell.try_borrow().map_err(op:Into::into) |
328 | } |
329 | } |
330 | |
331 | impl<'a, T> FromPyObject<'a> for PyRefMut<'a, T> |
332 | where |
333 | T: PyClass<Frozen = False>, |
334 | { |
335 | fn extract(obj: &'a PyAny) -> PyResult<Self> { |
336 | let cell: &PyCell<T> = PyTryFrom::try_from(obj)?; |
337 | cell.try_borrow_mut().map_err(op:Into::into) |
338 | } |
339 | } |
340 | |
341 | impl<'a, T> FromPyObject<'a> for Option<T> |
342 | where |
343 | T: FromPyObject<'a>, |
344 | { |
345 | fn extract(obj: &'a PyAny) -> PyResult<Self> { |
346 | if obj.as_ptr() == unsafe { ffi::Py_None() } { |
347 | Ok(None) |
348 | } else { |
349 | T::extract(obj).map(op:Some) |
350 | } |
351 | } |
352 | } |
353 | |
354 | /// Trait implemented by Python object types that allow a checked downcast. |
355 | /// If `T` implements `PyTryFrom`, we can convert `&PyAny` to `&T`. |
356 | /// |
357 | /// This trait is similar to `std::convert::TryFrom` |
358 | pub trait PyTryFrom<'v>: Sized + PyNativeType { |
359 | /// Cast from a concrete Python object type to PyObject. |
360 | fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>>; |
361 | |
362 | /// Cast from a concrete Python object type to PyObject. With exact type check. |
363 | fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>>; |
364 | |
365 | /// Cast a PyAny to a specific type of PyObject. The caller must |
366 | /// have already verified the reference is for this type. |
367 | /// |
368 | /// # Safety |
369 | /// |
370 | /// Callers must ensure that the type is valid or risk type confusion. |
371 | unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self; |
372 | } |
373 | |
374 | /// Trait implemented by Python object types that allow a checked downcast. |
375 | /// This trait is similar to `std::convert::TryInto` |
376 | pub trait PyTryInto<T>: Sized { |
377 | /// Cast from PyObject to a concrete Python object type. |
378 | fn try_into(&self) -> Result<&T, PyDowncastError<'_>>; |
379 | |
380 | /// Cast from PyObject to a concrete Python object type. With exact type check. |
381 | fn try_into_exact(&self) -> Result<&T, PyDowncastError<'_>>; |
382 | } |
383 | |
384 | // TryFrom implies TryInto |
385 | impl<U> PyTryInto<U> for PyAny |
386 | where |
387 | U: for<'v> PyTryFrom<'v>, |
388 | { |
389 | fn try_into(&self) -> Result<&U, PyDowncastError<'_>> { |
390 | <U as PyTryFrom<'_>>::try_from(self) |
391 | } |
392 | fn try_into_exact(&self) -> Result<&U, PyDowncastError<'_>> { |
393 | U::try_from_exact(self) |
394 | } |
395 | } |
396 | |
397 | impl<'v, T> PyTryFrom<'v> for T |
398 | where |
399 | T: PyTypeInfo + PyNativeType, |
400 | { |
401 | fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> { |
402 | let value = value.into(); |
403 | unsafe { |
404 | if T::is_type_of(value) { |
405 | Ok(Self::try_from_unchecked(value)) |
406 | } else { |
407 | Err(PyDowncastError::new(value, T::NAME)) |
408 | } |
409 | } |
410 | } |
411 | |
412 | fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> { |
413 | let value = value.into(); |
414 | unsafe { |
415 | if T::is_exact_type_of(value) { |
416 | Ok(Self::try_from_unchecked(value)) |
417 | } else { |
418 | Err(PyDowncastError::new(value, T::NAME)) |
419 | } |
420 | } |
421 | } |
422 | |
423 | #[inline ] |
424 | unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self { |
425 | Self::unchecked_downcast(value.into()) |
426 | } |
427 | } |
428 | |
429 | impl<'v, T> PyTryFrom<'v> for PyCell<T> |
430 | where |
431 | T: 'v + PyClass, |
432 | { |
433 | fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> { |
434 | let value = value.into(); |
435 | unsafe { |
436 | if T::is_type_of(value) { |
437 | Ok(Self::try_from_unchecked(value)) |
438 | } else { |
439 | Err(PyDowncastError::new(value, T::NAME)) |
440 | } |
441 | } |
442 | } |
443 | fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v Self, PyDowncastError<'v>> { |
444 | let value = value.into(); |
445 | unsafe { |
446 | if T::is_exact_type_of(value) { |
447 | Ok(Self::try_from_unchecked(value)) |
448 | } else { |
449 | Err(PyDowncastError::new(value, T::NAME)) |
450 | } |
451 | } |
452 | } |
453 | #[inline ] |
454 | unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v Self { |
455 | Self::unchecked_downcast(value.into()) |
456 | } |
457 | } |
458 | |
459 | /// Converts `()` to an empty Python tuple. |
460 | impl IntoPy<Py<PyTuple>> for () { |
461 | fn into_py(self, py: Python<'_>) -> Py<PyTuple> { |
462 | PyTuple::empty(py).into() |
463 | } |
464 | } |
465 | |
466 | /// Raw level conversion between `*mut ffi::PyObject` and PyO3 types. |
467 | /// |
468 | /// # Safety |
469 | /// |
470 | /// See safety notes on individual functions. |
471 | pub unsafe trait FromPyPointer<'p>: Sized { |
472 | /// Convert from an arbitrary `PyObject`. |
473 | /// |
474 | /// # Safety |
475 | /// |
476 | /// Implementations must ensure the object does not get freed during `'p` |
477 | /// and ensure that `ptr` is of the correct type. |
478 | /// Note that it must be safe to decrement the reference count of `ptr`. |
479 | unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self>; |
480 | /// Convert from an arbitrary `PyObject` or panic. |
481 | /// |
482 | /// # Safety |
483 | /// |
484 | /// Relies on [`from_owned_ptr_or_opt`](#method.from_owned_ptr_or_opt). |
485 | unsafe fn from_owned_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { |
486 | Self::from_owned_ptr_or_opt(py, ptr).unwrap_or_else(|| err::panic_after_error(py)) |
487 | } |
488 | /// Convert from an arbitrary `PyObject` or panic. |
489 | /// |
490 | /// # Safety |
491 | /// |
492 | /// Relies on [`from_owned_ptr_or_opt`](#method.from_owned_ptr_or_opt). |
493 | unsafe fn from_owned_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { |
494 | Self::from_owned_ptr_or_panic(py, ptr) |
495 | } |
496 | /// Convert from an arbitrary `PyObject`. |
497 | /// |
498 | /// # Safety |
499 | /// |
500 | /// Relies on [`from_owned_ptr_or_opt`](#method.from_owned_ptr_or_opt). |
501 | unsafe fn from_owned_ptr_or_err(py: Python<'p>, ptr: *mut ffi::PyObject) -> PyResult<&'p Self> { |
502 | Self::from_owned_ptr_or_opt(py, ptr).ok_or_else(|| err::PyErr::fetch(py)) |
503 | } |
504 | /// Convert from an arbitrary borrowed `PyObject`. |
505 | /// |
506 | /// # Safety |
507 | /// |
508 | /// Implementations must ensure the object does not get freed during `'p` and avoid type confusion. |
509 | unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) |
510 | -> Option<&'p Self>; |
511 | /// Convert from an arbitrary borrowed `PyObject`. |
512 | /// |
513 | /// # Safety |
514 | /// |
515 | /// Relies on unsafe fn [`from_borrowed_ptr_or_opt`](#method.from_borrowed_ptr_or_opt). |
516 | unsafe fn from_borrowed_ptr_or_panic(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { |
517 | Self::from_borrowed_ptr_or_opt(py, ptr).unwrap_or_else(|| err::panic_after_error(py)) |
518 | } |
519 | /// Convert from an arbitrary borrowed `PyObject`. |
520 | /// |
521 | /// # Safety |
522 | /// |
523 | /// Relies on unsafe fn [`from_borrowed_ptr_or_opt`](#method.from_borrowed_ptr_or_opt). |
524 | unsafe fn from_borrowed_ptr(py: Python<'p>, ptr: *mut ffi::PyObject) -> &'p Self { |
525 | Self::from_borrowed_ptr_or_panic(py, ptr) |
526 | } |
527 | /// Convert from an arbitrary borrowed `PyObject`. |
528 | /// |
529 | /// # Safety |
530 | /// |
531 | /// Relies on unsafe fn [`from_borrowed_ptr_or_opt`](#method.from_borrowed_ptr_or_opt). |
532 | unsafe fn from_borrowed_ptr_or_err( |
533 | py: Python<'p>, |
534 | ptr: *mut ffi::PyObject, |
535 | ) -> PyResult<&'p Self> { |
536 | Self::from_borrowed_ptr_or_opt(py, ptr).ok_or_else(|| err::PyErr::fetch(py)) |
537 | } |
538 | } |
539 | |
540 | unsafe impl<'p, T> FromPyPointer<'p> for T |
541 | where |
542 | T: 'p + crate::PyNativeType, |
543 | { |
544 | unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<&'p Self> { |
545 | gil::register_owned(py, obj:NonNull::new(ptr)?); |
546 | Some(&*(ptr as *mut Self)) |
547 | } |
548 | unsafe fn from_borrowed_ptr_or_opt( |
549 | _py: Python<'p>, |
550 | ptr: *mut ffi::PyObject, |
551 | ) -> Option<&'p Self> { |
552 | NonNull::new(ptr as *mut Self).map(|p: NonNull| &*p.as_ptr()) |
553 | } |
554 | } |
555 | |
556 | /// ```rust,compile_fail |
557 | /// use pyo3::prelude::*; |
558 | /// |
559 | /// #[pyclass] |
560 | /// struct TestClass { |
561 | /// num: u32, |
562 | /// } |
563 | /// |
564 | /// let t = TestClass { num: 10 }; |
565 | /// |
566 | /// Python::with_gil(|py| { |
567 | /// let pyvalue = Py::new(py, t).unwrap().to_object(py); |
568 | /// let t: TestClass = pyvalue.extract(py).unwrap(); |
569 | /// }) |
570 | /// ``` |
571 | mod test_no_clone {} |
572 | |
573 | #[cfg (test)] |
574 | mod tests { |
575 | use crate::types::{IntoPyDict, PyAny, PyDict, PyList}; |
576 | use crate::{PyObject, Python, ToPyObject}; |
577 | |
578 | use super::PyTryFrom; |
579 | |
580 | #[test ] |
581 | fn test_try_from() { |
582 | Python::with_gil(|py| { |
583 | let list: &PyAny = vec![3, 6, 5, 4, 7].to_object(py).into_ref(py); |
584 | let dict: &PyAny = vec![("reverse" , true)].into_py_dict(py).as_ref(); |
585 | |
586 | assert!(<PyList as PyTryFrom<'_>>::try_from(list).is_ok()); |
587 | assert!(<PyDict as PyTryFrom<'_>>::try_from(dict).is_ok()); |
588 | |
589 | assert!(<PyAny as PyTryFrom<'_>>::try_from(list).is_ok()); |
590 | assert!(<PyAny as PyTryFrom<'_>>::try_from(dict).is_ok()); |
591 | }); |
592 | } |
593 | |
594 | #[test ] |
595 | fn test_try_from_exact() { |
596 | Python::with_gil(|py| { |
597 | let list: &PyAny = vec![3, 6, 5, 4, 7].to_object(py).into_ref(py); |
598 | let dict: &PyAny = vec![("reverse" , true)].into_py_dict(py).as_ref(); |
599 | |
600 | assert!(PyList::try_from_exact(list).is_ok()); |
601 | assert!(PyDict::try_from_exact(dict).is_ok()); |
602 | |
603 | assert!(PyAny::try_from_exact(list).is_err()); |
604 | assert!(PyAny::try_from_exact(dict).is_err()); |
605 | }); |
606 | } |
607 | |
608 | #[test ] |
609 | fn test_try_from_unchecked() { |
610 | Python::with_gil(|py| { |
611 | let list = PyList::new(py, [1, 2, 3]); |
612 | let val = unsafe { <PyList as PyTryFrom>::try_from_unchecked(list.as_ref()) }; |
613 | assert!(list.is(val)); |
614 | }); |
615 | } |
616 | |
617 | #[test ] |
618 | fn test_option_as_ptr() { |
619 | Python::with_gil(|py| { |
620 | use crate::AsPyPointer; |
621 | let mut option: Option<PyObject> = None; |
622 | assert_eq!(option.as_ptr(), std::ptr::null_mut()); |
623 | |
624 | let none = py.None(); |
625 | option = Some(none.clone()); |
626 | |
627 | let ref_cnt = none.get_refcnt(py); |
628 | assert_eq!(option.as_ptr(), none.as_ptr()); |
629 | |
630 | // Ensure ref count not changed by as_ptr call |
631 | assert_eq!(none.get_refcnt(py), ref_cnt); |
632 | }); |
633 | } |
634 | } |
635 | |