Skip to content

Commit bdb66af

Browse files
committed
Make PyClassShell have dict&weakref
1 parent 4b5fa7e commit bdb66af

15 files changed

Lines changed: 212 additions & 92 deletions

File tree

pyo3-derive-backend/src/pyclass.rs

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,10 @@ impl PyClassArgs {
126126
let flag = exp.path.segments.first().unwrap().ident.to_string();
127127
let path = match flag.as_str() {
128128
"gc" => {
129-
parse_quote! {pyo3::type_object::PY_TYPE_FLAG_GC}
129+
parse_quote! {pyo3::type_flags::GC}
130130
}
131131
"weakref" => {
132-
parse_quote! {pyo3::type_object::PY_TYPE_FLAG_WEAKREF}
132+
parse_quote! {pyo3::type_flags::WEAKREF}
133133
}
134134
"subclass" => {
135135
if cfg!(not(feature = "unsound-subclass")) {
@@ -138,10 +138,10 @@ impl PyClassArgs {
138138
"You need to activate the `unsound-subclass` feature if you want to use subclassing",
139139
));
140140
}
141-
parse_quote! {pyo3::type_object::PY_TYPE_FLAG_BASETYPE}
141+
parse_quote! {pyo3::type_flags::BASETYPE}
142142
}
143143
"dict" => {
144-
parse_quote! {pyo3::type_object::PY_TYPE_FLAG_DICT}
144+
parse_quote! {pyo3::type_flags::DICT}
145145
}
146146
_ => {
147147
return Err(syn::Error::new_spanned(
@@ -267,7 +267,7 @@ fn impl_class(
267267
let extra = {
268268
if let Some(freelist) = &attr.freelist {
269269
quote! {
270-
impl pyo3::freelist::PyObjectWithFreeList for #cls {
270+
impl pyo3::freelist::PyClassWithFreeList for #cls {
271271
#[inline]
272272
fn get_free_list() -> &'static mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> {
273273
static mut FREELIST: *mut pyo3::freelist::FreeList<*mut pyo3::ffi::PyObject> = 0 as *mut _;
@@ -285,7 +285,7 @@ fn impl_class(
285285
}
286286
} else {
287287
quote! {
288-
impl pyo3::type_object::PyObjectAlloc for #cls {}
288+
impl pyo3::pyclass::PyClassAlloc for #cls {}
289289
}
290290
}
291291
};
@@ -308,24 +308,25 @@ fn impl_class(
308308
let mut has_gc = false;
309309
for f in attr.flags.iter() {
310310
if let syn::Expr::Path(ref epath) = f {
311-
if epath.path == parse_quote! {pyo3::type_object::PY_TYPE_FLAG_WEAKREF} {
311+
if epath.path == parse_quote! { pyo3::type_flags::WEAKREF } {
312312
has_weakref = true;
313-
} else if epath.path == parse_quote! {pyo3::type_object::PY_TYPE_FLAG_DICT} {
313+
} else if epath.path == parse_quote! { pyo3::type_flags::DICT } {
314314
has_dict = true;
315-
} else if epath.path == parse_quote! {pyo3::type_object::PY_TYPE_FLAG_GC} {
315+
} else if epath.path == parse_quote! { pyo3::type_flags::GC } {
316316
has_gc = true;
317317
}
318318
}
319319
}
320+
// TODO: implement dict and weakref
320321
let weakref = if has_weakref {
321-
quote! {std::mem::size_of::<*const pyo3::ffi::PyObject>()}
322+
quote! { type WeakRef = pyo3::pyclass_slots::PyClassWeakRefSlot; }
322323
} else {
323-
quote! {0}
324+
quote! { type WeakRef = pyo3::pyclass_slots::PyClassDummySlot; }
324325
};
325326
let dict = if has_dict {
326-
quote! {std::mem::size_of::<*const pyo3::ffi::PyObject>()}
327+
quote! { type Dict = pyo3::pyclass_slots::PyClassDictSlot; }
327328
} else {
328-
quote! {0}
329+
quote! { type Dict = pyo3::pyclass_slots::PyClassDummySlot; }
329330
};
330331
let module = if let Some(m) = &attr.module {
331332
quote! { Some(#m) }
@@ -358,32 +359,25 @@ fn impl_class(
358359
impl pyo3::type_object::PyTypeInfo for #cls {
359360
type Type = #cls;
360361
type BaseType = #base;
362+
type ConcreteLayout = pyo3::pyclass::PyClassShell<Self>;
361363

362364
const NAME: &'static str = #cls_name;
363365
const MODULE: Option<&'static str> = #module;
364366
const DESCRIPTION: &'static str = #doc;
365367
const FLAGS: usize = #(#flags)|*;
366368

367-
const SIZE: usize = {
368-
Self::OFFSET as usize +
369-
::std::mem::size_of::<#cls>() + #weakref + #dict
370-
};
371-
const OFFSET: isize = {
372-
// round base_size up to next multiple of align
373-
(
374-
(<#base as pyo3::type_object::PyTypeInfo>::SIZE +
375-
::std::mem::align_of::<#cls>() - 1) /
376-
::std::mem::align_of::<#cls>() * ::std::mem::align_of::<#cls>()
377-
) as isize
378-
};
379-
380369
#[inline]
381370
unsafe fn type_object() -> &'static mut pyo3::ffi::PyTypeObject {
382371
static mut TYPE_OBJECT: pyo3::ffi::PyTypeObject = pyo3::ffi::PyTypeObject_INIT;
383372
&mut TYPE_OBJECT
384373
}
385374
}
386375

376+
impl pyo3::PyClass for #cls {
377+
#dict
378+
#weakref
379+
}
380+
387381
impl pyo3::IntoPy<PyObject> for #cls {
388382
fn into_py(self, py: pyo3::Python) -> pyo3::PyObject {
389383
pyo3::IntoPy::into_py(pyo3::Py::new(py, self).unwrap(), py)

pyo3-derive-backend/src/pymethod.rs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -237,25 +237,14 @@ pub fn impl_wrap_new(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> T
237237
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#name),"()");
238238
let _py = pyo3::Python::assume_gil_acquired();
239239
let _pool = pyo3::GILPool::new(_py);
240-
match pyo3::type_object::PyRawObject::new(_py, #cls::type_object(), _cls) {
241-
Ok(_obj) => {
242-
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
243-
let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
244-
245-
#body
246-
247-
match _result {
248-
Ok(_) => pyo3::IntoPyPointer::into_ptr(_obj),
249-
Err(e) => {
250-
e.restore(_py);
251-
::std::ptr::null_mut()
252-
}
253-
}
254-
}
255-
Err(e) => {
256-
e.restore(_py);
257-
::std::ptr::null_mut()
258-
}
240+
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
241+
let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
242+
243+
#body
244+
245+
match <<#cls as pyo3::PyTypeInfo>::ConcreteLayout as pyo3::pyclass::PyClassNew>::new(_py, _result) {
246+
Ok(_slf) => _slf as _,
247+
Err(e) => e.restore_and_null(),
259248
}
260249
}
261250
}

src/class/iter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use crate::callback::{CallbackConverter, PyObjectCallbackConverter};
66
use crate::err::PyResult;
7-
use crate::{ffi, IntoPy, PyClass, PyClassShell, PyObject};
7+
use crate::{ffi, pyclass::PyClassShell, IntoPy, PyClass, PyObject};
88
use crate::{IntoPyPointer, Python};
99
use std::ptr;
1010

src/class/macros.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ macro_rules! py_unary_pyref_func {
3535
where
3636
T: for<'p> $trait<'p>,
3737
{
38-
use $crate::{FromPyPointer, PyClassShell};
38+
use $crate::{pyclass::PyClassShell, FromPyPointer};
3939
let py = $crate::Python::assume_gil_acquired();
4040
let _pool = $crate::GILPool::new(py);
4141
let slf: &mut PyClassShell<T> = FromPyPointer::from_borrowed_ptr_or_panic(py, slf);

src/err.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,13 @@ impl PyErr {
326326
unsafe { ffi::PyErr_Restore(ptype.into_ptr(), pvalue, ptraceback.into_ptr()) }
327327
}
328328

329+
#[doc(hidden)]
330+
/// Utility method for proc-macro code
331+
pub fn restore_and_null<T>(self, py: Python) -> *mut T {
332+
self.restore(py);
333+
std::ptr::null_mut()
334+
}
335+
329336
/// Issue a warning message.
330337
/// May return a PyErr if warnings-as-errors is enabled.
331338
pub fn warn(py: Python, category: &PyAny, message: &str, stacklevel: i32) -> PyResult<()> {

src/freelist.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::os::raw::c_void;
1212
/// Implementing this trait for custom class adds free allocation list to class.
1313
/// The performance improvement applies to types that are often created and deleted in a row,
1414
/// so that they can benefit from a freelist.
15-
pub trait PyObjectWithFreeList {
15+
pub trait PyClassWithFreeList {
1616
fn get_free_list() -> &'static mut FreeList<*mut ffi::PyObject>;
1717
}
1818

@@ -70,10 +70,10 @@ impl<T> FreeList<T> {
7070

7171
impl<T> PyClassAlloc for T
7272
where
73-
T: PyTypeInfo + PyObjectWithFreeList,
73+
T: PyTypeInfo + PyClassWithFreeList,
7474
{
7575
unsafe fn alloc(_py: Python) -> *mut Self::ConcreteLayout {
76-
if let Some(obj) = <Self as PyObjectWithFreeList>::get_free_list().pop() {
76+
if let Some(obj) = <Self as PyClassWithFreeList>::get_free_list().pop() {
7777
ffi::PyObject_Init(obj, <Self as PyTypeInfo>::type_object());
7878
obj as _
7979
} else {
@@ -89,7 +89,7 @@ where
8989
return;
9090
}
9191

92-
if let Some(obj) = <Self as PyObjectWithFreeList>::get_free_list().insert(obj) {
92+
if let Some(obj) = <Self as PyClassWithFreeList>::get_free_list().insert(obj) {
9393
match Self::type_object().tp_free {
9494
Some(free) => free(obj as *mut c_void),
9595
None => tp_free_fallback(obj),

src/instance.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,16 @@ unsafe impl<T> Sync for Py<T> {}
3636

3737
impl<T> Py<T> {
3838
/// Create new instance of T and move it under python management
39+
///
40+
/// **NOTE**
41+
/// This method's `where` bound is actually the same as `PyClass`.
42+
/// However, since Rust still doesn't have higher order generics, we cannot represent
43+
/// this bound by `PyClass`.
3944
pub fn new(py: Python, value: T) -> PyResult<Py<T>>
4045
where
4146
T: PyClass,
4247
{
43-
let obj = unsafe { PyClassShell::<T>::new(py, value) };
48+
let obj = unsafe { PyClassShell::new(py, value)? };
4449
let ob = unsafe { Py::from_owned_ptr(obj as _) };
4550
Ok(ob)
4651
}

src/internal_tricks.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,22 @@ use std::rc::Rc;
44
/// A marker type that makes the type !Send.
55
/// Temporal hack until https://github.com/rust-lang/rust/issues/13231 is resolved.
66
pub(crate) type Unsendable = PhantomData<Rc<()>>;
7+
8+
pub struct PrivateMarker;
9+
10+
macro_rules! private_decl {
11+
() => {
12+
/// This trait is private to implement; this method exists to make it
13+
/// impossible to implement outside the crate.
14+
fn __private__(&self) -> crate::internal_tricks::PrivateMarker;
15+
}
16+
}
17+
18+
macro_rules! private_impl {
19+
() => {
20+
#[doc(hidden)]
21+
fn __private__(&self) -> crate::internal_tricks::PrivateMarker {
22+
crate::internal_tricks::PrivateMarker
23+
}
24+
}
25+
}

src/lib.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,12 @@ pub use crate::conversion::{
124124
};
125125
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult};
126126
pub use crate::gil::{init_once, GILGuard, GILPool};
127-
pub use crate::instance::{ManagedPyRef, Py, PyNativeType};
127+
pub use crate::instance::{AsPyRef, ManagedPyRef, Py, PyNativeType};
128128
pub use crate::object::PyObject;
129129
pub use crate::objectprotocol::ObjectProtocol;
130-
pub use crate::pyclass::{PyClass, PyClassAlloc, PyClassShell};
130+
pub use crate::pyclass::{PyClass, PyClassShell};
131131
pub use crate::python::{prepare_freethreaded_python, Python};
132-
pub use crate::type_object::{PyConcreteObject, PyTypeInfo};
132+
pub use crate::type_object::{type_flags, PyConcreteObject, PyTypeInfo};
133133

134134
// Re-exported for wrap_function
135135
#[doc(hidden)]
@@ -161,12 +161,14 @@ pub mod ffi;
161161
pub mod freelist;
162162
mod gil;
163163
mod instance;
164+
#[macro_use]
164165
mod internal_tricks;
165166
pub mod marshal;
166167
mod object;
167168
mod objectprotocol;
168169
pub mod prelude;
169170
pub mod pyclass;
171+
pub mod pyclass_slots;
170172
mod python;
171173
pub mod type_object;
172174
pub mod types;

src/object.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use crate::instance::{AsPyRef, PyNativeType};
77
use crate::types::{PyAny, PyDict, PyTuple};
88
use crate::{AsPyPointer, Py, Python};
99
use crate::{FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, ToBorrowedObject, ToPyObject};
10-
use std::convert::AsRef;
1110
use std::ptr::NonNull;
1211

1312
/// A python object

0 commit comments

Comments
 (0)