pyo3/src/pyclass_init.rs

194 lines
5.8 KiB
Rust
Raw Normal View History

//! Initialization utilities for `#[pyclass]`.
use crate::callback::IntoPyCallbackOutput;
use crate::type_object::{PyBorrowFlagLayout, PyLayout, PySizedLayout, PyTypeInfo};
use crate::{PyCell, PyClass, PyResult, Python};
2020-01-05 07:01:05 +00:00
use std::marker::PhantomData;
/// Initializer for Python types.
///
/// This trait is intended to use internally for distinguishing `#[pyclass]` and
/// Python native types.
2020-01-05 07:01:05 +00:00
pub trait PyObjectInit<T: PyTypeInfo>: Sized {
fn init_class<L: PyLayout<T>>(self, layout: &mut L);
private_decl! {}
2020-01-05 07:01:05 +00:00
}
/// Initializer for Python native type, 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> {
fn init_class<L: PyLayout<T>>(self, _layout: &mut L) {}
private_impl! {}
2020-01-05 07:01:05 +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-05 07:01:05 +00:00
/// ```
/// # use pyo3::prelude::*;
/// # use pyo3::py_run;
/// #[pyclass(subclass)]
2020-01-05 07:01:05 +00:00
/// struct BaseClass {
/// #[pyo3(get)]
/// basename: &'static str,
/// }
/// #[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> {
/// PyClassInitializer::from(BaseClass { basename: "base" })
/// .add_subclass(SubClass { subname: "sub" })
/// .add_subclass(SubSubClass { subsubname: "subsub" })
2020-01-05 07:01:05 +00:00
/// }
/// }
/// let gil = Python::acquire_gil();
/// let py = gil.python();
/// let typeobj = py.get_type::<SubSubClass>();
/// let inst = typeobj.call((), None).unwrap();
/// py_run!(py, inst, r#"
/// assert inst.basename == 'base'
/// assert inst.subname == 'sub'
/// assert inst.subsubname == 'subsub'"#);
/// ```
pub struct PyClassInitializer<T: PyClass> {
init: T,
2020-01-07 08:37:29 +00:00
super_init: <T::BaseType as PyTypeInfo>::Initializer,
2020-01-05 07:01:05 +00:00
}
impl<T: PyClass> PyClassInitializer<T> {
2020-08-27 11:27:16 +00:00
/// Construct new initializer from value `T` and base class' initializer.
///
/// We recommend to mainly use `add_subclass`, instead of directly call `new`.
2020-01-05 07:01:05 +00:00
pub fn new(init: T, super_init: <T::BaseType as PyTypeInfo>::Initializer) -> Self {
Self { init, super_init }
2020-01-05 07:01:05 +00:00
}
/// Constructs a new initializer from base class' initializer.
///
2021-03-20 07:45:56 +00:00
/// # Examples
/// ```
/// # use pyo3::prelude::*;
/// #[pyclass]
/// struct BaseClass {
/// value: u32,
/// }
///
/// impl BaseClass {
/// fn new(value: i32) -> PyResult<Self> {
/// Ok(Self {
/// value: std::convert::TryFrom::try_from(value)?,
/// })
/// }
/// }
/// #[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 {}))
/// }
/// }
/// ```
2020-01-05 07:01:05 +00:00
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
where
S: PyClass + PyTypeInfo<BaseType = T>,
S::BaseLayout: PySizedLayout<T>,
2020-01-05 07:01:05 +00:00
S::BaseType: PyTypeInfo<Initializer = Self>,
{
PyClassInitializer::new(subclass_value, self)
}
2020-06-22 14:49:33 +00:00
/// Create a new PyCell and initialize it.
#[doc(hidden)]
pub fn create_cell(self, py: Python) -> PyResult<*mut PyCell<T>>
2020-01-05 07:01:05 +00:00
where
T: PyClass,
T::BaseLayout: PyBorrowFlagLayout<T::BaseType>,
2020-01-05 07:01:05 +00:00
{
unsafe { self.create_cell_from_subtype(py, T::type_object_raw(py)) }
2020-06-21 14:38:26 +00:00
}
/// Create a new PyCell and initialize it given a typeobject `subtype`.
/// Called by our `tp_new` generated by the `#[new]` attribute.
///
/// # Safety
2020-06-22 14:49:33 +00:00
/// `subtype` must be a subclass of T.
#[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,
T::BaseLayout: PyBorrowFlagLayout<T::BaseType>,
{
let cell = PyCell::internal_new(py, subtype)?;
2020-02-03 13:25:16 +00:00
self.init_class(&mut *cell);
Ok(cell)
2020-01-05 07:01:05 +00:00
}
}
impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
fn init_class<L: PyLayout<T>>(self, layout: &mut L) {
2020-01-05 07:01:05 +00:00
let Self { init, super_init } = self;
layout.py_init(init);
2020-03-01 03:58:28 +00:00
if let Some(super_obj) = layout.get_super() {
2020-01-05 07:01:05 +00:00
super_init.init_class(super_obj);
}
}
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,
T::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<T::BaseType>>,
{
2020-01-06 13:14:41 +00:00
fn from(value: T) -> PyClassInitializer<T> {
Self::new(value, PyNativeTypeInitializer(PhantomData))
2020-01-05 07:01:05 +00:00
}
}
impl<S, B> From<(S, B)> for PyClassInitializer<S>
2020-01-05 07:01:05 +00:00
where
S: PyClass + PyTypeInfo<BaseType = B>,
S::BaseLayout: PySizedLayout<B>,
2020-01-05 07:01:05 +00:00
B: PyClass + PyTypeInfo<Initializer = PyClassInitializer<B>>,
B::BaseType: PyTypeInfo<Initializer = PyNativeTypeInitializer<B::BaseType>>,
{
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
// 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>>,
{
fn convert(self, _py: Python) -> PyResult<PyClassInitializer<T>> {
Ok(self.into())
2020-06-22 16:16:44 +00:00
}
}