diff --git a/pyo3cls/src/py_class.rs b/pyo3cls/src/py_class.rs index 7d5ca5fe..e0aedfa5 100644 --- a/pyo3cls/src/py_class.rs +++ b/pyo3cls/src/py_class.rs @@ -107,6 +107,27 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident, token: Option) -> } } + impl _pyo3::typeob::PyTypeObject for #cls { + #[inline(always)] + fn init_type(py: Python) { + static START: std::sync::Once = std::sync::ONCE_INIT; + START.call_once(|| { + let mut ty = <#cls as _pyo3::typeob::PyTypeInfo>::type_object(); + + if (ty.tp_flags & _pyo3::ffi::Py_TPFLAGS_READY) == 0 { + // automatically initialize the class on-demand + let to = _pyo3::typeob::initialize_type::<#cls>( + py, None, <#cls as _pyo3::typeob::PyTypeInfo>::type_name(), ty) + .expect( + format!("An error occurred while initializing class {}", + <#cls as _pyo3::typeob::PyTypeInfo>::type_name()) + .as_ref()); + py.release(to); + } + }); + } + } + impl _pyo3::python::PyDowncastFrom for #cls { fn downcast_from<'a, 'p>(py: Python<'p>, ob: &'a _pyo3::PyObject) diff --git a/src/err.rs b/src/err.rs index b3e1dd64..2bcc14fe 100644 --- a/src/err.rs +++ b/src/err.rs @@ -52,24 +52,32 @@ macro_rules! py_exception { pub fn new(py: $crate::Python, args: T) -> $crate::PyErr { $crate::PyErr::new::<$name, T>(py, args) } + #[inline(always)] + fn type_object(py: $crate::Python) -> *mut $crate::ffi::PyTypeObject { + #[allow(non_upper_case_globals)] + static mut type_object: *mut $crate::ffi::PyTypeObject = + 0 as *mut $crate::ffi::PyTypeObject; + + unsafe { + if type_object.is_null() { + type_object = $crate::PyErr::new_type( + py, concat!(stringify!($module), ".", stringify!($name)), + Some(py.get_type::<$base>()), None).as_type_ptr(); + } + type_object + } + } } impl $crate::PyTypeObject for $name { + #[inline(always)] + fn init_type(py: $crate::Python) { + let _ = $name::type_object(py); + } + #[inline] fn type_object(py: $crate::Python) -> $crate::PyType { - unsafe { - #[allow(non_upper_case_globals)] - static mut type_object: *mut $crate::ffi::PyTypeObject = 0 as *mut $crate::ffi::PyTypeObject; - - if type_object.is_null() { - type_object = $crate::PyErr::new_type( - py, - concat!(stringify!($module), ".", stringify!($name)), - Some(py.get_type::<$base>()), None).as_type_ptr(); - } - - $crate::PyType::from_type_ptr(py, type_object) - } + unsafe { $crate::PyType::from_type_ptr(py, $name::type_object(py)) } } } }; diff --git a/src/objects/exc.rs b/src/objects/exc.rs index b5a0a2d5..076b66a7 100644 --- a/src/objects/exc.rs +++ b/src/objects/exc.rs @@ -20,6 +20,9 @@ macro_rules! exc_type( // pyobject_newtype!($name); impl $crate::PyTypeObject for $name { + #[inline(always)] + fn init_type(_py: Python) {} + #[inline] fn type_object(py: $crate::python::Python) -> $crate::PyType { unsafe { PyType::from_type_ptr(py, ffi::$exc_name as *mut ffi::PyTypeObject) } diff --git a/src/objects/mod.rs b/src/objects/mod.rs index a5da7ba8..d4a8c9f9 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -39,12 +39,10 @@ macro_rules! pyobject_nativetype( fn size() -> usize { $crate::std::mem::size_of::() } - #[inline] fn offset() -> isize { 0 } - #[inline] fn type_name() -> &'static str { stringify!($name) @@ -55,6 +53,16 @@ macro_rules! pyobject_nativetype( } } + impl $crate::typeob::PyTypeObject for $name { + #[inline(always)] + fn init_type(_py: Python) {} + + #[inline] + fn type_object(py: $crate::Python) -> $crate::PyType { + unsafe { $crate::PyType::from_type_ptr(py, &mut $crate::ffi::$typeobject) } + } + } + pyobject_nativetype!($name, $checkfunction); }; diff --git a/src/typeob.rs b/src/typeob.rs index a0b63c1b..578c3f5e 100644 --- a/src/typeob.rs +++ b/src/typeob.rs @@ -84,8 +84,7 @@ impl PyObjectAlloc for T where T : PyTypeInfo { /// must be of type `ty`. unsafe fn alloc(py: Python, value: T::Type) -> PyResult<*mut ffi::PyObject> { // TODO: remove this - let t = ::type_object(py); - py.release(t); + ::init_type(py); let obj = ffi::PyType_GenericAlloc( ::type_object(), 0); @@ -121,6 +120,9 @@ impl PyObjectAlloc for T where T : PyTypeInfo { /// Trait implemented by Python object types that have a corresponding type object. pub trait PyTypeObject { + /// Initialize type object + fn init_type(py: Python); + /// Retrieves the type object for this Python object type. fn type_object(py: Python) -> PyType; @@ -129,24 +131,28 @@ pub trait PyTypeObject { impl PyTypeObject for T where T: PyObjectAlloc + PyTypeInfo { #[inline] - fn type_object(py: Python) -> PyType { + default fn init_type(py: Python) { let mut ty = ::type_object(); - //return unsafe { PyType::from_type_ptr(py, ty) }; - if (ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0 { - unsafe { PyType::from_type_ptr(py, ty) } - } else { + if (ty.tp_flags & ffi::Py_TPFLAGS_READY) == 0 { // automatically initialize the class on-demand let to = initialize_type::( py, None, ::type_name(), ty).expect( format!("An error occurred while initializing class {}", ::type_name()).as_ref()); py.release(to); - unsafe { PyType::from_type_ptr(py, ty) } } } + + #[inline] + default fn type_object(py: Python) -> PyType { + ::init_type(py); + + unsafe { PyType::from_type_ptr(py, ::type_object()) } + } } + pub fn initialize_type(py: Python, module_name: Option<&str>, type_name: &str, type_object: &mut ffi::PyTypeObject) -> PyResult where T: PyObjectAlloc + PyTypeInfo @@ -294,27 +300,21 @@ fn py_class_method_defs(py: Python, type_object: *mut ffi::PyTypeObject) for def in ::methods() { defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?; - //defs.push(def.as_method_def()) } for def in ::methods() { defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?; - //defs.push(def.as_method_def()) } for def in ::methods() { defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?; - //defs.push(def.as_method_def()) } for def in ::methods() { defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?; - //defs.push(def.as_method_def()) } for def in ::methods() { defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?; - //defs.push(def.as_method_def()) } for def in ::methods() { defs.set_item(py, def.ml_name, def.as_method_descr(py, type_object)?)?; - //defs.push(def.as_method_def()) } Ok((new, call, defs)) diff --git a/tests/test_class.rs b/tests/test_class.rs index d7b05be0..bc1fbe0b 100644 --- a/tests/test_class.rs +++ b/tests/test_class.rs @@ -16,7 +16,6 @@ macro_rules! py_run { ($py:expr, $val:ident, $code:expr) => {{ let d = PyDict::new($py); d.set_item($py, stringify!($val), &$val).unwrap(); - //$py.run($code, None, Some(&d)).map_err(|e| e.print($py)).expect($code); $py.run($code, None, Some(&d)).expect($code); }} } @@ -1136,5 +1135,4 @@ fn class_with_properties() { py_run!(py, inst, "inst.DATA = 20"); py_run!(py, inst, "assert inst.get_num() == 20"); py_run!(py, inst, "assert inst.get_num() == inst.DATA"); - }