Skip to content

Commit c57177a

Browse files
committed
Refine tests and documents around pyclass.rs
1 parent 451de18 commit c57177a

File tree

7 files changed

+80
-28
lines changed

7 files changed

+80
-28
lines changed

src/lib.rs

100644100755
File mode changed.

src/pyclass.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! An experiment module which has all codes related only to #[pyclass]
1+
//! Traits and structs for `#[pyclass]`.
22
use crate::class::methods::{PyMethodDefType, PyMethodsProtocol};
33
use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject};
44
use crate::pyclass_init::PyClassInitializer;
@@ -26,12 +26,20 @@ pub(crate) unsafe fn default_alloc<T: PyTypeInfo>() -> *mut ffi::PyObject {
2626
alloc(tp_ptr, 0)
2727
}
2828

29-
/// A trait that enables custom alloc/dealloc implementations for pyclasses.
29+
/// This trait enables custom alloc/dealloc implementations for `T: PyClass`.
3030
pub trait PyClassAlloc: PyTypeInfo + Sized {
31+
/// Allocate the actual field for `#[pyclass]`.
32+
///
33+
/// # Safety
34+
/// This function must return a valid pointer to the Python heap.
3135
unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout {
3236
default_alloc::<Self>() as _
3337
}
3438

39+
/// Deallocate `#[pyclass]` on the Python heap.
40+
///
41+
/// # Safety
42+
/// `self_` must be a valid pointer to the Python heap.
3543
unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) {
3644
(*self_).py_drop(py);
3745
let obj = self_ as _;
@@ -188,11 +196,11 @@ impl<T: PyClass> PyObjectLayout<T> for PyClassShell<T> {
188196
Some(&mut self.ob_base)
189197
}
190198
unsafe fn internal_ref_cast(obj: &PyAny) -> &T {
191-
let shell = obj.as_ptr() as *const PyClassShell<T>;
199+
let shell = obj.as_ptr() as *const Self;
192200
&(*shell).pyclass
193201
}
194202
unsafe fn internal_mut_cast(obj: &PyAny) -> &mut T {
195-
let shell = obj.as_ptr() as *const PyClassShell<T> as *mut PyClassShell<T>;
203+
let shell = obj.as_ptr() as *const _ as *mut Self;
196204
&mut (*shell).pyclass
197205
}
198206
unsafe fn py_drop(&mut self, py: Python) {
@@ -258,17 +266,17 @@ where
258266
{
259267
unsafe fn from_owned_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
260268
NonNull::new(ptr).map(|p| {
261-
&mut *(gil::register_owned(py, p).as_ptr() as *const PyClassShell<T> as *mut _)
269+
&mut *(gil::register_owned(py, p).as_ptr() as *const _ as *mut PyClassShell<T>)
262270
})
263271
}
264272
unsafe fn from_borrowed_ptr_or_opt(py: Python<'p>, ptr: *mut ffi::PyObject) -> Option<Self> {
265273
NonNull::new(ptr).map(|p| {
266-
&mut *(gil::register_borrowed(py, p).as_ptr() as *const PyClassShell<T> as *mut _)
274+
&mut *(gil::register_borrowed(py, p).as_ptr() as *const _ as *mut PyClassShell<T>)
267275
})
268276
}
269277
}
270278

271-
/// Register new type in the python object system.
279+
/// Register `T: PyClass` to Python interpreter.
272280
#[cfg(not(Py_LIMITED_API))]
273281
pub fn initialize_type<T>(py: Python, module_name: Option<&str>) -> PyResult<*mut ffi::PyTypeObject>
274282
where

src/pyclass_init.rs

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
1+
//! Initialization utilities for `#[pyclass]`.
12
use crate::pyclass::{PyClass, PyClassShell};
23
use crate::type_object::{PyObjectLayout, PyObjectSizedLayout, PyTypeInfo};
34
use crate::{PyResult, Python};
45
use std::marker::PhantomData;
56

7+
/// Initializer for Python types.
8+
///
9+
/// This trait is intended to use internally for distinguishing `#[pyclass]` and
10+
/// Python native types.
611
pub trait PyObjectInit<T: PyTypeInfo>: Sized {
712
fn init_class(self, shell: &mut T::ConcreteLayout);
13+
private_decl! {}
814
}
915

16+
/// Initializer for Python native type, like `PyDict`.
1017
pub struct PyNativeTypeInitializer<T: PyTypeInfo>(PhantomData<T>);
1118

1219
impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
1320
fn init_class(self, _shell: &mut T::ConcreteLayout) {}
21+
private_impl! {}
1422
}
1523

