diff --git a/src/lib.rs b/src/lib.rs old mode 100644 new mode 100755 diff --git a/src/pyclass.rs b/src/pyclass.rs index 93124001..715cf0dd 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -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() -> *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::() 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 PyObjectLayout for PyClassShell { Some(&mut self.ob_base) } unsafe fn internal_ref_cast(obj: &PyAny) -> &T { - let shell = obj.as_ptr() as *const PyClassShell; + 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 as *mut PyClassShell; + 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 { NonNull::new(ptr).map(|p| { - &mut *(gil::register_owned(py, p).as_ptr() as *const PyClassShell as *mut _) + &mut *(gil::register_owned(py, p).as_ptr() as *const _ as *mut PyClassShell) }) } unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option { NonNull::new(ptr).map(|p| { - &mut *(gil::register_borrowed(py, p).as_ptr() as *const PyClassShell as *mut _) + &mut *(gil::register_borrowed(py, p).as_ptr() as *const _ as *mut PyClassShell) }) } } -/// Register new type in the python object system. +/// Register `T: PyClass` to Python interpreter. #[cfg(not(Py_LIMITED_API))] pub fn initialize_type(py: Python, module_name: Option<&str>) -> PyResult<*mut ffi::PyTypeObject> where diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index ef2d25fb..bc2904da 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -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: Sized { fn init_class(self, shell: &mut T::ConcreteLayout); + private_decl! {} } +/// Initializer for Python native type, like `PyDict`. pub struct PyNativeTypeInitializer(PhantomData); impl PyObjectInit for PyNativeTypeInitializer { fn init_class(self, _shell: &mut T::ConcreteLayout) {} + private_impl! {} } -/// An initializer for `PyClassShell`. +/// 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 PyObjectInit for PyNativeTypeInitializer { /// impl SubSubClass { /// #[new] /// fn new() -> PyClassInitializer { -/// 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 { } impl PyClassInitializer { + /// 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: ::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 { + /// Ok(Self { + /// value: std::convert::TryFrom::try_from(value)?, + /// }) + /// } + /// } + /// #[pyclass(extends=BaseClass)] + /// struct SubClass {} + /// + /// #[pymethods] + /// impl SubClass { + /// #[new] + /// fn new(value: i32) -> PyResult> { + /// let base_init = PyClassInitializer::from(BaseClass::new(value)?); + /// Ok(base_init.add_subclass(SubClass {})) + /// } + /// } + /// ``` pub fn add_subclass(self, subclass_value: S) -> PyClassInitializer where S: PyClass + PyTypeInfo, @@ -97,6 +136,7 @@ impl PyObjectInit for PyClassInitializer { super_init.init_class(super_obj); } } + private_impl! {} } impl From for PyClassInitializer diff --git a/src/pyclass_slots.rs b/src/pyclass_slots.rs index b79a546c..539e630e 100644 --- a/src/pyclass_slots.rs +++ b/src/pyclass_slots.rs @@ -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 = 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 = 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); diff --git a/src/type_object.rs b/src/type_object.rs index c35a190f..1aedc12d 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -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 { pub trait PyObjectSizedLayout: PyObjectLayout + 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; diff --git a/tests/test_dunder.rs b/tests/test_dunder.rs old mode 100644 new mode 100755 diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index b0b93ee0..4bbd3701 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -73,11 +73,9 @@ struct BaseClassWithResult { impl BaseClassWithResult { #[new] fn new(value: isize) -> PyResult { - if 0 <= value { - Ok(Self { _val: value as _ }) - } else { - Err(PyErr::new::(())) - } + 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