Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mbway committed Nov 23, 2024
1 parent d24dbc4 commit 53a13e1
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 28 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/PyO3>"]
readme = "README.md"
Expand All @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion newsfragments/4678.added.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Add support for extending variable/unknown sized base classes (eg `type` to create metaclasses)
Add support for opaque PyObjects allowing extending variable/unknown sized base classes (including `type` to create metaclasses)
2 changes: 1 addition & 1 deletion pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),)*
);
}
Expand Down
3 changes: 2 additions & 1 deletion src/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions src/internal_tricks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,15 @@ pub(crate) const fn ptr_from_ref<T>(t: &T) -> *const T {
pub(crate) fn ptr_from_mut<T>(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>(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>(t: *mut T) -> *const T {
t as *const T
}
41 changes: 22 additions & 19 deletions src/pycell/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -175,6 +178,7 @@ impl<T: PyNativeType + PyTypeInfo> 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());
}
Expand Down Expand Up @@ -214,7 +218,7 @@ pub(crate) mod opaque_layout {
use crate::{impl_::pyclass::PyClassImpl, PyTypeInfo};

#[cfg(Py_3_12)]
pub fn get_contents_ptr<T: PyClassImpl + PyTypeInfo>(
pub(crate) fn get_contents_ptr<T: PyClassImpl + PyTypeInfo>(
obj: *mut ffi::PyObject,
strategy: TypeObjectStrategy<'_>,
) -> *mut PyClassObjectContents<T> {
Expand Down Expand Up @@ -356,7 +360,7 @@ impl PyObjectLayout {
let obj: *mut static_layout::PyStaticClassLayout<T> = obj.cast();
// indicates `ob_base` has type InvalidBaseLayout
debug_assert_ne!(
std::mem::offset_of!(static_layout::PyStaticClassLayout<T>, contents),
offset_of!(static_layout::PyStaticClassLayout<T>, contents),
0,
"invalid ob_base found"
);
Expand All @@ -372,10 +376,10 @@ impl PyObjectLayout {
obj: &'a ffi::PyObject,
strategy: TypeObjectStrategy<'_>,
) -> &'a PyClassObjectContents<T> {
unsafe {
&*PyObjectLayout::get_contents_ptr::<T>(ptr_from_ref(obj).cast_mut(), strategy)
.cast_const()
}
&*cast_const(PyObjectLayout::get_contents_ptr::<T>(
cast_mut(ptr_from_ref(obj)),
strategy,
))
}

/// Obtain a pointer to the portion of `obj` containing the data for `T`
Expand All @@ -398,7 +402,7 @@ impl PyObjectLayout {
obj: &'a ffi::PyObject,
strategy: TypeObjectStrategy<'_>,
) -> &'a T {
unsafe { &*PyObjectLayout::get_data_ptr::<T>(ptr_from_ref(obj).cast_mut(), strategy) }
&*PyObjectLayout::get_data_ptr::<T>(cast_mut(ptr_from_ref(obj)), strategy)
}

/// Obtain a reference to the borrow checker for `obj`
Expand Down Expand Up @@ -436,9 +440,7 @@ impl PyObjectLayout {
py: Python<'_>,
obj: *mut ffi::PyObject,
) {
unsafe {
PyClassRecursiveOperations::<T>::deallocate(py, obj);
};
PyClassRecursiveOperations::<T>::deallocate(py, obj);
}

/// Clean up then free the memory associated with `obj`.
Expand All @@ -450,13 +452,11 @@ impl PyObjectLayout {
py: Python<'_>,
obj: *mut ffi::PyObject,
) {
unsafe {
#[cfg(not(PyPy))]
{
ffi::PyObject_GC_UnTrack(obj.cast());
}
PyClassRecursiveOperations::<T>::deallocate(py, obj);
};
#[cfg(not(PyPy))]
{
ffi::PyObject_GC_UnTrack(obj.cast());
}
PyClassRecursiveOperations::<T>::deallocate(py, obj);
}

/// Used to set `PyType_Spec::basicsize` when creating a `PyTypeObject` for `T`
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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))]
Expand Down Expand Up @@ -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();
});
}

Expand All @@ -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<T: PyClass>(py: Python<'_>) -> usize {
let typ = <T as PyTypeInfo>::type_object(py);
let raw_typ = typ.as_ptr().cast::<ffi::PyTypeObject>();
Expand Down
1 change: 0 additions & 1 deletion src/types/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 1 addition & 3 deletions tests/test_variable_sized_class_basics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ fn class_with_object_field() {
let obj = Bound::new(py, ClassWithObjectField { value: None }).unwrap();
py_run!(py, obj, "obj.value = 5");
let obj_ref = obj.borrow();
let Some(value) = &obj_ref.value else {
panic!("obj_ref.value is None");
};
let value = obj_ref.value.as_ref().unwrap();
assert_eq!(*value.downcast_bound::<PyInt>(py).unwrap(), 5);
});
}

0 comments on commit 53a13e1

Please sign in to comment.