diff --git a/CHANGELOG.md b/CHANGELOG.md index e80cdbdc..8e846f2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * `IntoPyDictPointer` was replace by `IntoPyDict` which doesn't convert `PyDict` itself anymore and returns a `PyDict` instead of `*mut PyObject`. * `PyTuple::new` now takes an `IntoIterator` instead of a slice * Updated to syn 0.15 + * Splitted `PyTypeObject` into `PyTypeObject` without the create method and `PyTypeCreate` with requires `PyObjectAlloc + PyTypeInfo + Sized`. ### Fixed diff --git a/guide/src/class.md b/guide/src/class.md index e9cb3a3c..b52b46fe 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -9,7 +9,6 @@ To define python custom class, rust struct needs to be annotated with `#[pyclass # extern crate pyo3; # use pyo3::prelude::*; - #[pyclass] struct MyClass { num: i32, diff --git a/pyo3-derive-backend/src/py_class.rs b/pyo3-derive-backend/src/py_class.rs index 784ba205..1f311cd8 100644 --- a/pyo3-derive-backend/src/py_class.rs +++ b/pyo3-derive-backend/src/py_class.rs @@ -127,7 +127,7 @@ fn impl_class( FREELIST = Box::into_raw(Box::new( ::pyo3::freelist::FreeList::with_capacity(#freelist))); - <#cls as ::pyo3::typeob::PyTypeObject>::init_type(); + <#cls as ::pyo3::typeob::PyTypeCreate>::init_type(); } &mut *FREELIST } @@ -204,27 +204,6 @@ fn impl_class( } } - impl ::pyo3::typeob::PyTypeObject for #cls { - #[inline] - fn init_type() { - static START: ::std::sync::Once = ::std::sync::ONCE_INIT; - START.call_once(|| { - let ty = unsafe{<#cls as ::pyo3::typeob::PyTypeInfo>::type_object()}; - - if (ty.tp_flags & ::pyo3::ffi::Py_TPFLAGS_READY) == 0 { - let gil = ::pyo3::Python::acquire_gil(); - let py = gil.python(); - - // automatically initialize the class on-demand - ::pyo3::typeob::initialize_type::<#cls>(py, None) - .map_err(|e| e.print(py)) - .expect(format!("An error occurred while initializing class {}", - <#cls as ::pyo3::typeob::PyTypeInfo>::NAME).as_ref()); - } - }); - } - } - // TBH I'm not sure what exactely this does and I'm sure there's a better way, // but for now it works and it only safe code and it is required to return custom // objects, so for now I'm keeping it diff --git a/src/ffi2/object.rs b/src/ffi2/object.rs index c9f88079..9b1bc4ca 100644 --- a/src/ffi2/object.rs +++ b/src/ffi2/object.rs @@ -363,7 +363,7 @@ pub type allocfunc = unsafe extern "C" fn(arg1: *mut PyTypeObject, arg2: Py_ssize_t) -> *mut PyObject; #[repr(C)] -#[derive(Copy)] +#[derive(Copy, Clone)] pub struct PyTypeObject { pub ob_refcnt: Py_ssize_t, pub ob_type: *mut PyTypeObject, @@ -416,13 +416,6 @@ pub struct PyTypeObject { pub tp_version_tag: c_uint, } -impl Clone for PyTypeObject { - #[inline] - fn clone(&self) -> PyTypeObject { - *self - } -} - #[cfg(py_sys_config = "Py_TRACE_REFS")] pub const PyTypeObject_INIT: PyTypeObject = PyTypeObject { _ob_next: ::std::ptr::null_mut(), diff --git a/src/instance.rs b/src/instance.rs index 2bd623b6..ff4cb786 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -14,6 +14,7 @@ use python::{IntoPyPointer, Python, ToPyPointer}; use pythonrun; use typeob::{PyTypeInfo, PyTypeObject}; use types::PyObjectRef; +use typeob::PyTypeCreate; pub struct PyToken(PhantomData>); @@ -175,7 +176,7 @@ where F: FnOnce(::PyToken) -> T, T: PyTypeObject + PyTypeInfo, { - let ob = ::create(py)?; + let ob = ::create(py)?; ob.init(f)?; let ob = unsafe { Py::from_owned_ptr(ob.into_ptr()) }; @@ -189,7 +190,7 @@ where F: FnOnce(::PyToken) -> T, T: PyTypeObject + PyTypeInfo, { - let ob = ::create(py)?; + let ob = ::create(py)?; ob.init(f)?; unsafe { Ok(py.from_owned_ptr(ob.into_ptr())) } @@ -202,7 +203,7 @@ where F: FnOnce(::PyToken) -> T, T: PyTypeObject + PyTypeInfo, { - let ob = ::create(py)?; + let ob = ::create(py)?; ob.init(f)?; unsafe { Ok(py.mut_from_owned_ptr(ob.into_ptr())) } diff --git a/src/python.rs b/src/python.rs index 9e58a32c..3c281ee8 100644 --- a/src/python.rs +++ b/src/python.rs @@ -12,7 +12,8 @@ use std; use std::ffi::CString; use std::marker::PhantomData; use std::os::raw::c_int; -use typeob::{PyObjectAlloc, PyTypeInfo, PyTypeObject}; +use typeob::PyTypeCreate; +use typeob::{PyTypeInfo, PyTypeObject}; use types::{PyDict, PyModule, PyObjectRef, PyType}; /// Marker type that indicates that the GIL is currently held. @@ -253,7 +254,7 @@ impl<'p> Python<'p> { pub fn init(self, f: F) -> PyResult> where F: FnOnce(PyToken) -> T, - T: PyTypeInfo + PyObjectAlloc, + T: PyTypeCreate, { Py::new(self, f) } @@ -264,7 +265,7 @@ impl<'p> Python<'p> { pub fn init_ref(self, f: F) -> PyResult<&'p T> where F: FnOnce(PyToken) -> T, - T: PyTypeInfo + PyObjectAlloc, + T: PyTypeCreate, { Py::new_ref(self, f) } @@ -275,7 +276,7 @@ impl<'p> Python<'p> { pub fn init_mut(self, f: F) -> PyResult<&'p mut T> where F: FnOnce(PyToken) -> T, - T: PyTypeInfo + PyObjectAlloc, + T: PyTypeCreate, { Py::new_mut(self, f) } diff --git a/src/typeob.rs b/src/typeob.rs index fd43b4a9..8b42fbc9 100644 --- a/src/typeob.rs +++ b/src/typeob.rs @@ -217,7 +217,7 @@ where default unsafe fn alloc(_py: Python) -> PyResult<*mut ffi::PyObject> { // TODO: remove this - T::init_type(); + ::init_type(); let tp_ptr = T::type_object(); let alloc = (*tp_ptr).tp_alloc.unwrap_or(ffi::PyType_GenericAlloc); @@ -256,20 +256,42 @@ where } } -/// Trait implemented by Python object types that have a corresponding type object. +/// Python object types that have a corresponding type object. pub trait PyTypeObject { /// Initialize type object fn init_type(); /// Retrieves the type object for this Python object type. fn type_object() -> Py; +} + +/// Python object types that have a corresponding type object and be +/// instanciated with [Self::create()] +pub trait PyTypeCreate: PyObjectAlloc + PyTypeInfo + Sized { + #[inline] + fn init_type() { + let type_object = unsafe { *::type_object() }; + + if (type_object.tp_flags & ffi::Py_TPFLAGS_READY) == 0 { + // automatically initialize the class on-demand + let gil = Python::acquire_gil(); + let py = gil.python(); + + initialize_type::(py, None).unwrap_or_else(|_| { + panic!("An error occurred while initializing class {}", Self::NAME) + }); + } + } + + #[inline] + fn type_object() -> Py { + ::init_type(); + PyType::new::() + } /// Create PyRawObject which can be initialized with rust value #[must_use] - fn create(py: Python) -> PyResult - where - Self: Sized + PyObjectAlloc + PyTypeInfo, - { + fn create(py: Python) -> PyResult { ::init_type(); unsafe { @@ -284,29 +306,18 @@ pub trait PyTypeObject { } } +impl PyTypeCreate for T where T: PyObjectAlloc + PyTypeInfo + Sized {} + impl PyTypeObject for T where - T: PyObjectAlloc + PyTypeInfo, + T: PyTypeCreate, { - #[inline] - default fn init_type() { - unsafe { - if ((*::type_object()).tp_flags & ffi::Py_TPFLAGS_READY) == 0 { - // automatically initialize the class on-demand - let gil = Python::acquire_gil(); - let py = gil.python(); - - initialize_type::(py, None).unwrap_or_else(|_| { - panic!("An error occurred while initializing class {}", T::NAME) - }); - } - } + fn init_type() { + ::init_type() } - #[inline] - default fn type_object() -> Py { - ::init_type(); - PyType::new::() + fn type_object() -> Py { + ::type_object() } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 53860cb5..83d4a4f8 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -125,7 +125,7 @@ macro_rules! pyobject_native_type_convert( } } - impl<$($type_param,)*> $crate::typeob::PyTypeObject for $name { + impl<$($type_param,)*> $crate::typeob::PyTypeCreate for $name { #[inline] fn init_type() {} diff --git a/src/types/string.rs b/src/types/string.rs index da7b75de..c683d5e1 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -10,8 +10,8 @@ use ffi; use instance::{Py, PyObjectWithToken}; use object::PyObject; use python::{Python, ToPyPointer}; -use types::PyObjectRef; use types::exceptions; +use types::PyObjectRef; /// Represents a Python `string`. #[repr(transparent)] @@ -72,7 +72,7 @@ impl PyString { Ok(s) => Ok(Cow::Borrowed(s)), Err(e) => Err(PyErr::from_instance( exceptions::UnicodeDecodeError::new_utf8(self.py(), self.as_bytes(), e)?, - )) + )), } } @@ -120,12 +120,12 @@ impl PyBytes { #[cfg(test)] mod test { - use std::borrow::Cow; - use conversion::{FromPyObject, ToPyObject, PyTryFrom}; - use instance::AsPyRef; - use python::Python; - use object::PyObject; use super::PyString; + use conversion::{FromPyObject, PyTryFrom, ToPyObject}; + use instance::AsPyRef; + use object::PyObject; + use python::Python; + use std::borrow::Cow; #[test] fn test_non_bmp() { diff --git a/src/types/string2.rs b/src/types/string2.rs index 88a7f4c1..34e84567 100644 --- a/src/types/string2.rs +++ b/src/types/string2.rs @@ -88,7 +88,7 @@ impl PyString { Ok(s) => Ok(Cow::Borrowed(s)), Err(e) => Err(PyErr::from_instance( exceptions::UnicodeDecodeError::new_utf8(self.py(), self.as_bytes(), e)?, - )) + )), } } @@ -156,9 +156,8 @@ impl PyUnicode { unsafe { // PyUnicode_AsUTF8String would return null if the pointer did not reference a valid // unicode object, but because we have a valid PyUnicode, assume success - let data: Py = Py::from_owned_ptr( - ffi::PyUnicode_AsUTF8String(self.0.as_ptr()), - ); + let data: Py = + Py::from_owned_ptr(ffi::PyUnicode_AsUTF8String(self.0.as_ptr())); let buffer = ffi::PyBytes_AsString(data.as_ptr()) as *const u8; let length = ffi::PyBytes_Size(data.as_ptr()) as usize; debug_assert!(!buffer.is_null()); @@ -175,7 +174,7 @@ impl PyUnicode { Ok(s) => Ok(Cow::Borrowed(s)), Err(e) => Err(PyErr::from_instance( exceptions::UnicodeDecodeError::new_utf8(self.py(), self.as_bytes(), e)?, - )) + )), } } @@ -205,12 +204,12 @@ impl std::convert::From> for Py { #[cfg(test)] mod test { - use std::borrow::Cow; - use conversion::{FromPyObject, ToPyObject, PyTryFrom}; - use instance::AsPyRef; - use python::Python; - use object::PyObject; use super::PyString; + use conversion::{FromPyObject, PyTryFrom, ToPyObject}; + use instance::AsPyRef; + use object::PyObject; + use python::Python; + use std::borrow::Cow; #[test] fn test_non_bmp() {