Refine tests and documents around pyclass.rs
This commit is contained in:
parent
451de182cb
commit
c57177a169
|
@ -1,4 +1,4 @@
|
|||
//! An experiment module which has all codes related only to #[pyclass]
|
||||
//! Traits and structs for `#[pyclass]`.
|
||||
use crate::class::methods::{PyMethodDefType, PyMethodsProtocol};
|
||||
use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject};
|
||||
use crate::pyclass_init::PyClassInitializer;
|
||||
|
@ -26,12 +26,20 @@ pub(crate) unsafe fn default_alloc<T: PyTypeInfo>() -> *mut ffi::PyObject {
|
|||
alloc(tp_ptr, 0)
|
||||
}
|
||||
|
||||
/// A trait that enables custom alloc/dealloc implementations for pyclasses.
|
||||
/// This trait enables custom alloc/dealloc implementations for `T: PyClass`.
|
||||
pub trait PyClassAlloc: PyTypeInfo + Sized {
|
||||
/// Allocate the actual field for `#[pyclass]`.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function must return a valid pointer to the Python heap.
|
||||
unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout {
|
||||
default_alloc::<Self>() as _
|
||||
}
|
||||
|
||||
/// Deallocate `#[pyclass]` on the Python heap.
|
||||
///
|
||||
/// # Safety
|
||||
/// `self_` must be a valid pointer to the Python heap.
|
||||
unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) {
|
||||
(*self_).py_drop(py);
|
||||
let obj = self_ as _;
|
||||
|
@ -188,11 +196,11 @@ impl<T: PyClass> PyObjectLayout<T> for PyClassShell<T> {
|
|||
Some(&mut self.ob_base)
|
||||
}
|
||||
unsafe fn internal_ref_cast(obj: &PyAny) -> &T {
|
||||
let shell = obj.as_ptr() as *const PyClassShell<T>;
|
||||
let shell = obj.as_ptr() as *const Self;
|
||||
&(*shell).pyclass
|
||||
}
|
||||
unsafe fn internal_mut_cast(obj: &PyAny) -> &mut T {
|
||||
let shell = obj.as_ptr() as *const PyClassShell<T> as *mut PyClassShell<T>;
|
||||
let shell = obj.as_ptr() as *const _ as *mut Self;
|
||||
&mut (*shell).pyclass
|
||||
}
|
||||
unsafe fn py_drop(&mut self, py: Python) {
|
||||
|
@ -258,17 +266,17 @@ where
|
|||
{
|
||||
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
NonNull::new(ptr).map(|p| {
|
||||
&mut *(gil::register_owned(py, p).as_ptr() as *const PyClassShell<T> as *mut _)
|
||||
&mut *(gil::register_owned(py, p).as_ptr() as *const _ as *mut PyClassShell<T>)
|
||||
})
|
||||
}
|
||||
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
|
||||
NonNull::new(ptr).map(|p| {
|
||||
&mut *(gil::register_borrowed(py, p).as_ptr() as *const PyClassShell<T> as *mut _)
|
||||
&mut *(gil::register_borrowed(py, p).as_ptr() as *const _ as *mut PyClassShell<T>)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Register new type in the python object system.
|
||||
/// Register `T: PyClass` to Python interpreter.
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
pub fn initialize_type<T>(py: Python, module_name: Option<&str>) -> PyResult<*mut ffi::PyTypeObject>
|
||||
where
|
||||
|
|
|
@ -1,22 +1,32 @@
|
|||
//! Initialization utilities for `#[pyclass]`.
|
||||
use crate::pyclass::{PyClass, PyClassShell};
|
||||
use crate::type_object::{PyObjectLayout, PyObjectSizedLayout, PyTypeInfo};
|
||||
use crate::{PyResult, Python};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Initializer for Python types.
|
||||
///
|
||||
/// This trait is intended to use internally for distinguishing `#[pyclass]` and
|
||||
/// Python native types.
|
||||
pub trait PyObjectInit<T: PyTypeInfo>: Sized {
|
||||
fn init_class(self, shell: &mut T::ConcreteLayout);
|
||||
private_decl! {}
|
||||
}
|
||||
|
||||
/// Initializer for Python native type, like `PyDict`.
|
||||
pub struct PyNativeTypeInitializer<T: PyTypeInfo>(PhantomData<T>);
|
||||
|
||||
impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
|
||||
fn init_class(self, _shell: &mut T::ConcreteLayout) {}
|
||||
private_impl! {}
|
||||
}
|
||||
|
||||
/// An initializer for `PyClassShell<T>`.
|
||||
/// Initializer for our `#[pyclass]` system.
|
||||
///
|
||||
/// You can use this type to initalize complicatedly nested `#[pyclass]`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use pyo3::prelude::*;
|
||||
/// # use pyo3::py_run;
|
||||
|
@ -40,9 +50,9 @@ impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
|
|||
/// impl SubSubClass {
|
||||
/// #[new]
|
||||
/// fn new() -> PyClassInitializer<Self> {
|
||||
/// let base_init = PyClassInitializer::from(BaseClass{basename: "base"});
|
||||
/// base_init.add_subclass(SubClass{subname: "sub"})
|
||||
/// .add_subclass(SubSubClass{subsubname: "subsub"})
|
||||
/// PyClassInitializer::from(BaseClass { basename: "base" })
|
||||
/// .add_subclass(SubClass { subname: "sub" })
|
||||
/// .add_subclass(SubSubClass { subsubname: "subsub" })
|
||||
/// }
|
||||
/// }
|
||||
/// let gil = Python::acquire_gil();
|
||||
|
@ -60,13 +70,42 @@ pub struct PyClassInitializer<T: PyClass> {
|
|||
}
|
||||
|
||||
impl<T: PyClass> PyClassInitializer<T> {
|
||||
/// Constract new initialzer from value `T` and base class' initializer.
|
||||
///
|
||||
/// We recommend to mainly use `add_subclass`, instead of directly call `new`.
|
||||
pub fn new(init: T, super_init: <T::BaseType as PyTypeInfo>::Initializer) -> Self {
|
||||
Self {
|
||||
init,
|
||||
super_init: super_init,
|
||||
}
|
||||
Self { init, super_init }
|
||||
}
|
||||
|
||||
/// Constructs a new initializer from base class' initializer.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # 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 {}))
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
|
||||
where
|
||||
S: PyClass + PyTypeInfo<BaseType = T>,
|
||||
|
@ -97,6 +136,7 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
|
|||
super_init.init_class(super_obj);
|
||||
}
|
||||
}
|
||||
private_impl! {}
|
||||
}
|
||||
|
||||
impl<T> From<T> for PyClassInitializer<T>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! This module contains additional fields pf pyclass
|
||||
//! This module contains additional fields for `#[pyclass]`..
|
||||
//! Mainly used by our proc-macro codes.
|
||||
use crate::{ffi, Python};
|
||||
|
||||
const POINTER_SIZE: isize = std::mem::size_of::<*mut ffi::PyObject>() as _;
|
||||
|
||||
/// Represents `__dict__`.
|
||||
/// Represents `__dict__` field for `#[pyclass]`.
|
||||
pub trait PyClassDict {
|
||||
const OFFSET: Option<isize> = None;
|
||||
fn new() -> Self;
|
||||
|
@ -11,7 +12,7 @@ pub trait PyClassDict {
|
|||
private_decl! {}
|
||||
}
|
||||
|
||||
/// Represents `__weakref__`.
|
||||
/// Represents `__weakref__` field for `#[pyclass]`.
|
||||
pub trait PyClassWeakRef {
|
||||
const OFFSET: Option<isize> = None;
|
||||
fn new() -> Self;
|
||||
|
@ -19,7 +20,7 @@ pub trait PyClassWeakRef {
|
|||
private_decl! {}
|
||||
}
|
||||
|
||||
/// Dummy slot means the function doesn't has such a feature.
|
||||
/// Zero-sized dummy field.
|
||||
pub struct PyClassDummySlot;
|
||||
|
||||
impl PyClassDict for PyClassDummySlot {
|
||||
|
@ -36,7 +37,9 @@ impl PyClassWeakRef for PyClassDummySlot {
|
|||
}
|
||||
}
|
||||
|
||||
/// actual dict field
|
||||
/// Actual dict field, which holds the pointer to `__dict__`.
|
||||
///
|
||||
/// `#[pyclass(dict)]` automatically adds this.
|
||||
#[repr(transparent)]
|
||||
pub struct PyClassDictSlot(*mut ffi::PyObject);
|
||||
|
||||
|
@ -53,7 +56,9 @@ impl PyClassDict for PyClassDictSlot {
|
|||
}
|
||||
}
|
||||
|
||||
/// actual weakref field
|
||||
/// Actual weakref field, which holds the pointer to `__weakref__`.
|
||||
///
|
||||
/// `#[pyclass(weakref)]` automatically adds this.
|
||||
#[repr(transparent)]
|
||||
pub struct PyClassWeakRefSlot(*mut ffi::PyObject);
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
//! Python type object information
|
||||
|
||||
use crate::instance::Py;
|
||||
|
@ -41,6 +40,7 @@ pub trait PyObjectLayout<T: PyTypeInfo> {
|
|||
pub trait PyObjectSizedLayout<T: PyTypeInfo>: PyObjectLayout<T> + Sized {}
|
||||
|
||||
/// Our custom type flags
|
||||
#[doc(hidden)]
|
||||
pub mod type_flags {
|
||||
/// type object supports python GC
|
||||
pub const GC: usize = 1;
|
||||
|
@ -59,6 +59,7 @@ pub mod type_flags {
|
|||
}
|
||||
|
||||
/// Python type information.
|
||||
/// All Python native types(e.g., `PyDict`) and `#[pyclass]` structs implement this trait.
|
||||
pub trait PyTypeInfo: Sized {
|
||||
/// Type of objects to store in PyObject struct
|
||||
type Type;
|
||||
|
|
|
@ -73,11 +73,9 @@ struct BaseClassWithResult {
|
|||
impl BaseClassWithResult {
|
||||
#[new]
|
||||
fn new(value: isize) -> PyResult<Self> {
|
||||
if 0 <= value {
|
||||
Ok(Self { _val: value as _ })
|
||||
} else {
|
||||
Err(PyErr::new::<pyo3::exceptions::RuntimeError, _>(()))
|
||||
}
|
||||
Ok(Self {
|
||||
_val: std::convert::TryFrom::try_from(value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +103,7 @@ fn handle_result_in_new() {
|
|||
try:
|
||||
subclass(-10)
|
||||
assert Fals
|
||||
except RuntimeError as e:
|
||||
except ValueError as e:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
|
Loading…
Reference in New Issue