diff --git a/CHANGELOG.md b/CHANGELOG.md index e6756cb4609..2247dbeac7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - Fix passing explicit `None` to `Option` argument `#[pyfunction]` with a default value. [#936](https://github.com/PyO3/pyo3/pull/936) -- Fix `PyClass.__new__`'s behavior when inherited by a Python class. [#990](https://github.com/PyO3/pyo3/pull/990) +- Fix `PyClass.__new__`'s not respecting subclasses when inherited by a Python class. [#990](https://github.com/PyO3/pyo3/pull/990) ## [0.10.1] - 2020-05-14 ### Fixed diff --git a/examples/rustapi_module/tests/test_subclassing.py b/examples/rustapi_module/tests/test_subclassing.py index 185162018c2..1f8ce06404e 100644 --- a/examples/rustapi_module/tests/test_subclassing.py +++ b/examples/rustapi_module/tests/test_subclassing.py @@ -7,10 +7,11 @@ class SomeSubClass(Subclassable): def __str__(self): - return "Subclass" + return "SomeSubclass" def test_subclassing(): if not PYPY: a = SomeSubClass() - assert str(a) == "Subclass" + assert str(a) == "SomeSubclass" + assert type(a) is SomeSubClass diff --git a/pyo3-derive-backend/src/pymethod.rs b/pyo3-derive-backend/src/pymethod.rs index fce1b702acf..56471a63974 100644 --- a/pyo3-derive-backend/src/pymethod.rs +++ b/pyo3-derive-backend/src/pymethod.rs @@ -193,7 +193,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { quote! { #[allow(unused_mut)] unsafe extern "C" fn __wrap( - subcls: *mut pyo3::ffi::PyTypeObject, + subtype: *mut pyo3::ffi::PyTypeObject, _args: *mut pyo3::ffi::PyObject, _kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject { @@ -206,7 +206,7 @@ pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> TokenStream { let _result = pyo3::derive_utils::IntoPyNewResult::into_pynew_result(#body)?; let initializer = pyo3::PyClassInitializer::from(_result); - let cell = initializer.create_cell_from_subtype(_py, subcls)?; + let cell = initializer.create_cell_from_subtype(_py, subtype)?; Ok(cell as *mut pyo3::ffi::PyObject) }) } diff --git a/src/freelist.rs b/src/freelist.rs index 6bd9e2a4f24..2f47c7daefc 100644 --- a/src/freelist.rs +++ b/src/freelist.rs @@ -2,10 +2,9 @@ //! Free allocation list -use crate::ffi; use crate::pyclass::{tp_free_fallback, PyClassAlloc}; use crate::type_object::{PyLayout, PyTypeInfo}; -use crate::Python; +use crate::{ffi, AsPyPointer, FromPyPointer, PyAny, Python}; use std::mem; use std::os::raw::c_void; @@ -86,14 +85,15 @@ where unsafe fn dealloc(py: Python, self_: *mut Self::Layout) { (*self_).py_drop(py); - - let obj = self_ as _; - if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 { + let obj = PyAny::from_borrowed_ptr_or_panic(py, self_ as _); + if Self::is_exact_instance(obj) && ffi::PyObject_CallFinalizerFromDealloc(obj.as_ptr()) < 0 + { + // tp_finalize resurrected. return; } - if let Some(obj) = ::get_free_list().insert(obj) { - match Self::type_object_raw(py).tp_free { + if let Some(obj) = ::get_free_list().insert(obj.as_ptr()) { + match (*ffi::Py_TYPE(obj)).tp_free { Some(free) => free(obj as *mut c_void), None => tp_free_fallback(obj), } diff --git a/src/instance.rs b/src/instance.rs index 2dce9efd9f0..97b6335b7e2 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -59,7 +59,7 @@ where T::BaseLayout: PyBorrowFlagLayout, { let initializer = value.into(); - let obj = unsafe { initializer.create_cell(py)? }; + let obj = initializer.create_cell(py)?; let ob = unsafe { Py::from_owned_ptr(py, obj as _) }; Ok(ob) } diff --git a/src/pyclass.rs b/src/pyclass.rs index 9b4cc184882..7fa2ace42c5 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -1,10 +1,10 @@ //! `PyClass` trait use crate::class::methods::{PyClassAttributeDef, PyMethodDefType, PyMethods}; use crate::class::proto_methods::PyProtoMethods; -use crate::conversion::{IntoPyPointer, ToPyObject}; +use crate::conversion::{AsPyPointer, FromPyPointer, IntoPyPointer, ToPyObject}; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; use crate::type_object::{type_flags, PyLayout}; -use crate::types::PyDict; +use crate::types::{PyAny, PyDict}; use crate::{class, ffi, PyCell, PyErr, PyNativeType, PyResult, PyTypeInfo, Python}; use std::ffi::CString; use std::os::raw::c_void; @@ -42,14 +42,16 @@ pub trait PyClassAlloc: PyTypeInfo + Sized { /// `self_` must be a valid pointer to the Python heap. unsafe fn dealloc(py: Python, self_: *mut Self::Layout) { (*self_).py_drop(py); - let obj = self_ as _; - if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 { + let obj = PyAny::from_borrowed_ptr_or_panic(py, self_ as _); + if Self::is_exact_instance(obj) && ffi::PyObject_CallFinalizerFromDealloc(obj.as_ptr()) < 0 + { + // tp_finalize resurrected. return; } - match Self::type_object_raw(py).tp_free { - Some(free) => free(obj as *mut c_void), - None => tp_free_fallback(obj), + match (*ffi::Py_TYPE(obj.as_ptr())).tp_free { + Some(free) => free(obj.as_ptr() as *mut c_void), + None => tp_free_fallback(obj.as_ptr()), } } } @@ -66,8 +68,7 @@ fn tp_dealloc() -> Option { Some(dealloc::) } -#[doc(hidden)] -pub unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) { +pub(crate) unsafe fn tp_free_fallback(obj: *mut ffi::PyObject) { let ty = ffi::Py_TYPE(obj); if ffi::PyType_IS_GC(ty) != 0 { ffi::PyObject_GC_Del(obj as *mut c_void); diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index 162690d9654..51ed4f64059 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -115,16 +115,19 @@ impl PyClassInitializer { } // Create a new PyCell and initialize it. - pub(crate) unsafe fn create_cell(self, py: Python) -> PyResult<*mut PyCell> + pub(crate) fn create_cell(self, py: Python) -> PyResult<*mut PyCell> where T: PyClass, T::BaseLayout: PyBorrowFlagLayout, { - self.create_cell_from_subtype(py, T::type_object_raw(py) as *const _ as _) + unsafe { self.create_cell_from_subtype(py, T::type_object_raw(py) as *const _ as _) } } /// Create a new PyCell and initialize it given a typeobject `subtype`. /// Called by our `tp_new` generated by the `#[new]` attribute. + /// + /// # Safety + /// `cls` must be a subclass of T. pub unsafe fn create_cell_from_subtype( self, py: Python,