| 1 | //! Contains initialization utilities for `#[pyclass]`. |
| 2 | use crate::ffi_ptr_ext::FfiPtrExt; |
| 3 | use crate::internal::get_slot::TP_ALLOC; |
| 4 | use crate::types::PyType; |
| 5 | use crate::{ffi, Borrowed, PyErr, PyResult, Python}; |
| 6 | use crate::{ffi::PyTypeObject, sealed::Sealed, type_object::PyTypeInfo}; |
| 7 | use std::marker::PhantomData; |
| 8 | use std::ptr; |
| 9 | |
| 10 | /// Initializer for Python types. |
| 11 | /// |
| 12 | /// This trait is intended to use internally for distinguishing `#[pyclass]` and |
| 13 | /// Python native types. |
| 14 | pub trait PyObjectInit<T>: Sized + Sealed { |
| 15 | /// # Safety |
| 16 | /// - `subtype` must be a valid pointer to a type object of T or a subclass. |
| 17 | unsafe fn into_new_object( |
| 18 | self, |
| 19 | py: Python<'_>, |
| 20 | subtype: *mut PyTypeObject, |
| 21 | ) -> PyResult<*mut ffi::PyObject>; |
| 22 | |
| 23 | #[doc (hidden)] |
| 24 | fn can_be_subclassed(&self) -> bool; |
| 25 | } |
| 26 | |
| 27 | /// Initializer for Python native types, like `PyDict`. |
| 28 | pub struct PyNativeTypeInitializer<T: PyTypeInfo>(pub PhantomData<T>); |
| 29 | |
| 30 | impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> { |
| 31 | unsafe fn into_new_object( |
| 32 | self, |
| 33 | py: Python<'_>, |
| 34 | subtype: *mut PyTypeObject, |
| 35 | ) -> PyResult<*mut ffi::PyObject> { |
| 36 | unsafe fn inner( |
| 37 | py: Python<'_>, |
| 38 | type_object: *mut PyTypeObject, |
| 39 | subtype: *mut PyTypeObject, |
| 40 | ) -> PyResult<*mut ffi::PyObject> { |
| 41 | // HACK (due to FIXME below): PyBaseObject_Type's tp_new isn't happy with NULL arguments |
| 42 | let is_base_object = ptr::eq(type_object, ptr::addr_of!(ffi::PyBaseObject_Type)); |
| 43 | let subtype_borrowed: Borrowed<'_, '_, PyType> = unsafe { |
| 44 | subtype |
| 45 | .cast::<ffi::PyObject>() |
| 46 | .assume_borrowed_unchecked(py) |
| 47 | .downcast_unchecked() |
| 48 | }; |
| 49 | |
| 50 | if is_base_object { |
| 51 | let alloc = subtype_borrowed |
| 52 | .get_slot(TP_ALLOC) |
| 53 | .unwrap_or(ffi::PyType_GenericAlloc); |
| 54 | |
| 55 | let obj = unsafe { alloc(subtype, 0) }; |
| 56 | return if obj.is_null() { |
| 57 | Err(PyErr::fetch(py)) |
| 58 | } else { |
| 59 | Ok(obj) |
| 60 | }; |
| 61 | } |
| 62 | |
| 63 | #[cfg (Py_LIMITED_API)] |
| 64 | unreachable!("subclassing native types is not possible with the `abi3` feature" ); |
| 65 | |
| 66 | #[cfg (not(Py_LIMITED_API))] |
| 67 | { |
| 68 | match unsafe { (*type_object).tp_new } { |
| 69 | // FIXME: Call __new__ with actual arguments |
| 70 | Some(newfunc) => { |
| 71 | let obj = |
| 72 | unsafe { newfunc(subtype, std::ptr::null_mut(), std::ptr::null_mut()) }; |
| 73 | if obj.is_null() { |
| 74 | Err(PyErr::fetch(py)) |
| 75 | } else { |
| 76 | Ok(obj) |
| 77 | } |
| 78 | } |
| 79 | None => Err(crate::exceptions::PyTypeError::new_err( |
| 80 | "base type without tp_new" , |
| 81 | )), |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | let type_object = T::type_object_raw(py); |
| 86 | unsafe { inner(py, type_object, subtype) } |
| 87 | } |
| 88 | |
| 89 | #[inline ] |
| 90 | fn can_be_subclassed(&self) -> bool { |
| 91 | true |
| 92 | } |
| 93 | } |
| 94 | |