16-
/// An initializer for `PyClassShell<T>`.
24+
/// Initializer for our `#[pyclass]` system.
1725
///
1826
/// You can use this type to initalize complicatedly nested `#[pyclass]`.
1927
///
28+
/// # Example
29+
///
2030
/// ```
2131
/// # use pyo3::prelude::*;
2232
/// # use pyo3::py_run;
@@ -40,9 +50,9 @@ impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
4050
/// impl SubSubClass {
4151
/// #[new]
4252
/// fn new() -> PyClassInitializer<Self> {
43-
/// let base_init = PyClassInitializer::from(BaseClass{basename: "base"});
44-
/// base_init.add_subclass(SubClass{subname: "sub"})
45-
/// .add_subclass(SubSubClass{subsubname: "subsub"})
53+
/// PyClassInitializer::from(BaseClass { basename: "base" })
54+
/// .add_subclass(SubClass { subname: "sub" })
55+
/// .add_subclass(SubSubClass { subsubname: "subsub" })
4656
/// }
4757
/// }
4858
/// let gil = Python::acquire_gil();
@@ -60,13 +70,42 @@ pub struct PyClassInitializer<T: PyClass> {
6070
}
6171

6272
impl<T: PyClass> PyClassInitializer<T> {
73+
/// Constract new initialzer from value `T` and base class' initializer.
74+
///
75+
/// We recommend to mainly use `add_subclass`, instead of directly call `new`.
6376
pub fn new(init: T, super_init: <T::BaseType as PyTypeInfo>::Initializer) -> Self {
64-
Self {
65-
init,
66-
super_init: super_init,
67-
}
77+
Self { init, super_init }
6878
}
6979

80+
/// Constructs a new initializer from base class' initializer.
81+
///
82+
/// # Example
83+
/// ```
84+
/// # use pyo3::prelude::*;
85+
/// #[pyclass]
86+
/// struct BaseClass {
87+
/// value: u32,
88+
/// }
89+
///
90+
/// impl BaseClass {
91+
/// fn new(value: i32) -> PyResult<Self> {
92+
/// Ok(Self {
93+
/// value: std::convert::TryFrom::try_from(value)?,
94+
/// })
95+
/// }
96+
/// }
97+
/// #[pyclass(extends=BaseClass)]
98+
/// struct SubClass {}
99+
///
100+
/// #[pymethods]
101+
/// impl SubClass {
102+
/// #[new]
103+
/// fn new(value: i32) -> PyResult<PyClassInitializer<Self>> {
104+
/// let base_init = PyClassInitializer::from(BaseClass::new(value)?);
105+
/// Ok(base_init.add_subclass(SubClass {}))
106+
/// }
107+
/// }
108+
/// ```
70109
pub fn add_subclass<S>(self, subclass_value: S) -> PyClassInitializer<S>
71110
where
72111
S: PyClass + PyTypeInfo<BaseType = T>,
@@ -97,6 +136,7 @@ impl<T: PyClass> PyObjectInit<T> for PyClassInitializer<T> {
97136
super_init.init_class(super_obj);
98137
}
99138
}
139+
private_impl! {}
100140
}
101141

102142
impl<T> From<T> for PyClassInitializer<T>

