Proof of concept of using PEP384s PyType_Spec
This commit is contained in:
parent
4a05f2782c
commit
400462063d
|
@ -1,6 +1,7 @@
|
|||
import datetime as pdt
|
||||
import platform
|
||||
import struct
|
||||
import re
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
@ -327,4 +328,5 @@ def test_tz_class_introspection():
|
|||
tzi = rdt.TzClass()
|
||||
|
||||
assert tzi.__class__ == rdt.TzClass
|
||||
assert repr(tzi).startswith("<TzClass object at")
|
||||
# PyPy generate <importlib.bootstrap.TzClass ...> for some reason.
|
||||
assert re.match(r"^<[\w\.]*TzClass object at", repr(tzi))
|
||||
|
|
|
@ -205,7 +205,7 @@ or by `self_.into_super()` as `PyRef<Self::BaseClass>`.
|
|||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
|
||||
#[pyclass]
|
||||
#[pyclass(subclass)]
|
||||
struct BaseClass {
|
||||
val1: usize,
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ impl BaseClass {
|
|||
}
|
||||
}
|
||||
|
||||
#[pyclass(extends=BaseClass)]
|
||||
#[pyclass(extends=BaseClass, subclass)]
|
||||
struct SubClass {
|
||||
val2: usize,
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
//! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html)
|
||||
|
||||
use crate::callback::{HashCallbackOutput, IntoPyCallbackOutput};
|
||||
use crate::pyclass::maybe_push_slot;
|
||||
use crate::{exceptions, ffi, FromPyObject, PyAny, PyCell, PyClass, PyErr, PyObject, PyResult};
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
/// Operators for the __richcmp__ method
|
||||
#[derive(Debug)]
|
||||
|
@ -147,13 +148,38 @@ pub struct PyObjectMethods {
|
|||
|
||||
#[doc(hidden)]
|
||||
impl PyObjectMethods {
|
||||
pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) {
|
||||
type_object.tp_str = self.tp_str;
|
||||
type_object.tp_repr = self.tp_repr;
|
||||
type_object.tp_hash = self.tp_hash;
|
||||
type_object.tp_getattro = self.tp_getattro;
|
||||
type_object.tp_richcompare = self.tp_richcompare;
|
||||
type_object.tp_setattro = self.tp_setattro;
|
||||
pub(crate) fn update_slots(&self, slots: &mut Vec<ffi::PyType_Slot>) {
|
||||
maybe_push_slot(slots, ffi::Py_tp_str, self.tp_str.map(|v| v as *mut c_void));
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_repr,
|
||||
self.tp_repr.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_hash,
|
||||
self.tp_hash.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_getattro,
|
||||
self.tp_getattro.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_richcompare,
|
||||
self.tp_richcompare.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_setattro,
|
||||
self.tp_setattro.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_bool,
|
||||
self.nb_bool.map(|v| v as *mut c_void),
|
||||
);
|
||||
}
|
||||
// Set functions used by `#[pyproto]`.
|
||||
pub fn set_str<T>(&mut self)
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
//! https://docs.python.org/3/reference/datamodel.html#implementing-descriptors)
|
||||
|
||||
use crate::callback::IntoPyCallbackOutput;
|
||||
use crate::pyclass::maybe_push_slot;
|
||||
use crate::types::PyAny;
|
||||
use crate::{ffi, FromPyObject, PyClass, PyObject};
|
||||
use std::os::raw::c_int;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
/// Descriptor interface
|
||||
#[allow(unused_variables)]
|
||||
|
@ -79,9 +80,17 @@ pub struct PyDescrMethods {
|
|||
|
||||
#[doc(hidden)]
|
||||
impl PyDescrMethods {
|
||||
pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) {
|
||||
type_object.tp_descr_get = self.tp_descr_get;
|
||||
type_object.tp_descr_set = self.tp_descr_set;
|
||||
pub(crate) fn update_slots(&self, slots: &mut Vec<ffi::PyType_Slot>) {
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_descr_get,
|
||||
self.tp_descr_get.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_descr_set,
|
||||
self.tp_descr_set.map(|v| v as *mut c_void),
|
||||
);
|
||||
}
|
||||
pub fn set_descr_get<T>(&mut self)
|
||||
where
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//! Python GC support
|
||||
//!
|
||||
|
||||
use crate::pyclass::maybe_push_slot;
|
||||
use crate::{ffi, AsPyPointer, PyCell, PyClass, Python};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
|
@ -27,9 +28,17 @@ pub struct PyGCMethods {
|
|||
|
||||
#[doc(hidden)]
|
||||
impl PyGCMethods {
|
||||
pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) {
|
||||
type_object.tp_traverse = self.tp_traverse;
|
||||
type_object.tp_clear = self.tp_clear;
|
||||
pub(crate) fn update_slots(&self, slots: &mut Vec<ffi::PyType_Slot>) {
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_traverse,
|
||||
self.tp_traverse.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_clear,
|
||||
self.tp_clear.map(|v| v as *mut c_void),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_traverse<T>(&mut self)
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
use crate::callback::IntoPyCallbackOutput;
|
||||
use crate::derive_utils::TryFromPyCell;
|
||||
use crate::err::PyResult;
|
||||
use crate::pyclass::maybe_push_slot;
|
||||
use crate::{ffi, IntoPy, IntoPyPointer, PyClass, PyObject, Python};
|
||||
use std::os::raw::c_void;
|
||||
|
||||
/// Python Iterator Interface.
|
||||
///
|
||||
|
@ -79,9 +81,17 @@ pub struct PyIterMethods {
|
|||
|
||||
#[doc(hidden)]
|
||||
impl PyIterMethods {
|
||||
pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) {
|
||||
type_object.tp_iter = self.tp_iter;
|
||||
type_object.tp_iternext = self.tp_iternext;
|
||||
pub(crate) fn update_slots(&self, slots: &mut Vec<ffi::PyType_Slot>) {
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_iter,
|
||||
self.tp_iter.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_tp_iternext,
|
||||
self.tp_iternext.map(|v| v as *mut c_void),
|
||||
);
|
||||
}
|
||||
pub fn set_iter<T>(&mut self)
|
||||
where
|
||||
|
|
|
@ -581,11 +581,6 @@ pub trait PyNumberIndexProtocol<'p>: PyNumberProtocol<'p> {
|
|||
|
||||
#[doc(hidden)]
|
||||
impl ffi::PyNumberMethods {
|
||||
pub(crate) fn from_nb_bool(nb_bool: ffi::inquiry) -> *mut Self {
|
||||
let mut nm = ffi::PyNumberMethods_INIT;
|
||||
nm.nb_bool = Some(nb_bool);
|
||||
Box::into_raw(Box::new(nm))
|
||||
}
|
||||
pub fn set_add_radd<T>(&mut self)
|
||||
where
|
||||
T: for<'p> PyNumberAddProtocol<'p> + for<'p> PyNumberRAddProtocol<'p>,
|
||||
|
|
|
@ -455,7 +455,7 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
|
|||
/// - You want to get super class.
|
||||
/// ```
|
||||
/// # use pyo3::prelude::*;
|
||||
/// #[pyclass]
|
||||
/// #[pyclass(subclass)]
|
||||
/// struct Parent {
|
||||
/// basename: &'static str,
|
||||
/// }
|
||||
|
@ -516,11 +516,11 @@ where
|
|||
/// # Examples
|
||||
/// ```
|
||||
/// # use pyo3::prelude::*;
|
||||
/// #[pyclass]
|
||||
/// #[pyclass(subclass)]
|
||||
/// struct Base1 {
|
||||
/// name1: &'static str,
|
||||
/// }
|
||||
/// #[pyclass(extends=Base1)]
|
||||
/// #[pyclass(extends=Base1, subclass)]
|
||||
/// struct Base2 {
|
||||
/// name2: &'static str,
|
||||
/// }
|
||||
|
|
499
src/pyclass.rs
499
src/pyclass.rs
|
@ -7,9 +7,10 @@ use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
|
|||
use crate::type_object::{type_flags, PyLayout};
|
||||
use crate::types::PyAny;
|
||||
use crate::{class, ffi, PyCell, PyErr, PyNativeType, PyResult, PyTypeInfo, Python};
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::CString;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
use std::os::raw::{c_int, c_uint, c_void};
|
||||
use std::{ptr, thread};
|
||||
|
||||
#[inline]
|
||||
|
@ -107,98 +108,315 @@ pub trait PyClass:
|
|||
type BaseNativeType: PyTypeInfo + PyNativeType;
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
pub(crate) fn initialize_type_object<T>(
|
||||
pub(crate) fn maybe_push_slot(
|
||||
slots: &mut Vec<ffi::PyType_Slot>,
|
||||
slot: c_int,
|
||||
val: Option<*mut c_void>,
|
||||
) {
|
||||
if let Some(v) = val {
|
||||
slots.push(ffi::PyType_Slot { slot, pfunc: v });
|
||||
}
|
||||
}
|
||||
|
||||
fn push_numbers_slots(slots: &mut Vec<ffi::PyType_Slot>, numbers: &ffi::PyNumberMethods) {
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_add,
|
||||
numbers.nb_add.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_subtract,
|
||||
numbers.nb_subtract.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_multiply,
|
||||
numbers.nb_multiply.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_remainder,
|
||||
numbers.nb_remainder.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_divmod,
|
||||
numbers.nb_divmod.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_power,
|
||||
numbers.nb_power.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_negative,
|
||||
numbers.nb_negative.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_positive,
|
||||
numbers.nb_positive.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_absolute,
|
||||
numbers.nb_absolute.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_bool,
|
||||
numbers.nb_bool.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_invert,
|
||||
numbers.nb_invert.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_lshift,
|
||||
numbers.nb_lshift.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_rshift,
|
||||
numbers.nb_rshift.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_and,
|
||||
numbers.nb_and.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_xor,
|
||||
numbers.nb_xor.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_or,
|
||||
numbers.nb_or.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_int,
|
||||
numbers.nb_int.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_float,
|
||||
numbers.nb_float.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_add,
|
||||
numbers.nb_inplace_add.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_subtract,
|
||||
numbers.nb_inplace_subtract.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_multiply,
|
||||
numbers.nb_inplace_multiply.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_remainder,
|
||||
numbers.nb_inplace_remainder.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_power,
|
||||
numbers.nb_inplace_power.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_lshift,
|
||||
numbers.nb_inplace_lshift.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_rshift,
|
||||
numbers.nb_inplace_rshift.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_and,
|
||||
numbers.nb_inplace_and.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_xor,
|
||||
numbers.nb_inplace_xor.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_or,
|
||||
numbers.nb_inplace_or.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_floor_divide,
|
||||
numbers.nb_floor_divide.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_true_divide,
|
||||
numbers.nb_true_divide.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_floor_divide,
|
||||
numbers.nb_inplace_floor_divide.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_true_divide,
|
||||
numbers.nb_inplace_true_divide.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_index,
|
||||
numbers.nb_index.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_matrix_multiply,
|
||||
numbers.nb_matrix_multiply.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_nb_inplace_matrix_multiply,
|
||||
numbers.nb_inplace_matrix_multiply.map(|v| v as *mut c_void),
|
||||
);
|
||||
}
|
||||
|
||||
fn push_mapping_slots(slots: &mut Vec<ffi::PyType_Slot>, mapping: &ffi::PyMappingMethods) {
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_mp_length,
|
||||
mapping.mp_length.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_mp_subscript,
|
||||
mapping.mp_subscript.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_mp_ass_subscript,
|
||||
mapping.mp_ass_subscript.map(|v| v as *mut c_void),
|
||||
);
|
||||
}
|
||||
|
||||
fn push_sequence_slots(slots: &mut Vec<ffi::PyType_Slot>, seq: &ffi::PySequenceMethods) {
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_sq_length,
|
||||
seq.sq_length.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_sq_concat,
|
||||
seq.sq_concat.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_sq_repeat,
|
||||
seq.sq_repeat.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_sq_item,
|
||||
seq.sq_item.map(|v| v as *mut c_void),
|
||||
);
|
||||
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_sq_ass_item,
|
||||
seq.sq_ass_item.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_sq_contains,
|
||||
seq.sq_contains.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_sq_inplace_concat,
|
||||
seq.sq_inplace_concat.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_sq_inplace_repeat,
|
||||
seq.sq_inplace_repeat.map(|v| v as *mut c_void),
|
||||
);
|
||||
}
|
||||
|
||||
fn push_async_slots(slots: &mut Vec<ffi::PyType_Slot>, asnc: &ffi::PyAsyncMethods) {
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_am_await,
|
||||
asnc.am_await.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_am_aiter,
|
||||
asnc.am_aiter.map(|v| v as *mut c_void),
|
||||
);
|
||||
maybe_push_slot(
|
||||
slots,
|
||||
ffi::Py_am_anext,
|
||||
asnc.am_anext.map(|v| v as *mut c_void),
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn create_type_object<T>(
|
||||
py: Python,
|
||||
module_name: Option<&str>,
|
||||
type_object: &mut ffi::PyTypeObject,
|
||||
) -> PyResult<()>
|
||||
) -> PyResult<*mut ffi::PyTypeObject>
|
||||
where
|
||||
T: PyClass,
|
||||
{
|
||||
type_object.tp_doc = match T::DESCRIPTION {
|
||||
// PyPy will segfault if passed only a nul terminator as `tp_doc`, ptr::null() is OK though.
|
||||
"\0" => ptr::null(),
|
||||
s if s.as_bytes().ends_with(b"\0") => s.as_ptr() as _,
|
||||
let mut slots = vec![];
|
||||
|
||||
slots.push(ffi::PyType_Slot {
|
||||
slot: ffi::Py_tp_base,
|
||||
pfunc: T::BaseType::type_object_raw(py) as *mut c_void,
|
||||
});
|
||||
|
||||
let doc = match T::DESCRIPTION {
|
||||
"\0" => None,
|
||||
s if s.as_bytes().ends_with(b"\0") => Some(s.as_ptr() as _),
|
||||
// If the description is not null-terminated, create CString and leak it
|
||||
s => CString::new(s)?.into_raw(),
|
||||
s => Some(CString::new(s)?.into_raw() as _),
|
||||
};
|
||||
maybe_push_slot(&mut slots, ffi::Py_tp_doc, doc);
|
||||
|
||||
type_object.tp_base = T::BaseType::type_object_raw(py);
|
||||
|
||||
type_object.tp_name = match module_name {
|
||||
Some(module_name) => CString::new(format!("{}.{}", module_name, T::NAME))?.into_raw(),
|
||||
None => CString::new(T::NAME)?.into_raw(),
|
||||
};
|
||||
|
||||
// dealloc
|
||||
type_object.tp_dealloc = tp_dealloc::<T>();
|
||||
|
||||
// type size
|
||||
type_object.tp_basicsize = std::mem::size_of::<T::Layout>() as ffi::Py_ssize_t;
|
||||
|
||||
// __dict__ support
|
||||
if let Some(dict_offset) = PyCell::<T>::dict_offset() {
|
||||
type_object.tp_dictoffset = dict_offset as ffi::Py_ssize_t;
|
||||
}
|
||||
|
||||
// weakref support
|
||||
if let Some(weakref_offset) = PyCell::<T>::weakref_offset() {
|
||||
type_object.tp_weaklistoffset = weakref_offset as ffi::Py_ssize_t;
|
||||
}
|
||||
|
||||
// GC support
|
||||
if let Some(gc) = T::gc_methods() {
|
||||
unsafe { gc.as_ref() }.update_typeobj(type_object);
|
||||
}
|
||||
|
||||
// descriptor protocol
|
||||
if let Some(descr) = T::descr_methods() {
|
||||
unsafe { descr.as_ref() }.update_typeobj(type_object);
|
||||
}
|
||||
|
||||
// iterator methods
|
||||
if let Some(iter) = T::iter_methods() {
|
||||
unsafe { iter.as_ref() }.update_typeobj(type_object);
|
||||
}
|
||||
|
||||
// nb_bool is a part of PyObjectProtocol, but should be placed under tp_as_number
|
||||
let mut nb_bool = None;
|
||||
// basic methods
|
||||
if let Some(basic) = T::basic_methods() {
|
||||
unsafe { basic.as_ref() }.update_typeobj(type_object);
|
||||
nb_bool = unsafe { basic.as_ref() }.nb_bool;
|
||||
}
|
||||
|
||||
// number methods
|
||||
type_object.tp_as_number = T::number_methods()
|
||||
.map(|mut p| {
|
||||
unsafe { p.as_mut() }.nb_bool = nb_bool;
|
||||
p.as_ptr()
|
||||
})
|
||||
.unwrap_or_else(|| nb_bool.map_or_else(ptr::null_mut, ffi::PyNumberMethods::from_nb_bool));
|
||||
// mapping methods
|
||||
type_object.tp_as_mapping = T::mapping_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
|
||||
// sequence methods
|
||||
type_object.tp_as_sequence = T::sequence_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
|
||||
// async methods
|
||||
type_object.tp_as_async = T::async_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
|
||||
// buffer protocol
|
||||
type_object.tp_as_buffer = T::buffer_methods().map_or_else(ptr::null_mut, |p| p.as_ptr());
|
||||
maybe_push_slot(
|
||||
&mut slots,
|
||||
ffi::Py_tp_dealloc,
|
||||
tp_dealloc::<T>().map(|v| v as *mut c_void),
|
||||
);
|
||||
|
||||
let (new, call, mut methods) = py_class_method_defs::<T>();
|
||||
|
||||
maybe_push_slot(&mut slots, ffi::Py_tp_new, new.map(|v| v as *mut c_void));
|
||||
maybe_push_slot(&mut slots, ffi::Py_tp_call, call.map(|v| v as *mut c_void));
|
||||
// normal methods
|
||||
if !methods.is_empty() {
|
||||
methods.push(ffi::PyMethodDef_INIT);
|
||||
type_object.tp_methods = Box::into_raw(methods.into_boxed_slice()) as _;
|
||||
maybe_push_slot(
|
||||
&mut slots,
|
||||
ffi::Py_tp_methods,
|
||||
Some(Box::into_raw(methods.into_boxed_slice()) as *mut c_void),
|
||||
);
|
||||
}
|
||||
|
||||
// __new__ method
|
||||
type_object.tp_new = new;
|
||||
// __call__ method
|
||||
type_object.tp_call = call;
|
||||
|
||||
// properties
|
||||
let mut props = py_class_properties::<T>();
|
||||
|
||||
|
@ -207,34 +425,106 @@ where
|
|||
}
|
||||
if !props.is_empty() {
|
||||
props.push(ffi::PyGetSetDef_INIT);
|
||||
type_object.tp_getset = Box::into_raw(props.into_boxed_slice()) as _;
|
||||
maybe_push_slot(
|
||||
&mut slots,
|
||||
ffi::Py_tp_getset,
|
||||
Some(Box::into_raw(props.into_boxed_slice()) as *mut c_void),
|
||||
);
|
||||
}
|
||||
|
||||
// set type flags
|
||||
py_class_flags::<T>(type_object);
|
||||
if let Some(basic) = T::basic_methods() {
|
||||
unsafe { basic.as_ref() }.update_slots(&mut slots);
|
||||
}
|
||||
|
||||
// register type object
|
||||
unsafe {
|
||||
if ffi::PyType_Ready(type_object) == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
PyErr::fetch(py).into()
|
||||
if let Some(number) = T::number_methods() {
|
||||
push_numbers_slots(&mut slots, unsafe { number.as_ref() });
|
||||
}
|
||||
|
||||
// iterator methods
|
||||
if let Some(iter) = T::iter_methods() {
|
||||
unsafe { iter.as_ref() }.update_slots(&mut slots);
|
||||
}
|
||||
|
||||
// mapping methods
|
||||
if let Some(mapping) = T::mapping_methods() {
|
||||
push_mapping_slots(&mut slots, unsafe { mapping.as_ref() });
|
||||
}
|
||||
|
||||
// sequence methods
|
||||
if let Some(seq) = T::sequence_methods() {
|
||||
push_sequence_slots(&mut slots, unsafe { seq.as_ref() });
|
||||
}
|
||||
|
||||
// descriptor protocol
|
||||
if let Some(descr) = T::descr_methods() {
|
||||
unsafe { descr.as_ref() }.update_slots(&mut slots);
|
||||
}
|
||||
|
||||
// async methods
|
||||
if let Some(asnc) = T::async_methods() {
|
||||
push_async_slots(&mut slots, unsafe { asnc.as_ref() });
|
||||
}
|
||||
|
||||
// GC support
|
||||
if let Some(gc) = T::gc_methods() {
|
||||
unsafe { gc.as_ref() }.update_slots(&mut slots);
|
||||
}
|
||||
|
||||
slots.push(ffi::PyType_Slot {
|
||||
slot: 0,
|
||||
pfunc: ptr::null_mut(),
|
||||
});
|
||||
let mut spec = ffi::PyType_Spec {
|
||||
name: match module_name {
|
||||
Some(module_name) => CString::new(format!("{}.{}", module_name, T::NAME))?.into_raw(),
|
||||
None => CString::new(T::NAME)?.into_raw(),
|
||||
},
|
||||
basicsize: std::mem::size_of::<T::Layout>() as c_int,
|
||||
itemsize: 0,
|
||||
flags: py_class_flags::<T>(),
|
||||
slots: slots.as_mut_slice().as_mut_ptr(),
|
||||
};
|
||||
|
||||
let type_object = unsafe { ffi::PyType_FromSpec(&mut spec) };
|
||||
if type_object.is_null() {
|
||||
PyErr::fetch(py).into()
|
||||
} else {
|
||||
// Just patch the type objects for the things there's no
|
||||
// PyType_FromSpec API for... there's no reason this should work,
|
||||
// except for that it does and we have tests.
|
||||
let mut type_object = type_object as *mut ffi::PyTypeObject;
|
||||
if let Some(buffer) = T::buffer_methods() {
|
||||
unsafe {
|
||||
(*(*type_object).tp_as_buffer).bf_getbuffer = buffer.as_ref().bf_getbuffer;
|
||||
(*(*type_object).tp_as_buffer).bf_releasebuffer = buffer.as_ref().bf_releasebuffer;
|
||||
}
|
||||
}
|
||||
// __dict__ support
|
||||
if let Some(dict_offset) = PyCell::<T>::dict_offset() {
|
||||
unsafe {
|
||||
(*type_object).tp_dictoffset = dict_offset as ffi::Py_ssize_t;
|
||||
}
|
||||
}
|
||||
// weakref support
|
||||
if let Some(weakref_offset) = PyCell::<T>::weakref_offset() {
|
||||
unsafe {
|
||||
(*type_object).tp_weaklistoffset = weakref_offset as ffi::Py_ssize_t;
|
||||
}
|
||||
}
|
||||
Ok(type_object)
|
||||
}
|
||||
}
|
||||
|
||||
fn py_class_flags<T: PyTypeInfo>(type_object: &mut ffi::PyTypeObject) {
|
||||
if type_object.tp_traverse != None
|
||||
|| type_object.tp_clear != None
|
||||
|| T::FLAGS & type_flags::GC != 0
|
||||
{
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC;
|
||||
fn py_class_flags<T: PyClass + PyTypeInfo>() -> c_uint {
|
||||
let mut flags = if T::gc_methods().is_some() || T::FLAGS & type_flags::GC != 0 {
|
||||
ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC
|
||||
} else {
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT;
|
||||
}
|
||||
ffi::Py_TPFLAGS_DEFAULT
|
||||
};
|
||||
if T::FLAGS & type_flags::BASETYPE != 0 {
|
||||
type_object.tp_flags |= ffi::Py_TPFLAGS_BASETYPE;
|
||||
flags |= ffi::Py_TPFLAGS_BASETYPE;
|
||||
}
|
||||
flags.try_into().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn py_class_attributes<T: PyMethods>() -> impl Iterator<Item = PyClassAttributeDef> {
|
||||
|
@ -244,6 +534,21 @@ pub(crate) fn py_class_attributes<T: PyMethods>() -> impl Iterator<Item = PyClas
|
|||
})
|
||||
}
|
||||
|
||||
fn fallback_new() -> Option<ffi::newfunc> {
|
||||
unsafe extern "C" fn fallback_new(
|
||||
_subtype: *mut ffi::PyTypeObject,
|
||||
_args: *mut ffi::PyObject,
|
||||
_kwds: *mut ffi::PyObject,
|
||||
) -> *mut ffi::PyObject {
|
||||
crate::callback_body!(py, {
|
||||
Err::<(), _>(crate::exceptions::PyTypeError::py_err(
|
||||
"No constructor defined",
|
||||
))
|
||||
})
|
||||
}
|
||||
Some(fallback_new)
|
||||
}
|
||||
|
||||
fn py_class_method_defs<T: PyMethods>() -> (
|
||||
Option<ffi::newfunc>,
|
||||
Option<ffi::PyCFunctionWithKeywords>,
|
||||
|
@ -251,7 +556,7 @@ fn py_class_method_defs<T: PyMethods>() -> (
|
|||
) {
|
||||
let mut defs = Vec::new();
|
||||
let mut call = None;
|
||||
let mut new = None;
|
||||
let mut new = fallback_new();
|
||||
|
||||
for def in T::py_methods() {
|
||||
match *def {
|
||||
|
|
|
@ -30,12 +30,12 @@ impl<T: PyTypeInfo> PyObjectInit<T> for PyNativeTypeInitializer<T> {
|
|||
/// ```
|
||||
/// # use pyo3::prelude::*;
|
||||
/// # use pyo3::py_run;
|
||||
/// #[pyclass]
|
||||
/// #[pyclass(subclass)]
|
||||
/// struct BaseClass {
|
||||
/// #[pyo3(get)]
|
||||
/// basename: &'static str,
|
||||
/// }
|
||||
/// #[pyclass(extends=BaseClass)]
|
||||
/// #[pyclass(extends=BaseClass, subclass)]
|
||||
/// struct SubClass {
|
||||
/// #[pyo3(get)]
|
||||
/// subname: &'static str,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use crate::conversion::IntoPyPointer;
|
||||
use crate::once_cell::GILOnceCell;
|
||||
use crate::pyclass::{initialize_type_object, py_class_attributes, PyClass};
|
||||
use crate::pyclass::{create_type_object, py_class_attributes, PyClass};
|
||||
use crate::pyclass_init::PyObjectInit;
|
||||
use crate::types::{PyAny, PyType};
|
||||
use crate::{ffi, AsPyPointer, PyErr, PyNativeType, PyObject, PyResult, Python};
|
||||
|
@ -157,12 +157,10 @@ impl LazyStaticType {
|
|||
|
||||
pub fn get_or_init<T: PyClass>(&self, py: Python) -> *mut ffi::PyTypeObject {
|
||||
let type_object = *self.value.get_or_init(py, || {
|
||||
let mut type_object = Box::new(ffi::PyTypeObject_INIT);
|
||||
initialize_type_object::<T>(py, T::MODULE, type_object.as_mut()).unwrap_or_else(|e| {
|
||||
create_type_object::<T>(py, T::MODULE).unwrap_or_else(|e| {
|
||||
e.print(py);
|
||||
panic!("An error occurred while initializing class {}", T::NAME)
|
||||
});
|
||||
Box::into_raw(type_object)
|
||||
})
|
||||
});
|
||||
|
||||
// We might want to fill the `tp_dict` with python instances of `T`
|
||||
|
|
|
@ -51,7 +51,10 @@ fn class_attributes() {
|
|||
py_assert!(py, foo_obj, "foo_obj.MY_CONST == 'foobar'");
|
||||
}
|
||||
|
||||
// Ignored because heap types are not immutable:
|
||||
// https://github.com/python/cpython/blob/master/Objects/typeobject.c#L3399-L3409
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn class_attributes_are_immutable() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
|
|
@ -119,7 +119,11 @@ fn test_raw_idents() {
|
|||
#[pyclass]
|
||||
struct EmptyClassInModule {}
|
||||
|
||||
// Ignored because heap types do not show up as being in builtins, instead they
|
||||
// raise AttributeError:
|
||||
// https://github.com/python/cpython/blob/master/Objects/typeobject.c#L544-L573
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn empty_class_in_module() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
@ -165,7 +169,7 @@ fn class_with_object_field() {
|
|||
py_assert!(py, ty, "ty(None).value == None");
|
||||
}
|
||||
|
||||
#[pyclass(unsendable)]
|
||||
#[pyclass(unsendable, subclass)]
|
||||
struct UnsendableBase {
|
||||
value: std::rc::Rc<usize>,
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ fn test_cloneable_pyclass() {
|
|||
assert_eq!(&c, &*mrc);
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
#[pyclass(subclass)]
|
||||
#[derive(Default)]
|
||||
struct BaseClass {
|
||||
value: i32,
|
||||
|
|
|
@ -453,7 +453,7 @@ fn test_cls_impl() {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
#[pyclass(dict)]
|
||||
#[pyclass(dict, subclass)]
|
||||
struct DunderDictSupport {}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -146,7 +146,7 @@ fn gc_integration2() {
|
|||
py_run!(py, inst, "import gc; assert inst in gc.get_objects()");
|
||||
}
|
||||
|
||||
#[pyclass(weakref)]
|
||||
#[pyclass(weakref, subclass)]
|
||||
struct WeakRefSupport {}
|
||||
|
||||
#[test]
|
||||
|
@ -179,7 +179,7 @@ fn inherited_weakref() {
|
|||
);
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
#[pyclass(subclass)]
|
||||
struct BaseClassWithDrop {
|
||||
data: Option<Arc<AtomicBool>>,
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use pyo3::types::IntoPyDict;
|
|||
use pyo3::types::{PyDict, PySet};
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
#[pyclass(subclass)]
|
||||
struct BaseClass {
|
||||
#[pyo3(get)]
|
||||
val1: usize,
|
||||
|
@ -106,7 +106,7 @@ fn mutation_fails() {
|
|||
)
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
#[pyclass(subclass)]
|
||||
struct BaseClassWithResult {
|
||||
_val: usize,
|
||||
}
|
||||
|
|
|
@ -31,7 +31,10 @@ fn class_with_docs() {
|
|||
py_assert!(py, typeobj, "typeobj.__text_signature__ is None");
|
||||
}
|
||||
|
||||
// Ignored because heap types don't have working __text_signature__:
|
||||
// https://github.com/python/cpython/blob/master/Objects/typeobject.c#L864-L870
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn class_with_docs_and_signature() {
|
||||
/// docs line1
|
||||
#[pyclass]
|
||||
|
@ -66,7 +69,10 @@ fn class_with_docs_and_signature() {
|
|||
);
|
||||
}
|
||||
|
||||
// Ignored because heap types don't have working __text_signature__:
|
||||
// https://github.com/python/cpython/blob/master/Objects/typeobject.c#L864-L870
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn class_with_signature() {
|
||||
#[pyclass]
|
||||
#[text_signature = "(a, b=None, *, c=42)"]
|
||||
|
|
Loading…
Reference in New Issue