Skip to content

Commit 0cb6dc7

Browse files
committed
Better trait bounds with PyMethodsProtocol
1 parent b81a56a commit 0cb6dc7

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
@@ -199,11 +199,8 @@ pub(crate) unsafe fn pytype_drop<T: PyTypeInfo>(py: Python, obj: *mut ffi::PyObj
199199
///
200200
/// All native types and all `#[pyclass]` types use the default functions, while
201201
/// [PyObjectWithFreeList](crate::freelist::PyObjectWithFreeList) gets a special version.
202-
pub trait PyObjectAlloc: PyTypeInfo + PyMethodsProtocol + Sized {
202+
pub trait PyObjectAlloc: PyTypeInfo + Sized {
203203
unsafe fn alloc(_py: Python) -> PyResult<*mut ffi::PyObject> {
204-
// TODO: remove this
205-
<Self as PyTypeCreate>::init_type();
206-
207204
let tp_ptr = Self::type_object();
208205
let alloc = (*tp_ptr).tp_alloc.unwrap_or(ffi::PyType_GenericAlloc);
209206
let obj = alloc(tp_ptr, 0);
@@ -261,21 +258,8 @@ pub trait PyTypeObject {
261258

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

280264
#[inline]
281265
fn type_object() -> Py<PyType> {
@@ -300,7 +284,25 @@ pub trait PyTypeCreate: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol + Sized {
300284
}
301285
}
302286

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

305307
impl<T> PyTypeObject for T
306308
where
@@ -316,8 +318,10 @@ where
316318
}
317319

318320
/// Register new type in python object system.
321+
///
322+
/// Currently, module_name is always None, so it defaults to builtins.
319323
#[cfg(not(Py_LIMITED_API))]
320-
pub fn initialize_type<T>(py: Python, module_name: Option<&str>) -> PyResult<()>
324+
pub fn initialize_type<T>(py: Python, module_name: Option<&str>) -> PyResult<*mut ffi::PyTypeObject>
321325
where
322326
T: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol,
323327
{
@@ -434,7 +438,7 @@ where
434438
// register type object
435439
unsafe {
436440
if ffi::PyType_Ready(type_object) == 0 {
437-
Ok(())
441+
Ok(type_object as *mut ffi::PyTypeObject)
438442
} else {
439443
PyErr::fetch(py).into()
440444
}

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
@@ -68,11 +68,14 @@ fn empty_class_in_module() {
6868
ty.getattr("__name__").unwrap().extract::<String>().unwrap(),
6969
"EmptyClassInModule"
7070
);
71+
// Rationale: The class can be added to many modules, but will only be initialized once.
72+
// We currently have no way of determining a canonical module, so builtins is better
73+
// than using whatever calls init first.
7174
assert_eq!(
7275
ty.getattr("__module__")
7376
.unwrap()
7477
.extract::<String>()
7578
.unwrap(),
76-
"test_module.nested"
79+
"builtins"
7780
);
7881
}

0 commit comments

Comments
 (0)