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 | |