pyo3/impl_/
pyclass_init.rs

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