Skip to content

Commit 176cb2a

Browse files
committed
Make it enable to safely inherit native types
1 parent 8175d6f commit 176cb2a

36 files changed

+238
-104
lines changed

pyo3-derive-backend/src/pyclass.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct PyClassArgs {
1616
pub name: Option<syn::Expr>,
1717
pub flags: Vec<syn::Expr>,
1818
pub base: syn::TypePath,
19+
pub has_extends: bool,
1920
pub module: Option<syn::LitStr>,
2021
}
2122

@@ -39,8 +40,9 @@ impl Default for PyClassArgs {
3940
module: None,
4041
// We need the 0 as value for the constant we're later building using quote for when there
4142
// are no other flags
42-
flags: vec![parse_quote! {0}],
43-
base: parse_quote! {pyo3::types::PyAny},
43+
flags: vec![parse_quote! { 0 }],
44+
base: parse_quote! { pyo3::types::PyAny },
45+
has_extends: false,
4446
}
4547
}
4648
}
@@ -89,6 +91,7 @@ impl PyClassArgs {
8991
path: exp.path.clone(),
9092
qself: None,
9193
};
94+
self.has_extends = true;
9295
}
9396
_ => {
9497
return Err(syn::Error::new_spanned(
@@ -318,7 +321,7 @@ fn impl_class(
318321
}
319322
}
320323
}
321-
// TODO: implement dict and weakref
324+
322325
let weakref = if has_weakref {
323326
quote! { type WeakRef = pyo3::pyclass_slots::PyClassWeakRefSlot; }
324327
} else {
@@ -355,6 +358,11 @@ fn impl_class(
355358

356359
let base = &attr.base;
357360
let flags = &attr.flags;
361+
let extended = if attr.has_extends {
362+
quote! { pyo3::type_flags::EXTENDED }
363+
} else {
364+
quote! { 0 }
365+
};
358366

359367
Ok(quote! {
360368
impl pyo3::type_object::PyTypeInfo for #cls {
@@ -365,7 +373,7 @@ fn impl_class(
365373
const NAME: &'static str = #cls_name;
366374
const MODULE: Option<&'static str> = #module;
367375
const DESCRIPTION: &'static str = #doc;
368-
const FLAGS: usize = #(#flags)|*;
376+
const FLAGS: usize = #(#flags)|* | #extended;
369377

370378
#[inline]
371379
unsafe fn type_object() -> &'static mut pyo3::ffi::PyTypeObject {

src/conversion.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! Conversions between various states of rust and python types and their wrappers.
44
use crate::err::{self, PyDowncastError, PyResult};
55
use crate::object::PyObject;
6-
use crate::type_object::{PyConcreteObject, PyTypeInfo};
6+
use crate::type_object::{PyObjectLayout, PyTypeInfo};
77
use crate::types::PyAny;
88
use crate::types::PyTuple;
99
use crate::{ffi, gil, Py, Python};

src/ffi/code.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,6 @@ pub struct PyCodeObject {
3131
pub co_extra: *mut c_void,
3232
}
3333

34-
impl Default for PyCodeObject {
35-
#[inline]
36-
fn default() -> Self {
37-
unsafe { ::std::mem::zeroed() }
38-
}
39-
}
40-
4134
/* Masks for co_flags */
4235
pub const CO_OPTIMIZED: c_int = 0x0001;
4336
pub const CO_NEWLOCALS: c_int = 0x0002;

src/ffi/complexobject.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,17 @@ extern "C" {
2727
pub fn PyComplex_ImagAsDouble(op: *mut PyObject) -> c_double;
2828
}
2929

30-
#[cfg(not(Py_LIMITED_API))]
3130
#[repr(C)]
3231
#[derive(Copy, Clone)]
3332
pub struct Py_complex {
3433
pub real: c_double,
3534
pub imag: c_double,
3635
}
3736

38-
#[cfg(not(Py_LIMITED_API))]
3937
#[repr(C)]
4038
#[derive(Copy, Clone)]
4139
pub struct PyComplexObject {
42-
_ob_base: PyObject,
40+
pub ob_base: PyObject,
4341
pub cval: Py_complex,
4442
}
4543

src/ffi/datetime.rs

100644100755
File mode changed.

src/ffi/dictobject.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,22 @@ extern "C" {
1313
pub static mut PyDictValues_Type: PyTypeObject;
1414
}
1515

16+
#[repr(C)]
17+
pub struct PyDictKeysObject {
18+
_unused: [u8; 0],
19+
}
20+
21+
#[repr(C)]
22+
#[derive(Debug)]
23+
pub struct PyDictObject {
24+
pub ob_base: PyObject,
25+
pub ma_used: Py_ssize_t,
26+
#[cfg(Py_3_6)]
27+
pub ma_version_tag: u64,
28+
pub ma_keys: *mut PyDictKeysObject,
29+
pub ma_values: *mut *mut PyObject,
30+
}
31+
1632
#[inline]
1733
pub unsafe fn PyDict_Check(op: *mut PyObject) -> c_int {
1834
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS)

src/ffi/floatobject.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::ffi::object::*;
22
use std::os::raw::{c_double, c_int};
33

4-
#[cfg(not(Py_LIMITED_API))]
54
#[repr(C)]
65
pub struct PyFloatObject {
76
pub ob_base: PyObject,

src/ffi/listobject.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,7 @@ use std::os::raw::c_int;
55
#[repr(C)]
66
#[derive(Copy, Clone)]
77
pub struct PyListObject {
8-
#[cfg(py_sys_config = "Py_TRACE_REFS")]
9-
pub _ob_next: *mut PyObject,
10-
#[cfg(py_sys_config = "Py_TRACE_REFS")]
11-
pub _ob_prev: *mut PyObject,
12-
pub ob_refcnt: Py_ssize_t,
13-
pub ob_type: *mut PyTypeObject,
14-
pub ob_size: Py_ssize_t,
8+
pub ob_base: PyVarObject,
159
pub ob_item: *mut *mut PyObject,
1610
pub allocated: Py_ssize_t,
1711
}

src/ffi/longobject.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ use std::os::raw::{
66
};
77

88
/// This is an opaque type in the python c api
9-
#[repr(transparent)]
10-
pub struct PyLongObject(*mut c_void);
9+
#[repr(C)]
10+
pub struct PyLongObject {
11+
_unused: [u8; 0],
12+
}
1113

1214
#[cfg_attr(windows, link(name = "pythonXY"))]
1315
extern "C" {

src/ffi/setobject.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::ffi::object::*;
2-
use crate::ffi::pyport::Py_ssize_t;
2+
use crate::ffi::pyport::{Py_hash_t, Py_ssize_t};
33
use std::os::raw::c_int;
44

55
#[cfg_attr(windows, link(name = "pythonXY"))]
@@ -11,6 +11,28 @@ extern "C" {
1111
pub static mut PySetIter_Type: PyTypeObject;
1212
}
1313

14+
pub const PySet_MINSIZE: usize = 8;
15+
16+
#[repr(C)]
17+
#[derive(Debug)]
18+
pub struct setentry {
19+
pub key: *mut PyObject,
20+
pub hash: Py_hash_t,
21+
}
22+
23+
#[repr(C)]
24+
pub struct PySetObject {
25+
pub ob_base: PyObject,
26+
pub fill: Py_ssize_t,
27+
pub used: Py_ssize_t,
28+
pub mask: Py_ssize_t,
29+
pub table: *mut setentry,
30+
pub hash: Py_hash_t,
31+
pub finger: Py_ssize_t,
32+
pub smalltable: [setentry; PySet_MINSIZE],
33+
pub weakreflist: *mut PyObject,
34+
}
35+
1436
#[inline]
1537
#[cfg_attr(PyPy, link_name = "PyPyFrozenSet_CheckExact")]
1638
pub unsafe fn PyFrozenSet_CheckExact(ob: *mut PyObject) -> c_int {

src/ffi/sliceobject.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ pub unsafe fn PySlice_Check(op: *mut PyObject) -> c_int {
2626
(Py_TYPE(op) == &mut PySlice_Type) as c_int
2727
}
2828

29+
#[repr(C)]
30+
pub struct PySliceObject {
31+
pub ob_base: PyObject,
32+
pub start: *mut PyObject,
33+
pub stop: *mut PyObject,
34+
pub step: *mut PyObject,
35+
}
36+
2937
#[cfg_attr(windows, link(name = "pythonXY"))]
3038
extern "C" {
3139
#[cfg_attr(PyPy, link_name = "PyPySlice_New")]

src/ffi/tupleobject.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::ffi::pyport::Py_ssize_t;
33
use std::os::raw::c_int;
44

55
#[repr(C)]
6-
#[cfg(not(Py_LIMITED_API))]
76
pub struct PyTupleObject {
87
pub ob_base: PyVarObject,
98
pub ob_item: [*mut PyObject; 1],

src/freelist.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use crate::ffi;
66
use crate::pyclass::{tp_free_fallback, PyClassAlloc};
7-
use crate::type_object::{PyConcreteObject, PyTypeInfo};
7+
use crate::type_object::{PyObjectLayout, PyTypeInfo};
88
use crate::Python;
99
use std::mem;
1010
use std::os::raw::c_void;
@@ -77,7 +77,7 @@ where
7777
ffi::PyObject_Init(obj, <Self as PyTypeInfo>::type_object());
7878
obj as _
7979
} else {
80-
ffi::PyType_GenericAlloc(<Self as PyTypeInfo>::type_object(), 0) as _
80+
crate::pyclass::default_alloc::<Self>() as _
8181
}
8282
}
8383

src/instance.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::gil;
44
use crate::object::PyObject;
55
use crate::objectprotocol::ObjectProtocol;
66
use crate::pyclass::{IntoInitializer, PyClass, PyClassShell};
7-
use crate::type_object::{PyConcreteObject, PyTypeInfo};
7+
use crate::type_object::{PyObjectLayout, PyTypeInfo};
88
use crate::types::PyAny;
99
use crate::{ffi, IntoPy};
1010
use crate::{AsPyPointer, FromPyObject, IntoPyPointer, Python, ToPyObject};
@@ -37,7 +37,9 @@ impl<T> Py<T> {
3737
/// Create new instance of T and move it under python management
3838
pub fn new(py: Python, value: impl IntoInitializer<T>) -> PyResult<Py<T>>
3939
where
40-
T: PyClass + PyTypeInfo<ConcreteLayout = PyClassShell<T>>,
40+
T: PyClass,
41+
<T::BaseType as PyTypeInfo>::ConcreteLayout:
42+
crate::type_object::PyObjectSizedLayout<T::BaseType>,
4143
{
4244
let initializer = value.into_initializer()?;
4345
let obj = unsafe { initializer.create_shell(py)? };

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub use crate::object::PyObject;
129129
pub use crate::objectprotocol::ObjectProtocol;
130130
pub use crate::pyclass::{PyClass, PyClassInitializer, PyClassShell};
131131
pub use crate::python::{prepare_freethreaded_python, Python};
132-
pub use crate::type_object::{type_flags, PyConcreteObject, PyTypeInfo};
132+
pub use crate::type_object::{type_flags, PyTypeInfo};
133133

134134
// Re-exported for wrap_function
135135
#[doc(hidden)]

src/pyclass.rs

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,33 @@ use crate::class::methods::{PyMethodDefType, PyMethodsProtocol};
33
use crate::conversion::{AsPyPointer, FromPyPointer, ToPyObject};
44
use crate::exceptions::RuntimeError;
55
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
6-
use crate::type_object::{type_flags, PyConcreteObject, PyTypeObject};
6+
use crate::type_object::{type_flags, PyObjectLayout, PyObjectSizedLayout, PyTypeObject};
77
use crate::types::PyAny;
88
use crate::{class, ffi, gil, PyErr, PyObject, PyResult, PyTypeInfo, Python};
99
use std::ffi::CString;
1010
use std::mem::ManuallyDrop;
1111
use std::os::raw::c_void;
1212
use std::ptr::{self, NonNull};
1313

14+
#[inline]
15+
pub(crate) unsafe fn default_alloc<T: PyTypeInfo>() -> *mut ffi::PyObject {
16+
let tp_ptr = T::type_object();
17+
if T::FLAGS & type_flags::EXTENDED != 0
18+
&& <T::BaseType as PyTypeInfo>::ConcreteLayout::IS_NATIVE_TYPE
19+
{
20+
let base_tp_ptr = <T::BaseType as PyTypeInfo>::type_object();
21+
if let Some(base_new) = (*base_tp_ptr).tp_new {
22+
return base_new(tp_ptr, ptr::null_mut(), ptr::null_mut());
23+
}
24+
}
25+
let alloc = (*tp_ptr).tp_alloc.unwrap_or(ffi::PyType_GenericAlloc);
26+
alloc(tp_ptr, 0)
27+
}
28+
1429
/// A trait that enables custome alloc/dealloc implementations for pyclasses.
1530
pub trait PyClassAlloc: PyTypeInfo + Sized {
1631
unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout {
17-
let tp_ptr = Self::type_object();
18-
let alloc = (*tp_ptr).tp_alloc.unwrap_or(ffi::PyType_GenericAlloc);
19-
alloc(tp_ptr, 0) as _
32+
default_alloc::<Self>() as _
2033
}
2134

2235
unsafe fn dealloc(py: Python, self_: *mut Self::ConcreteLayout) {
@@ -49,7 +62,9 @@ pub unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) {
4962
}
5063
}
5164

52-
pub trait PyClass: PyTypeInfo + Sized + PyClassAlloc + PyMethodsProtocol {
65+
pub trait PyClass:
66+
PyTypeInfo<ConcreteLayout = PyClassShell<Self>> + Sized + PyClassAlloc + PyMethodsProtocol
67+
{
5368
type Dict: PyClassDict;
5469
type WeakRef: PyClassWeakRef;
5570
}
@@ -88,7 +103,8 @@ pub struct PyClassShell<T: PyClass> {
88103
impl<T: PyClass> PyClassShell<T> {
89104
pub fn new_ref(py: Python, value: impl IntoInitializer<T>) -> PyResult<&Self>
90105
where
91-
T: PyTypeInfo<ConcreteLayout = Self>,
106+
<T::BaseType as PyTypeInfo>::ConcreteLayout:
107+
crate::type_object::PyObjectSizedLayout<T::BaseType>,
92108
{
93109
unsafe {
94110
let initializer = value.into_initializer()?;
@@ -99,7 +115,8 @@ impl<T: PyClass> PyClassShell<T> {
99115

100116
pub fn new_mut(py: Python, value: impl IntoInitializer<T>) -> PyResult<&mut Self>
101117
where
102-
T: PyTypeInfo<ConcreteLayout = Self>,
118+
<T::BaseType as PyTypeInfo>::ConcreteLayout:
119+
crate::type_object::PyObjectSizedLayout<T::BaseType>,
103120
{
104121
unsafe {
105122
let initializer = value.into_initializer()?;
@@ -109,7 +126,11 @@ impl<T: PyClass> PyClassShell<T> {
109126
}
110127

111128
#[doc(hidden)]
112-
unsafe fn new(py: Python) -> PyResult<*mut Self> {
129+
unsafe fn new(py: Python) -> PyResult<*mut Self>
130+
where
131+
<T::BaseType as PyTypeInfo>::ConcreteLayout:
132+
crate::type_object::PyObjectSizedLayout<T::BaseType>,
133+
{
113134
<T::BaseType as PyTypeObject>::init_type();
114135
T::init_type();
115136
let base = T::alloc(py);
@@ -123,8 +144,12 @@ impl<T: PyClass> PyClassShell<T> {
123144
}
124145
}
125146

126-
impl<T: PyClass> PyConcreteObject<T> for PyClassShell<T> {
147+
impl<T: PyClass> PyObjectLayout<T> for PyClassShell<T> {
127148
const NEED_INIT: bool = std::mem::size_of::<T>() != 0;
149+
const IS_NATIVE_TYPE: bool = false;
150+
fn get_super(&mut self) -> Option<&mut <T::BaseType as PyTypeInfo>::ConcreteLayout> {
151+
Some(&mut self.ob_base)
152+
}
128153
unsafe fn internal_ref_cast(obj: &PyAny) -> &T {
129154
let shell = obj.as_ptr() as *const PyClassShell<T>;
130155
&(*shell).pyclass
@@ -142,11 +167,10 @@ impl<T: PyClass> PyConcreteObject<T> for PyClassShell<T> {
142167
unsafe fn py_init(&mut self, value: T) {
143168
self.pyclass = ManuallyDrop::new(value);
144169
}
145-
fn get_super(&mut self) -> Option<&mut <T::BaseType as PyTypeInfo>::ConcreteLayout> {
146-
Some(&mut self.ob_base)
147-
}
148170
}
149171

172+
impl<T: PyClass> PyObjectSizedLayout<T> for PyClassShell<T> {}
173+
150174
impl<T: PyClass> AsPyPointer for PyClassShell<T> {
151175
fn as_ptr(&self) -> *mut ffi::PyObject {
152176
(self as *const _) as *mut _
@@ -208,8 +232,6 @@ where
208232
}
209233

210234
/// An initializer for `PyClassShell<T>`.
211-
///
212-
/// **NOTE** If
213235
pub struct PyClassInitializer<T: PyTypeInfo> {
214236
init: Option<T>,
215237
super_init: Option<*mut PyClassInitializer<T::BaseType>>,
@@ -273,7 +295,9 @@ impl<T: PyTypeInfo> PyClassInitializer<T> {
273295

274296
pub unsafe fn create_shell(self, py: Python) -> PyResult<*mut PyClassShell<T>>
275297
where
276-
T: PyClass + PyTypeInfo<ConcreteLayout = PyClassShell<T>>,
298+
T: PyClass,
299+
<T::BaseType as PyTypeInfo>::ConcreteLayout:
300+
crate::type_object::PyObjectSizedLayout<T::BaseType>,
277301
{
278302
let shell = PyClassShell::new(py)?;
279303
self.init_class(&mut *shell)?;

0 commit comments

Comments
 (0)