From 26920d5ec5ed96612663d03b022f84c0c2793d73 Mon Sep 17 00:00:00 2001 From: Matthew Broadway Date: Fri, 22 Nov 2024 23:20:32 +0000 Subject: [PATCH] fixes --- Cargo.toml | 4 +-- newsfragments/4678.added.md | 2 +- pyo3-macros-backend/src/pyclass.rs | 2 +- src/exceptions.rs | 3 ++- src/internal_tricks.rs | 12 +++++++++ src/pycell/layout.rs | 41 ++++++++++++++++-------------- src/types/set.rs | 1 - 7 files changed, 40 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d7eb03d55d4..42dad19ed8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3" -version = "0.23.1" +version = "0.24.0" description = "Bindings to Python interpreter" authors = ["PyO3 Project and Contributors "] readme = "README.md" @@ -12,7 +12,7 @@ categories = ["api-bindings", "development-tools::ffi"] license = "MIT OR Apache-2.0" exclude = ["/.gitignore", ".cargo/config", "/codecov.yml", "/Makefile", "/pyproject.toml", "/noxfile.py", "/.github", "/tests/test_compile_error.rs", "/tests/ui"] edition = "2021" -rust-version = "1.65" +rust-version = "1.63" [dependencies] cfg-if = "1.0" diff --git a/newsfragments/4678.added.md b/newsfragments/4678.added.md index 8520219a34f..1d8f3293133 100644 --- a/newsfragments/4678.added.md +++ b/newsfragments/4678.added.md @@ -1 +1 @@ -Add support for extending variable/unknown sized base classes (eg `type` to create metaclasses) \ No newline at end of file +Add support for opaque PyObjects allowing extending variable/unknown sized base classes (including `type` to create metaclasses) \ No newline at end of file diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 06a39bab4c5..042b6ebb368 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -1198,7 +1198,7 @@ fn impl_complex_enum_variant_match_args( }); parse_quote! { #[allow(non_upper_case_globals)] - const __match_args__: ( #(#args_tp,)* ) = ( + const #ident: ( #(#args_tp,)* ) = ( #(stringify!(#field_names),)* ); } diff --git a/src/exceptions.rs b/src/exceptions.rs index 4f358ced1f7..d3766ad61f4 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -276,7 +276,8 @@ macro_rules! impl_windows_native_exception ( pub struct $name($crate::PyAny); $crate::impl_exception_boilerplate!($name); - $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject }); + $crate::pyobject_native_type!($name, $layout); + $crate::pyobject_native_type_object_methods!($name, #global_ptr=$crate::ffi::$exc_name); ); ($name:ident, $exc_name:ident, $doc:expr) => ( impl_windows_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject); diff --git a/src/internal_tricks.rs b/src/internal_tricks.rs index 97b13aff2a8..d2e67326c11 100644 --- a/src/internal_tricks.rs +++ b/src/internal_tricks.rs @@ -47,3 +47,15 @@ pub(crate) const fn ptr_from_ref(t: &T) -> *const T { pub(crate) fn ptr_from_mut(t: &mut T) -> *mut T { t as *mut T } + +// TODO: use ptr::cast_mut on MSRV 1.65 +#[inline] +pub(crate) fn cast_mut(t: *const T) -> *mut T { + t as *mut T +} + +// TODO: use ptr::cast_const on MSRV 1.65 +#[inline] +pub(crate) fn cast_const(t: *mut T) -> *const T { + t as *const T +} diff --git a/src/pycell/layout.rs b/src/pycell/layout.rs index 6cd51963fc3..a734154231d 100644 --- a/src/pycell/layout.rs +++ b/src/pycell/layout.rs @@ -6,10 +6,13 @@ use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::ptr::addr_of_mut; +use memoffset::offset_of; + use crate::impl_::pyclass::{ PyClassBaseType, PyClassDict, PyClassImpl, PyClassThreadChecker, PyClassWeakRef, PyObjectOffset, }; use crate::internal::get_slot::{TP_DEALLOC, TP_FREE}; +use crate::internal_tricks::{cast_const, cast_mut}; use crate::pycell::borrow_checker::{GetBorrowChecker, PyClassBorrowChecker}; use crate::type_object::PyNativeType; use crate::types::PyType; @@ -175,6 +178,7 @@ impl PyObjectRecursiveOperations .get_slot(TP_DEALLOC) .expect("PyType_Type should have tp_dealloc"); // `PyType_Type::dealloc` calls `Py_GC_UNTRACK` so we have to re-track before deallocating + #[cfg(not(PyPy))] ffi::PyObject_GC_Track(obj.cast()); return tp_dealloc(obj.cast()); } @@ -214,7 +218,7 @@ pub(crate) mod opaque_layout { use crate::{impl_::pyclass::PyClassImpl, PyTypeInfo}; #[cfg(Py_3_12)] - pub fn get_contents_ptr( + pub(crate) fn get_contents_ptr( obj: *mut ffi::PyObject, strategy: TypeObjectStrategy<'_>, ) -> *mut PyClassObjectContents { @@ -356,7 +360,7 @@ impl PyObjectLayout { let obj: *mut static_layout::PyStaticClassLayout = obj.cast(); // indicates `ob_base` has type InvalidBaseLayout debug_assert_ne!( - std::mem::offset_of!(static_layout::PyStaticClassLayout, contents), + offset_of!(static_layout::PyStaticClassLayout, contents), 0, "invalid ob_base found" ); @@ -372,10 +376,10 @@ impl PyObjectLayout { obj: &'a ffi::PyObject, strategy: TypeObjectStrategy<'_>, ) -> &'a PyClassObjectContents { - unsafe { - &*PyObjectLayout::get_contents_ptr::(ptr_from_ref(obj).cast_mut(), strategy) - .cast_const() - } + &*cast_const(PyObjectLayout::get_contents_ptr::( + cast_mut(ptr_from_ref(obj)), + strategy, + )) } /// Obtain a pointer to the portion of `obj` containing the data for `T` @@ -398,7 +402,7 @@ impl PyObjectLayout { obj: &'a ffi::PyObject, strategy: TypeObjectStrategy<'_>, ) -> &'a T { - unsafe { &*PyObjectLayout::get_data_ptr::(ptr_from_ref(obj).cast_mut(), strategy) } + &*PyObjectLayout::get_data_ptr::(cast_mut(ptr_from_ref(obj)), strategy) } /// Obtain a reference to the borrow checker for `obj` @@ -436,9 +440,7 @@ impl PyObjectLayout { py: Python<'_>, obj: *mut ffi::PyObject, ) { - unsafe { - PyClassRecursiveOperations::::deallocate(py, obj); - }; + PyClassRecursiveOperations::::deallocate(py, obj); } /// Clean up then free the memory associated with `obj`. @@ -450,13 +452,11 @@ impl PyObjectLayout { py: Python<'_>, obj: *mut ffi::PyObject, ) { - unsafe { - #[cfg(not(PyPy))] - { - ffi::PyObject_GC_UnTrack(obj.cast()); - } - PyClassRecursiveOperations::::deallocate(py, obj); - }; + #[cfg(not(PyPy))] + { + ffi::PyObject_GC_UnTrack(obj.cast()); + } + PyClassRecursiveOperations::::deallocate(py, obj); } /// Used to set `PyType_Spec::basicsize` when creating a `PyTypeObject` for `T` @@ -613,6 +613,7 @@ mod static_tests { /// Test the functions calculate properties about the static layout without requiring an instance. /// The class in this test requires extra space for the `dict` and `weaklist` fields #[test] + #[cfg(any(Py_3_9, not(Py_LIMITED_API)))] fn test_layout_properties_no_inheritance_optional_fields() { #[pyclass(crate = "crate", dict, weakref, extends=PyAny)] struct MyClass(#[allow(unused)] u64); @@ -1041,7 +1042,7 @@ mod static_tests { contents: u8, } - assert_eq!(std::mem::offset_of!(InvalidLayout, contents), 0); + assert_eq!(offset_of!(InvalidLayout, contents), 0); } } @@ -1051,6 +1052,7 @@ mod static_tests { mod opaque_tests { use memoffset::offset_of; use static_assertions::const_assert; + use std::mem::size_of; use std::ops::Range; #[cfg(not(Py_LIMITED_API))] @@ -1704,7 +1706,7 @@ mod opaque_fail_tests { )] fn test_panic_at_construction_inherit_opaque() { Python::with_gil(|py| { - Py::new(py, Metaclass::default()).unwrap(); + Py::new(py, Metaclass).unwrap(); }); } @@ -1726,6 +1728,7 @@ mod test_utils { /// The size in bytes of a [ffi::PyObject] of the type `T` #[cfg(not(Py_LIMITED_API))] + #[allow(unused)] pub fn get_pyobject_size(py: Python<'_>) -> usize { let typ = ::type_object(py); let raw_typ = typ.as_ptr().cast::(); diff --git a/src/types/set.rs b/src/types/set.rs index 87f6749ab97..51ced5fac5b 100644 --- a/src/types/set.rs +++ b/src/types/set.rs @@ -29,7 +29,6 @@ pyobject_native_type!(PySet, ffi::PySetObject, #checkfunction=ffi::PySet_Check); #[cfg(any(PyPy, GraalPy))] pyobject_native_type_core!(PySet, #checkfunction=ffi::PySet_Check); -#[cfg(not(any(PyPy, GraalPy)))] pyobject_native_type_object_methods!(PySet, #global=ffi::PySet_Type); impl PySet {