2021-09-13 20:58:15 +00:00
|
|
|
//! Contains initialization utilities for `#[pyclass]`.
|
2021-05-16 23:31:05 +00:00
|
|
|
use crate::class::impl_::PyClassThreadChecker;
|
|
|
|
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
|
2021-05-04 20:10:03 +00:00
|
|
|
use crate::{callback::IntoPyCallbackOutput, class::impl_::PyClassBaseType};
|
2021-05-16 23:31:05 +00:00
|
|
|
use crate::{ffi, PyCell, PyClass, PyErr, PyResult, Python};
|
|
|
|
use crate::{
|
|
|
|
ffi::PyTypeObject,
|
|
|
|
pycell::{BorrowFlag, PyCellContents},
|
|
|
|
type_object::{get_tp_alloc, PyTypeInfo},
|
|
|
|
};
|
|
|
|
use std::{
|
|
|
|
cell::{Cell, UnsafeCell},
|
|
|
|
marker::PhantomData,
|
|
|
|
mem::{ManuallyDrop, MaybeUninit},
|
|
|
|
};
|
2020-01-05 07:01:05 +00:00
|
|
|
|
2020-01-08 13:44:50 +00:00
|
|
|
/// Initializer for Python types.
|
|
|
|
///
|
|
|
|
/// This trait is intended to use internally for distinguishing `#[pyclass]` and
|
|
|
|
/// Python native types.
|
2021-05-04 20:10:03 +00:00
|
|
|
pub trait PyObjectInit<T>: Sized {
|
2021-06-23 08:18:33 +00:00
|
|
|
/// # Safety
|
|
|
|
/// - `subtype` must be a valid pointer to a type object of T or a subclass.
|
2021-05-16 23:31:05 +00:00
|
|
|
unsafe fn into_new_object(
|
|
|
|
self,
|
|
|
|
py: Python,
|
|
|
|
subtype: *mut PyTypeObject,
|
|
|
|
) -> PyResult<*mut ffi::PyObject>;
|
2020-01-08 13:44:50 +00:00
|
|
|
private_decl! {}
|
2020-01-05 07:01:05 +00:00
|
|
|
}
|
|
|
|
|
2021-08-01 07:05:27 +00:00
|
|
|
/// Initializer for Python native types, like `PyDict`.
|
2020-01-05 07:01:05 +00:00
|
|
|
pub struct PyNativeTypeInitializer<T: PyTypeInfo>(PhantomData<T>);
|
|
|
|
|
|
|
|
impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
|
2021-05-16 23:31:05 +00:00
|
|
|
unsafe fn into_new_object(
|
|
|
|
self,
|
|
|
|
py: Python,
|
|
|
|
subtype: *mut PyTypeObject,
|
|
|
|
) -> PyResult<*mut ffi::PyObject> {
|
|
|
|
let type_object = T::type_object_raw(py);
|
|
|
|
|
|
|
|
// HACK (due to FIXME below): PyBaseObject_Type's tp_new isn't happy with NULL arguments
|
|
|
|
if type_object == (&ffi::PyBaseObject_Type as *const _ as *mut _) {
|
|
|
|
let alloc = get_tp_alloc(subtype).unwrap_or(ffi::PyType_GenericAlloc);
|
|
|
|
let obj = alloc(subtype, 0);
|
|
|
|
return if obj.is_null() {
|
2021-10-30 09:51:02 +00:00
|
|
|
Err(PyErr::fetch(py))
|
2021-05-16 23:31:05 +00:00
|
|
|
} else {
|
|
|
|
Ok(obj)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(Py_LIMITED_API)]
|
|
|
|
unreachable!("subclassing native types is not possible with the `abi3` feature");
|
|
|
|
|
|
|
|
#[cfg(not(Py_LIMITED_API))]
|
|
|
|
{
|
|
|
|
match (*type_object).tp_new {
|
|
|
|
// FIXME: Call __new__ with actual arguments
|
|
|
|
Some(newfunc) => {
|
|
|
|
let obj = newfunc(subtype, std::ptr::null_mut(), std::ptr::null_mut());
|
|
|
|
if obj.is_null() {
|
2021-10-30 09:51:02 +00:00
|
|
|
Err(PyErr::fetch(py))
|
2021-05-16 23:31:05 +00:00
|
|
|
} else {
|
|
|
|
Ok(obj)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => Err(crate::exceptions::PyTypeError::new_err(
|
|
|
|
"base type without tp_new",
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-08 13:44:50 +00:00
|
|
|
private_impl! {}
|
2020-01-05 07:01:05 +00:00
|
|
|
}
|
|
|
|
|
2020-01-08 13:44:50 +00:00
|
|
|
/// Initializer for our `#[pyclass]` system.
|
2020-01-05 07:01:05 +00:00
|
|
|
///
|
|
|
|
/// You can use this type to initalize complicatedly nested `#[pyclass]`.
|
|
|
|
///
|
2021-03-20 07:45:56 +00:00
|
|
|
/// # Examples
|
2020-01-08 13:44:50 +00:00
|
|
|
///
|
2020-01-05 07:01:05 +00:00
|
|
|
/// ```
|
|
|
|
/// # use pyo3::prelude::*;
|
|
|
|
/// # use pyo3::py_run;
|
2020-09-01 00:36:26 +00:00
|
|
|
/// #[pyclass(subclass)]
|
2020-01-05 07:01:05 +00:00
|
|
|
/// struct BaseClass {
|
|
|
|
/// #[pyo3(get)]
|
|
|
|
/// basename: &'static str,
|
|
|
|
/// }
|
2020-09-01 00:36:26 +00:00
|
|
|
/// #[pyclass(extends=BaseClass, subclass)]
|
2020-01-05 07:01:05 +00:00
|
|
|
/// struct SubClass {
|
|
|
|
/// #[pyo3(get)]
|
|
|
|
/// subname: &'static str,
|
|
|
|
/// }
|
|
|
|
/// #[pyclass(extends=SubClass)]
|
|
|
|
/// struct SubSubClass {
|
|
|
|
/// #[pyo3(get)]
|
|
|
|
/// subsubname: &'static str,
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// #[pymethods]
|
|
|
|
/// impl SubSubClass {
|
|
|
|
/// #[new]
|
|
|
|
/// fn new() -> PyClassInitializer<Self> {
|
2020-01-08 13:44:50 +00:00
|
|
|
/// PyClassInitializer::from(BaseClass { basename: "base" })
|
|
|
|
/// .add_subclass(SubClass { subname: "sub" })
|
2021-10-14 21:15:25 +00:00
|
|
|
/// .add_subclass(SubSubClass {
|
|
|
|
/// subsubname: "subsub",
|
|
|
|
/// })
|
2020-01-05 07:01:05 +00:00
|
|
|
/// }
|
|
|
|
/// }
|
2021-03-20 07:44:28 +00:00
|
|
|
/// Python::with_gil(|py| {
|
|
|
|
/// let typeobj = py.get_type::<SubSubClass>();
|
|
|
|
/// let sub_sub_class = typeobj.call((), None).unwrap();
|
|
|
|
/// py_run!(
|
|
|
|
/// py,
|
|
|
|
/// sub_sub_class,
|
|
|
|
/// r#"
|
|
|
|
/// assert sub_sub_class.basename == 'base'
|
|
|
|
/// assert sub_sub_class.subname == 'sub'
|
|
|
|
/// assert sub_sub_class.subsubname == 'subsub'"#
|
|
|
|
/// );
|
|
|
|
/// });
|
2020-01-05 07:01:05 +00:00
|
|
|
/// ```
|
|
|
|
pub struct PyClassInitializer<T: PyClass> {
|
|
|
|
init: T,
|
2021-05-04 20:10:03 +00:00
|
|
|
super_init: <T::BaseType as PyClassBaseType>::Initializer,
|
2020-01-05 07:01:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: PyClass> PyClassInitializer<T> {
|
2021-08-01 07:05:27 +00:00
|
|
|
/// Constructs a new initializer from value `T` and base class' initializer.
|
2020-01-08 13:44:50 +00:00
|
|
|
///
|
2021-08-01 08:11:46 +00:00
|
|
|
/// It is recommended to use `add_subclass` instead of this method for most usage.
|
2021-05-04 20:10:03 +00:00
|
|
|
pub fn new(init: T, super_init: <T::BaseType as PyClassBaseType>::Initializer) -> Self {
|
2020-01-08 13:44:50 +00:00
|
|
|
Self { init, super_init }
|
2020-01-05 07:01:05 +00:00
|
|
|
}
|
|
|
|
|
2021-08-01 07:05:27 +00:00
|
|
|
/// Constructs a new initializer from an initializer for the base class.
|
2020-01-08 13:44:50 +00:00
|
|
|
///
|
2021-03-20 07:45:56 +00:00
|
|
|
/// # Examples
|
2020-01-08 13:44:50 +00:00
|
|
|
/// ```
|
2021-10-14 21:15:25 +00:00
|
|
|
/// use pyo3::prelude::*;
|
|
|
|
///
|
|
|
|
/// #[pyclass(subclass)]
|
2020-01-08 13:44:50 +00:00
|
|
|
/// struct BaseClass {
|
2021-10-14 21:15:25 +00:00
|
|
|
/// #[pyo3(get)]
|
|
|
|
/// value: i32,
|
2020-01-08 13:44:50 +00:00
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl BaseClass {
|
|
|
|
/// fn new(value: i32) -> PyResult<Self> {
|
2021-10-14 21:15:25 +00:00
|
|
|
/// Ok(Self { value })
|
2020-01-08 13:44:50 +00:00
|
|
|
/// }
|
|
|
|
/// }
|
2021-10-14 21:15:25 +00:00
|
|
|
///
|
2020-01-08 13:44:50 +00:00
|
|
|
/// #[pyclass(extends=BaseClass)]
|
|
|
|
/// struct SubClass {}
|
|
|
|
///
|
|
|
|
/// #[pymethods]
|
|
|
|
/// impl SubClass {
|
|
|
|
/// #[new]
|
|
|
|
/// fn new(value: i32) -> PyResult<PyClassInitializer<Self>> {
|
|
|
|
/// let base_init = PyClassInitializer::from(BaseClass::new(value)?);
|
|
|
|
/// Ok(base_init.add_subclass(SubClass {}))
|
|
|
|
/// }
|
|
|
|
/// }
|
2021-10-14 21:15:25 +00:00
|
|
|
///
|
|
|
|
/// fn main() -> PyResult<()> {
|
|
|
|
/// Python::with_gil(|py| {
|
|
|
|
/// let m = PyModule::new(py, "example")?;
|
|
|
|
/// m.add_class::<SubClass>()?;
|
|
|
|
/// m.add_class::<BaseClass>()?;
|
|
|
|
///
|
|
|
|
/// let instance = m.getattr("SubClass")?.call1((92,))?;
|
|
|
|
///
|
|
|
|
/// // `SubClass` does not have a `value` attribute, but `BaseClass` does.
|
|
|
|
/// let n = instance.getattr("value")?.extract::<i32>()?;
|
|
|
|
/// assert_eq!(n, 92);
|
|
|
|
///
|
|
|
|
/// Ok(())
|
|
|
|
/// })
|
|
|
|
/// }
|
2020-01-08 13:44:50 +00:00
|
|
|
/// ```
|
2020-01-05 07:01:05 +00:00
|
|
|
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
|
|
|
|
where
|
2021-05-04 20:10:03 +00:00
|
|
|
S: PyClass<BaseType = T>,
|
|
|
|
S::BaseType: PyClassBaseType<Initializer = Self>,
|
2020-01-05 07:01:05 +00:00
|
|
|
{
|
|
|
|
PyClassInitializer::new(subclass_value, self)
|
|
|
|
}
|
|
|
|
|
2021-08-01 07:05:27 +00:00
|
|
|
/// Creates a new PyCell and initializes it.
|
2020-06-22 14:49:33 +00:00
|
|
|
#[doc(hidden)]
|
|
|
|
pub fn create_cell(self, py: Python) -> PyResult<*mut PyCell<T>>
|
2020-01-05 07:01:05 +00:00
|
|
|
where
|
|
|
|
T: PyClass,
|
|
|
|
{
|
2020-06-22 20:34:42 +00:00
|
|
|
unsafe { self.create_cell_from_subtype(py, T::type_object_raw(py)) }
|
2020-06-21 14:38:26 +00:00
|
|
|
}
|
|
|
|
|
2021-08-01 07:05:27 +00:00
|
|
|
/// Creates a new PyCell and initializes it given a typeobject `subtype`.
|
2021-08-01 08:11:46 +00:00
|
|
|
/// Called by the Python `tp_new` implementation generated by a `#[new]` function in a `#[pymethods]` block.
|
2020-06-21 17:38:08 +00:00
|
|
|
///
|
|
|
|
/// # Safety
|
2021-06-23 08:18:33 +00:00
|
|
|
/// `subtype` must be a valid pointer to the type object of T or a subclass.
|
2020-06-22 14:49:33 +00:00
|
|
|
#[doc(hidden)]
|
2020-06-21 14:38:26 +00:00
|
|
|
pub unsafe fn create_cell_from_subtype(
|
|
|
|
self,
|
|
|
|
py: Python,
|
|
|
|
subtype: *mut crate::ffi::PyTypeObject,
|
|
|
|
) -> PyResult<*mut PyCell<T>>
|
|
|
|
where
|
|
|
|
T: PyClass,
|
|
|
|
{
|
2021-05-16 23:31:05 +00:00
|
|
|
self.into_new_object(py, subtype).map(|obj| obj as _)
|
2020-01-05 07:01:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
|
2021-05-16 23:31:05 +00:00
|
|
|
unsafe fn into_new_object(
|
|
|
|
self,
|
|
|
|
py: Python,
|
|
|
|
subtype: *mut PyTypeObject,
|
|
|
|
) -> PyResult<*mut ffi::PyObject> {
|
2021-08-01 07:05:27 +00:00
|
|
|
/// Layout of a PyCellBase after base new has been called, but the borrow flag has not
|
|
|
|
/// yet been initialized.
|
2021-05-16 23:31:05 +00:00
|
|
|
#[repr(C)]
|
|
|
|
struct PartiallyInitializedPyCellBase<T> {
|
|
|
|
_ob_base: T,
|
|
|
|
borrow_flag: MaybeUninit<Cell<BorrowFlag>>,
|
|
|
|
}
|
|
|
|
|
2021-08-01 07:05:27 +00:00
|
|
|
/// Layout of a PyCell after base new has been called, but the contents have not yet been
|
2021-05-16 23:31:05 +00:00
|
|
|
/// written.
|
|
|
|
#[repr(C)]
|
|
|
|
struct PartiallyInitializedPyCell<T: PyClass> {
|
|
|
|
_ob_base: <T::BaseType as PyClassBaseType>::LayoutAsBase,
|
|
|
|
contents: MaybeUninit<PyCellContents<T>>,
|
|
|
|
}
|
|
|
|
|
2020-01-05 07:01:05 +00:00
|
|
|
let Self { init, super_init } = self;
|
2021-05-16 23:31:05 +00:00
|
|
|
let obj = super_init.into_new_object(py, subtype)?;
|
|
|
|
|
|
|
|
// FIXME: Only need to initialize borrow flag once per whole hierarchy
|
|
|
|
let base: *mut PartiallyInitializedPyCellBase<T::BaseNativeType> = obj as _;
|
|
|
|
std::ptr::write(
|
|
|
|
(*base).borrow_flag.as_mut_ptr(),
|
|
|
|
Cell::new(BorrowFlag::UNUSED),
|
|
|
|
);
|
|
|
|
|
|
|
|
// FIXME: Initialize borrow flag if necessary??
|
|
|
|
let cell: *mut PartiallyInitializedPyCell<T> = obj as _;
|
|
|
|
std::ptr::write(
|
|
|
|
(*cell).contents.as_mut_ptr(),
|
|
|
|
PyCellContents {
|
|
|
|
value: ManuallyDrop::new(UnsafeCell::new(init)),
|
|
|
|
thread_checker: T::ThreadChecker::new(),
|
|
|
|
dict: T::Dict::new(),
|
|
|
|
weakref: T::WeakRef::new(),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
Ok(obj)
|
2020-01-05 07:01:05 +00:00
|
|
|
}
|
2021-05-16 23:31:05 +00:00
|
|
|
|
2020-01-08 13:44:50 +00:00
|
|
|
private_impl! {}
|
2020-01-05 07:01:05 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 13:14:41 +00:00
|
|
|
impl<T> From<T> for PyClassInitializer<T>
|
2020-01-05 07:01:05 +00:00
|
|
|
where
|
|
|
|
T: PyClass,
|
2021-05-04 20:10:03 +00:00
|
|
|
T::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<T::BaseType>>,
|
2020-01-05 07:01:05 +00:00
|
|
|
{
|
2021-05-16 23:31:05 +00:00
|
|
|
#[inline]
|
2020-01-06 13:14:41 +00:00
|
|
|
fn from(value: T) -> PyClassInitializer<T> {
|
2020-01-07 03:49:36 +00:00
|
|
|
Self::new(value, PyNativeTypeInitializer(PhantomData))
|
2020-01-05 07:01:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-07 04:07:14 +00:00
|
|
|
impl<S, B> From<(S, B)> for PyClassInitializer<S>
|
2020-01-05 07:01:05 +00:00
|
|
|
where
|
2021-05-04 20:10:03 +00:00
|
|
|
S: PyClass<BaseType = B>,
|
|
|
|
B: PyClass,
|
|
|
|
B::BaseType: PyClassBaseType<Initializer = PyNativeTypeInitializer<B::BaseType>>,
|
2020-01-05 07:01:05 +00:00
|
|
|
{
|
2020-01-07 04:07:14 +00:00
|
|
|
fn from(sub_and_base: (S, B)) -> PyClassInitializer<S> {
|
|
|
|
let (sub, base) = sub_and_base;
|
|
|
|
PyClassInitializer::from(base).add_subclass(sub)
|
2020-01-05 07:01:05 +00:00
|
|
|
}
|
|
|
|
}
|
2020-06-22 16:16:44 +00:00
|
|
|
|
2020-12-13 22:51:19 +00:00
|
|
|
// Implementation used by proc macros to allow anything convertible to PyClassInitializer<T> to be
|
|
|
|
// the return value of pyclass #[new] method (optionally wrapped in `Result<U, E>`).
|
|
|
|
impl<T, U> IntoPyCallbackOutput<PyClassInitializer<T>> for U
|
2020-06-22 16:16:44 +00:00
|
|
|
where
|
|
|
|
T: PyClass,
|
|
|
|
U: Into<PyClassInitializer<T>>,
|
|
|
|
{
|
2021-05-16 23:31:05 +00:00
|
|
|
#[inline]
|
2020-12-13 22:51:19 +00:00
|
|
|
fn convert(self, _py: Python) -> PyResult<PyClassInitializer<T>> {
|
|
|
|
Ok(self.into())
|
2020-06-22 16:16:44 +00:00
|
|
|
}
|
|
|
|
}
|