src/pyclass_slots.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
1-
//! This module contains additional fields pf pyclass
1+
//! This module contains additional fields for `#[pyclass]`..
2+
//! Mainly used by our proc-macro codes.
23
use crate::{ffi, Python};
34

45
const POINTER_SIZE: isize = std::mem::size_of::<*mut ffi::PyObject>() as _;
56

6-
/// Represents `__dict__`.
7+
/// Represents `__dict__` field for `#[pyclass]`.
78
pub trait PyClassDict {
89
const OFFSET: Option<isize> = None;
910
fn new() -> Self;
1011
unsafe fn clear_dict(&mut self, _py: Python) {}
1112
private_decl! {}
1213
}
1314

14-
/// Represents `__weakref__`.
15+
/// Represents `__weakref__` field for `#[pyclass]`.
1516
pub trait PyClassWeakRef {
1617
const OFFSET: Option<isize> = None;
1718
fn new() -> Self;
1819
unsafe fn clear_weakrefs(&mut self, _obj: *mut ffi::PyObject, _py: Python) {}
1920
private_decl! {}
2021
}
2122

22-
/// Dummy slot means the function doesn't has such a feature.
23+
/// Zero-sized dummy field.
2324
pub struct PyClassDummySlot;
2425

2526
impl PyClassDict for PyClassDummySlot {
@@ -36,7 +37,9 @@ impl PyClassWeakRef for PyClassDummySlot {
3637
}
3738
}
3839

39-
/// actual dict field
40+
/// Actual dict field, which holds the pointer to `__dict__`.
41+
///
42+
/// `#[pyclass(dict)]` automatically adds this.
4043
#[repr(transparent)]
4144
pub struct PyClassDictSlot(*mut ffi::PyObject);
4245

@@ -53,7 +56,9 @@ impl PyClassDict for PyClassDictSlot {
5356
}
5457
}
5558

56-
/// actual weakref field
59+
/// Actual weakref field, which holds the pointer to `__weakref__`.
60+
///
61+
/// `#[pyclass(weakref)]` automatically adds this.
5762
#[repr(transparent)]
5863
pub struct PyClassWeakRefSlot(*mut ffi::PyObject);
5964

src/type_object.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// Copyright (c) 2017-present PyO3 Project and Contributors
2-
32
//! Python type object information
43
54
use crate::instance::Py;
@@ -41,6 +40,7 @@ pub trait PyObjectLayout<T: PyTypeInfo> {
4140
pub trait PyObjectSizedLayout<T: PyTypeInfo>: PyObjectLayout<T> + Sized {}
4241

4342
/// Our custom type flags
43+
#[doc(hidden)]
4444
pub mod type_flags {
4545
/// type object supports python GC
4646
pub const GC: usize = 1;
@@ -59,6 +59,7 @@ pub mod type_flags {
5959
}
6060

6161
/// Python type information.
62+
/// All Python native types(e.g., `PyDict`) and `#[pyclass]` structs implement this trait.
6263
pub trait PyTypeInfo: Sized {
6364
/// Type of objects to store in PyObject struct
6465
type Type;

tests/test_dunder.rs

100644100755
File mode changed.

tests/test_inheritance.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,9 @@ struct BaseClassWithResult {
7373
impl BaseClassWithResult {
7474
#[new]
7575
fn new(value: isize) -> PyResult<Self> {
76-
if 0 <= value {
77-
Ok(Self { _val: value as _ })
78-
} else {
79-
Err(PyErr::new::<pyo3::exceptions::RuntimeError, _>(()))
80-
}
76+
Ok(Self {
77+
_val: std::convert::TryFrom::try_from(value)?,
78+
})
8179
}
8280
}
8381

@@ -105,7 +103,7 @@ fn handle_result_in_new() {
105103
try:
106104
subclass(-10)
107105
assert Fals
108-
except RuntimeError as e:
106+
except ValueError as e:
109107
pass
110108
except Exception as e:
111109
raise e

0 commit comments

Comments
 (0)