1//! Contains initialization utilities for `#[pyclass]`.
2use crate::callback::IntoPyCallbackOutput;
3use crate::impl_::pyclass::{PyClassBaseType, PyClassDict, PyClassThreadChecker, PyClassWeakRef};
4use crate::{ffi, Py, PyCell, PyClass, PyErr, PyResult, Python};
5use crate::{
6 ffi::PyTypeObject,
7 pycell::{
8 impl_::{PyClassBorrowChecker, PyClassMutability},
9 PyCellContents,
10 },
11 type_object::{get_tp_alloc, PyTypeInfo},
12};
13use std::{
14 cell::UnsafeCell,
15 marker::PhantomData,
16 mem::{ManuallyDrop, MaybeUninit},
17};
18
19/// Initializer for Python types.
20///
21/// This trait is intended to use internally for distinguishing `#[pyclass]` and
22/// Python native types.
23pub trait PyObjectInit<T>: Sized {
24 /// # Safety
25 /// - `subtype` must be a valid pointer to a type object of T or a subclass.
26 unsafe fn into_new_object(
27 self,
28 py: Python<'_>,
29 subtype: *mut PyTypeObject,
30 ) -> PyResult<*mut ffi::PyObject>;
31 private_decl! {}
32}
33
34/// Initializer for Python native types, like `PyDict`.
35pub struct PyNativeTypeInitializer<T: PyTypeInfo>(PhantomData<T>);
36
37impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
38 unsafe fn into_new_object(
39 self,
40 py: Python<'_>,
41 subtype: *mut PyTypeObject,
42 ) -> PyResult<*mut ffi::PyObject> {
43 unsafe fn inner(
44 py: Python<'_>,
45 type_object: *mut PyTypeObject,
46 subtype: *mut PyTypeObject,
47 ) -> PyResult<*mut ffi::PyObject> {
48 // HACK (due to FIXME below): PyBaseObject_Type's tp_new isn't happy with NULL arguments
49 let is_base_object = type_object == std::ptr::addr_of_mut!(ffi::PyBaseObject_Type);
50 if is_base_object {
51 let alloc = get_tp_alloc(subtype).unwrap_or(ffi::PyType_GenericAlloc);
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 private_impl! {}
86}
87
88/// Initializer for our `#[pyclass]` system.
89///
90/// You can use this type to initialize complicatedly nested `#[pyclass]`.
91///
92/// # Examples
93///
94/// ```
95/// # use pyo3::prelude::*;
96/// # use pyo3::py_run;
97/// #[pyclass(subclass)]
98/// struct BaseClass {
99/// #[pyo3(get)]
100/// basename: &'static str,
101/// }
102/// #[pyclass(extends=BaseClass, subclass)]
103/// struct SubClass {
104/// #[pyo3(get)]
105/// subname: &'static str,
106/// }
107/// #[pyclass(extends=SubClass)]
108/// struct SubSubClass {
109/// #[pyo3(get)]
110/// subsubname: &'static str,
111/// }
112///
113/// #[pymethods]
114/// impl SubSubClass {
115/// #[new]
116/// fn new() -> PyClassInitializer<Self> {
117/// PyClassInitializer::from(BaseClass { basename: "base" })
118/// .add_subclass(SubClass { subname: "sub" })
119/// .add_subclass(SubSubClass {
120/// subsubname: "subsub",
121/// })
122/// }
123/// }
124/// Python::with_gil(|py| {
125/// let typeobj = py.get_type::<SubSubClass>();
126/// let sub_sub_class = typeobj.call((), None).unwrap();
127/// py_run!(
128/// py,
129/// sub_sub_class,
130/// r#"
131/// assert sub_sub_class.basename == 'base'
132/// assert sub_sub_class.subname == 'sub'
133/// assert sub_sub_class.subsubname == 'subsub'"#
134/// );
135/// });
136/// ```
137pub struct PyClassInitializer<T: PyClass>(PyClassInitializerImpl<T>);
138
139enum PyClassInitializerImpl<T: PyClass> {
140 Existing(Py<T>),
141 New {
142 init: T,
143 super_init: <T::BaseType as PyClassBaseType>::Initializer,
144 },
145}
146
147impl<T: PyClass> PyClassInitializer<T> {
148 /// Constructs a new initializer from value `T` and base class' initializer.
149 ///
150 /// It is recommended to use `add_subclass` instead of this method for most usage.
151 pub fn new(init: T, super_init: <T::BaseType as PyClassBaseType>::Initializer) -> Self {
152 Self(PyClassInitializerImpl::New { init, super_init })
153 }
154
155 /// Constructs a new initializer from an initializer for the base class.
156 ///
157 /// # Examples
158 /// ```
159 /// use pyo3::prelude::*;
160 ///
161 /// #[pyclass(subclass)]
162 /// struct BaseClass {
163 /// #[pyo3(get)]
164 /// value: i32,
165 /// }
166 ///
167 /// impl BaseClass {
168 /// fn new(value: i32) -> PyResult<Self> {
169 /// Ok(Self { value })
170 /// }
171 /// }
172 ///
173 /// #[pyclass(extends=BaseClass)]
174 /// struct SubClass {}
175 ///
176 /// #[pymethods]
177 /// impl SubClass {
178 /// #[new]
179 /// fn new(value: i32) -> PyResult<PyClassInitializer<Self>> {
180 /// let base_init = PyClassInitializer::from(BaseClass::new(value)?);
181 /// Ok(base_init.add_subclass(SubClass {}))
182 /// }
183 /// }
184 ///
185 /// fn main() -> PyResult<()> {
186 /// Python::with_gil(|py| {
187 /// let m = PyModule::new(py, "example")?;
188 /// m.add_class::<SubClass>()?;
189 /// m.add_class::<BaseClass>()?;
190 ///
191 /// let instance = m.getattr("SubClass")?.call1((92,))?;
192 ///
193 /// // `SubClass` does not have a `value` attribute, but `BaseClass` does.
194 /// let n = instance.getattr("value")?.extract::<i32>()?;
195 /// assert_eq!(n, 92);
196 ///
197 /// Ok(())
198 /// })
199 /// }
200 /// ```
201 pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
202 where
203 S: PyClass<BaseType = T>,
204 S::BaseType: PyClassBaseType<Initializer = Self>,
205 {
206 PyClassInitializer::new(subclass_value, self)
207 }
208
209 /// Creates a new PyCell and initializes it.
210 #[doc(hidden)]
211 pub fn create_cell(self, py: Python<'_>) -> PyResult<*mut PyCell<T>>
212 where
213 T: PyClass,
214 {
215 unsafe { self.create_cell_from_subtype(py, T::type_object_raw(py)) }
216 }
217
218 /// Creates a new PyCell and initializes it given a typeobject `subtype`.
219 /// Called by the Python `tp_new` implementation generated by a `#[new]` function in a `#[pymethods]` block.
220 ///
221 /// # Safety
222 /// `subtype` must be a valid pointer to the type object of T or a subclass.
223 #[doc(hidden)]
224 pub unsafe fn create_cell_from_subtype(
225 self,
226 py: Python<'_>,
227 subtype: *mut crate::ffi::PyTypeObject,
228 ) -> PyResult<*mut PyCell<T>>
229 where
230 T: PyClass,
231 {
232 self.into_new_object(py, subtype).map(|obj| obj as _)
233 }
234}
235
236impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
237 unsafe fn into_new_object(
238 self,
239 py: Python<'_>,
240 subtype: *mut PyTypeObject,
241 ) -> PyResult<*mut ffi::PyObject> {
242 /// Layout of a PyCell after base new has been called, but the contents have not yet been
243 /// written.
244 #[repr(C)]
245 struct PartiallyInitializedPyCell<T: PyClass> {
246 _ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase,
247 contents: MaybeUninit<PyCellContents<T>>,
248 }
249
250 let (init, super_init) = match self.0 {
251 PyClassInitializerImpl::Existing(value) => return Ok(value.into_ptr()),
252 PyClassInitializerImpl::New { init, super_init } => (init, super_init),
253 };
254
255 let obj = super_init.into_new_object(py, subtype)?;
256
257 let cell: *mut PartiallyInitializedPyCell<T> = obj as _;
258 std::ptr::write(
259 (*cell).contents.as_mut_ptr(),
260 PyCellContents {
261 value: ManuallyDrop::new(UnsafeCell::new(init)),
262 borrow_checker: <T::PyClassMutability as PyClassMutability>::Storage::new(),
263 thread_checker: T::ThreadChecker::new(),
264 dict: T::Dict::INIT,
265 weakref: T::WeakRef::INIT,
266 },
267 );
268 Ok(obj)
269 }
270
271 private_impl! {}
272}
273
274impl<T> From<T> for PyClassInitializer<T>
275where
276 T: PyClass,
277 T::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<T::BaseType>>,
278{
279 #[inline]
280 fn from(value: T) -> PyClassInitializer<T> {
281 Self::new(init:value, super_init:PyNativeTypeInitializer(PhantomData))
282 }
283}
284
285impl<S, B> From<(S, B)> for PyClassInitializer<S>
286where
287 S: PyClass<BaseType = B>,
288 B: PyClass,
289 B::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<B::BaseType>>,
290{
291 fn from(sub_and_base: (S, B)) -> PyClassInitializer<S> {
292 let (sub: S, base: B) = sub_and_base;
293 PyClassInitializer::from(base).add_subclass(subclass_value:sub)
294 }
295}
296
297impl<T: PyClass> From<Py<T>> for PyClassInitializer<T> {
298 #[inline]
299 fn from(value: Py<T>) -> PyClassInitializer<T> {
300 PyClassInitializer(PyClassInitializerImpl::Existing(value))
301 }
302}
303
304// Implementation used by proc macros to allow anything convertible to PyClassInitializer<T> to be
305// the return value of pyclass #[new] method (optionally wrapped in `Result<U, E>`).
306impl<T, U> IntoPyCallbackOutput<PyClassInitializer<T>> for U
307where
308 T: PyClass,
309 U: Into<PyClassInitializer<T>>,
310{
311 #[inline]
312 fn convert(self, _py: Python<'_>) -> PyResult<PyClassInitializer<T>> {
313 Ok(self.into())
314 }
315}
316