Refine tests and documents around pyclass.rs

This commit is contained in:
kngwyu 2020-01-08 22:44:50 +09:00
parent 451de182cb
commit c57177a169
7 changed files with 80 additions and 28 deletions

0
src/lib.rs Normal file → Executable file
View File

View File

@ -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

View File

@ -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>

View File

@ -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);

View File

@ -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;

0
tests/test_dunder.rs Normal file → Executable file
View File

View File

@ -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