Skip to content

Commit 8b2297d

Browse files
committed
Better trait bounds with PyMethodsProtocol
1 parent e4f24d0 commit 8b2297d

File tree

5 files changed

+35
-53
lines changed

5 files changed

+35
-53
lines changed

src/freelist.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use crate::err::PyResult;
66
use crate::ffi;
77
use crate::python::Python;
88
use crate::typeob::{pytype_drop, PyObjectAlloc, PyTypeInfo};
9-
use class::methods::PyMethodsProtocol;
109
use std::mem;
1110
use std::os::raw::c_void;
1211

@@ -71,7 +70,7 @@ impl<T> FreeList<T> {
7170

7271
impl<T> PyObjectAlloc for T
7372
where
74-
T: PyObjectWithFreeList + PyMethodsProtocol,
73+
T: PyObjectWithFreeList,
7574
{
7675
unsafe fn alloc(_py: Python) -> PyResult<*mut ffi::PyObject> {
7776
let obj = if let Some(obj) = <Self as PyObjectWithFreeList>::get_free_list().pop() {

src/typeob.rs

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,8 @@ pub(crate) unsafe fn pytype_drop<T: PyTypeInfo>(py: Python, obj: *mut ffi::PyObj
197197
///
198198
/// All native types and all `#[pyclass]` types use the default functions, while
199199
/// [PyObjectWithFreeList](crate::freelist::PyObjectWithFreeList) gets a special version.
200-
pub trait PyObjectAlloc: PyTypeInfo + PyMethodsProtocol + Sized {
200+
pub trait PyObjectAlloc: PyTypeInfo + Sized {
201201
unsafe fn alloc(_py: Python) -> PyResult<*mut ffi::PyObject> {
202-
// TODO: remove this
203-
<Self as PyTypeCreate>::init_type();
204-
205202
let tp_ptr = Self::type_object();
206203
let alloc = (*tp_ptr).tp_alloc.unwrap_or(ffi::PyType_GenericAlloc);
207204
let obj = alloc(tp_ptr, 0);
@@ -259,21 +256,8 @@ pub trait PyTypeObject {
259256

260257
/// Python object types that have a corresponding type object and be
261258
/// instanciated with [Self::create()]
262-
pub trait PyTypeCreate: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol + Sized {
263-
#[inline]
264-
fn init_type() {
265-
let type_object = unsafe { *<Self as PyTypeInfo>::type_object() };
266-
267-
if (type_object.tp_flags & ffi::Py_TPFLAGS_READY) == 0 {
268-
// automatically initialize the class on-demand
269-
let gil = Python::acquire_gil();
270-
let py = gil.python();
271-
272-
initialize_type::<Self>(py, None).unwrap_or_else(|_| {
273-
panic!("An error occurred while initializing class {}", Self::NAME)
274-
});
275-
}
276-
}
259+
pub trait PyTypeCreate: PyObjectAlloc + PyTypeInfo + Sized {
260+
fn init_type();
277261

278262
#[inline]
279263
fn type_object() -> Py<PyType> {
@@ -298,7 +282,25 @@ pub trait PyTypeCreate: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol + Sized {
298282
}
299283
}
300284

301-
impl<T> PyTypeCreate for T where T: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol {}
285+
impl<T> PyTypeCreate for T
286+
where
287+
T: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol,
288+
{
289+
#[inline]
290+
fn init_type() {
291+
let type_object = unsafe { *<Self as PyTypeInfo>::type_object() };
292+
293+
if (type_object.tp_flags & ffi::Py_TPFLAGS_READY) == 0 {
294+
// automatically initialize the class on-demand
295+
let gil = Python::acquire_gil();
296+
let py = gil.python();
297+
298+
initialize_type::<Self>(py, None).unwrap_or_else(|_| {
299+
panic!("An error occurred while initializing class {}", Self::NAME)
300+
});
301+
}
302+
}
303+
}
302304

303305
impl<T> PyTypeObject for T
304306
where
@@ -314,8 +316,10 @@ where
314316
}
315317

316318
/// Register new type in python object system.
319+
///
320+
/// Currently, module_name is always None, so it defaults to builtins.
317321
#[cfg(not(Py_LIMITED_API))]
318-
pub fn initialize_type<T>(py: Python, module_name: Option<&str>) -> PyResult<()>
322+
pub fn initialize_type<T>(py: Python, module_name: Option<&str>) -> PyResult<*mut ffi::PyTypeObject>
319323
where
320324
T: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol,
321325
{
@@ -432,7 +436,7 @@ where
432436
// register type object
433437
unsafe {
434438
if ffi::PyType_Ready(type_object) == 0 {
435-
Ok(())
439+
Ok(type_object as *mut ffi::PyTypeObject)
436440
} else {
437441
PyErr::fetch(py).into()
438442
}

src/types/mod.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,6 @@ macro_rules! pyobject_native_type_convert(
127127
}
128128
}
129129

130-
// We currently need to fulfill this trait bound for PyTypeCreate, even though we know
131-
// that the function will never actuall be called
132-
impl<$($type_param,)*> $crate::class::methods::PyMethodsProtocol for $name {
133-
fn py_methods() -> Vec<&'static $crate::class::methods::PyMethodDefType> {
134-
unreachable!();
135-
}
136-
}
137-
138130
impl<$($type_param,)*> $crate::typeob::PyObjectAlloc for $name {}
139131

140132
impl<$($type_param,)*> $crate::typeob::PyTypeCreate for $name {

src/types/module.rs

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ use crate::instance::PyObjectWithGIL;
99
use crate::object::PyObject;
1010
use crate::objectprotocol::ObjectProtocol;
1111
use crate::python::{Python, ToPyPointer};
12-
use crate::typeob::{initialize_type, PyTypeInfo};
13-
use crate::types::{exceptions, PyDict, PyObjectRef, PyType};
14-
use crate::PyObjectAlloc;
15-
use class::methods::PyMethodsProtocol;
12+
use crate::types::{exceptions, PyDict, PyObjectRef};
1613
use std::ffi::{CStr, CString};
1714
use std::os::raw::c_char;
1815
use std::str;
16+
use typeob::PyTypeCreate;
1917

2018
/// Represents a Python `module` object.
2119
#[repr(transparent)]
@@ -151,23 +149,9 @@ impl PyModule {
151149
/// and adds the type to this module.
152150
pub fn add_class<T>(&self) -> PyResult<()>
153151
where
154-
T: PyTypeInfo + PyObjectAlloc + PyMethodsProtocol,
152+
T: PyTypeCreate,
155153
{
156-
let ty = unsafe {
157-
let ty = <T as PyTypeInfo>::type_object();
158-
159-
if ((*ty).tp_flags & ffi::Py_TPFLAGS_READY) != 0 {
160-
PyType::new::<T>()
161-
} else {
162-
// automatically initialize the class
163-
initialize_type::<T>(self.py(), Some(self.name()?)).unwrap_or_else(|_| {
164-
panic!("An error occurred while initializing class {}", T::NAME)
165-
});
166-
PyType::new::<T>()
167-
}
168-
};
169-
170-
self.setattr(T::NAME, ty)
154+
self.setattr(T::NAME, <T as PyTypeCreate>::type_object())
171155
}
172156

173157
/// Adds a function or a (sub)module to a module, using the functions __name__ as name.

tests/test_class_basics.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,14 @@ fn empty_class_in_module() {
6666
ty.getattr("__name__").unwrap().extract::<String>().unwrap(),
6767
"EmptyClassInModule"
6868
);
69+
// Rationale: The class can be added to many modules, but will only be initialized once.
70+
// We currently have no way of determining a canonical module, so builtins is better
71+
// than using whatever calls init first.
6972
assert_eq!(
7073
ty.getattr("__module__")
7174
.unwrap()
7275
.extract::<String>()
7376
.unwrap(),
74-
"test_module.nested"
77+
"builtins"
7578
);
7679
}

0 commit comments

Comments
 (0)