#![allow(clippy::undocumented_unsafe_blocks)]
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::impl_::pyclass::{PyClassBaseType, PyClassImpl};
use crate::impl_::pyclass_init::PyNativeTypeInitializer;
use crate::internal::pyclass_init::PyObjectInit;
use crate::pycell::impl_::PyClassObjectLayout;
use crate::{ffi, Bound, PyClass, PyResult, Python};
use crate::{ffi::PyTypeObject, pycell::impl_::PyClassObjectContents};
use core::marker::PhantomData;
pub struct PyClassInitializer<T: PyClass> {
init: T,
super_init: <T::BaseType as PyClassBaseType>::Initializer,
}
impl<T: PyClass> PyClassInitializer<T> {
#[track_caller]
#[inline]
pub fn new(init: T, super_init: <T::BaseType as PyClassBaseType>::Initializer) -> Self {
Self { init, super_init }
}
#[track_caller]
#[inline]
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
where
T: PyClassBaseType<Initializer = Self>,
S: PyClass<BaseType = T>,
{
PyClassInitializer::new(subclass_value, self)
}
pub(crate) fn create_class_object(self, py: Python<'_>) -> PyResult<Bound<'_, T>>
where
T: PyClass,
{
unsafe { self.create_class_object_of_type(py, T::type_object_raw(py)) }
}
pub(crate) unsafe fn create_class_object_of_type(
self,
py: Python<'_>,
target_type: *mut crate::ffi::PyTypeObject,
) -> PyResult<Bound<'_, T>>
where
T: PyClass,
{
let obj = unsafe { self.super_init.into_new_object(py, target_type)? };
let contents = unsafe { <T as PyClassImpl>::Layout::contents_uninit(obj) };
let new_contents = PyClassObjectContents::new(self.init);
#[cfg(not(Py_3_13))]
let new_contents = {
let mut new_contents = new_contents;
if eagerly_created_dict_possible::<T>(py) {
unsafe {
let contents_ptr = (*contents).as_mut_ptr();
let dict_ptr = &raw const (*contents_ptr).dict;
new_contents.dict = core::ptr::read(dict_ptr);
}
}
new_contents
};
unsafe { (*contents).write(new_contents) };
Ok(unsafe { obj.assume_owned(py).cast_into_unchecked() })
}
}
#[cfg(not(Py_3_13))]
#[inline]
fn eagerly_created_dict_possible<T: PyClassImpl>(py: Python<'_>) -> bool {
if core::mem::size_of::<T::Dict>() == 0 {
return false;
}
cfg_select! {
Py_LIMITED_API =>
{
use crate::sync::PyOnceLock;
static IS_PYTHON_3_11_OR_3_12: PyOnceLock<bool> = PyOnceLock::new();
*IS_PYTHON_3_11_OR_3_12.get_or_init(py, || {
let version_info = py.version_info();
matches!((version_info.major, version_info.minor), (3, 11) | (3, 12))
})
}
not(Py_LIMITED_API) =>
{
let _ = py;
cfg!(Py_3_11)
}
}
}
impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
unsafe fn into_new_object(
self,
py: Python<'_>,
subtype: *mut PyTypeObject,
) -> PyResult<*mut ffi::PyObject> {
unsafe {
self.create_class_object_of_type(py, subtype)
.map(Bound::into_ptr)
}
}
}
impl<T> From<T> for PyClassInitializer<T>
where
T: PyClass,
T::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<T::BaseType>>,
{
#[inline]
fn from(value: T) -> PyClassInitializer<T> {
Self::new(value, PyNativeTypeInitializer(PhantomData))
}
}
impl<S, B> From<(S, B)> for PyClassInitializer<S>
where
S: PyClass<BaseType = B>,
B: PyClass + PyClassBaseType<Initializer = PyClassInitializer<B>>,
B::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<B::BaseType>>,
{
#[track_caller]
#[inline]
fn from(sub_and_base: (S, B)) -> PyClassInitializer<S> {
let (sub, base) = sub_and_base;
PyClassInitializer::from(base).add_subclass(sub)
}
}