Skip to content

Commit 5499d10

Browse files
committed
fix some tests and linting errors
1 parent d0961fe commit 5499d10

11 files changed

+94
-49
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ categories = ["api-bindings", "development-tools::ffi"]
1212
license = "MIT OR Apache-2.0"
1313
exclude = ["/.gitignore", ".cargo/config", "/codecov.yml", "/Makefile", "/pyproject.toml", "/noxfile.py", "/.github", "/tests/test_compile_error.rs", "/tests/ui"]
1414
edition = "2021"
15-
rust-version = "1.63"
15+
rust-version = "1.65"
1616

1717
[dependencies]
1818
cfg-if = "1.0"

guide/src/class/metaclass.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ impl MyMetaclass {
2929
slf: Bound<'_, Metaclass>,
3030
_args: Bound<'_, PyTuple>,
3131
_kwargs: Option<Bound<'_, PyDict>>,
32-
) -> PyResult<()> {
32+
) {
3333
slf.borrow_mut().counter = 5;
34-
Ok(())
3534
}
3635

3736
fn __getitem__(&self, item: u64) -> u64 {

src/impl_/pyclass.rs

+17-21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#[cfg(Py_3_12)]
2+
use crate::pycell::impl_::PyClassObjectContents;
13
use crate::{
24
conversion::IntoPyObject,
35
exceptions::{PyAttributeError, PyNotImplementedError, PyRuntimeError, PyValueError},
@@ -8,10 +10,7 @@ use crate::{
810
pyclass_init::PyObjectInit,
911
pymethods::{PyGetterDef, PyMethodDefType},
1012
},
11-
pycell::{
12-
impl_::{InternalPyClassObjectLayout, PyClassObjectContents},
13-
PyBorrowError,
14-
},
13+
pycell::{impl_::InternalPyClassObjectLayout, PyBorrowError},
1514
types::{any::PyAnyMethods, PyBool},
1615
Borrowed, BoundObject, Py, PyAny, PyClass, PyErr, PyRef, PyResult, PyTypeInfo, Python,
1716
};
@@ -1197,20 +1196,9 @@ pub enum PyObjectOffset {
11971196
/// An offset relative to the start of the subclass-specific data.
11981197
/// Only allowed when basicsize is negative (which is only allowed for python >=3.12).
11991198
/// <https://docs.python.org/3.12/c-api/structures.html#c.Py_RELATIVE_OFFSET>
1200-
#[cfg(Py_3_12)]
12011199
Relative(ffi::Py_ssize_t),
12021200
}
12031201

1204-
impl PyObjectOffset {
1205-
pub fn to_value_and_is_relative(&self) -> (ffi::Py_ssize_t, bool) {
1206-
match self {
1207-
PyObjectOffset::Absolute(offset) => (*offset, false),
1208-
#[cfg(Py_3_12)]
1209-
PyObjectOffset::Relative(offset) => (*offset, true),
1210-
}
1211-
}
1212-
}
1213-
12141202
impl std::ops::Add<usize> for PyObjectOffset {
12151203
type Output = PyObjectOffset;
12161204

@@ -1221,7 +1209,6 @@ impl std::ops::Add<usize> for PyObjectOffset {
12211209

12221210
match self {
12231211
PyObjectOffset::Absolute(offset) => PyObjectOffset::Absolute(offset + rhs),
1224-
#[cfg(Py_3_12)]
12251212
PyObjectOffset::Relative(offset) => PyObjectOffset::Relative(offset + rhs),
12261213
}
12271214
}
@@ -1321,11 +1308,16 @@ impl<
13211308
pub fn generate(&self, name: &'static CStr, doc: &'static CStr) -> PyMethodDefType {
13221309
use crate::pyclass::boolean_struct::private::Boolean;
13231310
if ClassT::Frozen::VALUE {
1324-
let (offset, is_relative) = Offset::offset().to_value_and_is_relative();
1325-
let flags = if is_relative {
1326-
ffi::Py_READONLY | ffi::Py_RELATIVE_OFFSET
1327-
} else {
1328-
ffi::Py_READONLY
1311+
let (offset, flags) = match Offset::offset() {
1312+
PyObjectOffset::Absolute(offset) => (offset, ffi::Py_READONLY),
1313+
#[cfg(Py_3_12)]
1314+
PyObjectOffset::Relative(offset) => {
1315+
(offset, ffi::Py_READONLY | ffi::Py_RELATIVE_OFFSET)
1316+
}
1317+
#[cfg(not(Py_3_12))]
1318+
PyObjectOffset::Relative(_) => {
1319+
panic!("relative offsets not valid before python 3.12");
1320+
}
13291321
};
13301322
PyMethodDefType::StructMember(ffi::PyMemberDef {
13311323
name: name.as_ptr(),
@@ -1560,6 +1552,10 @@ where
15601552
let contents = class_obj.contents_mut() as *mut PyClassObjectContents<ClassT>;
15611553
(contents.cast::<u8>(), offset)
15621554
}
1555+
#[cfg(not(Py_3_12))]
1556+
PyObjectOffset::Relative(_) => {
1557+
panic!("relative offsets not valid before python 3.12");
1558+
}
15631559
};
15641560
// Safety: conditions for pointer addition must be met
15651561
unsafe { base.add(offset as usize) }.cast::<FieldT>()

src/pycell/impl_.rs

+9
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,14 @@ pub trait PyClassObjectLayout<T>: PyLayout<T> {
248248
}
249249

250250
#[doc(hidden)]
251+
#[cfg_attr(
252+
all(diagnostic_namespace),
253+
diagnostic::on_unimplemented(
254+
message = "the class layout is not valid",
255+
label = "required for `#[pyclass(extends=...)]`",
256+
note = "the python version being built against influences which layouts are valid",
257+
)
258+
)]
251259
pub trait InternalPyClassObjectLayout<T: PyClassImpl>: PyClassObjectLayout<T> {
252260
/// Obtain a pointer to the contents of an uninitialized PyObject of this type
253261
/// Safety: the provided object must have the layout that the implementation is expecting
@@ -551,6 +559,7 @@ impl<T: PyClassImpl> InternalPyClassObjectLayout<T> for PyVariableClassObject<T>
551559

552560
unsafe impl<T: PyClassImpl> PyLayout<T> for PyVariableClassObject<T> {}
553561

562+
#[cfg(Py_3_12)]
554563
impl<T: PyClassImpl> PyClassObjectLayout<T> for PyVariableClassObject<T>
555564
where
556565
<T::BaseType as PyClassBaseType>::LayoutAsBase: PyClassObjectLayout<T::BaseType>,

src/pyclass/create_type_object.rs

+25-13
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::{
1919
collections::HashMap,
2020
ffi::{CStr, CString},
2121
os::raw::{c_char, c_int, c_ulong, c_void},
22-
ptr,
22+
ptr::{self, addr_of_mut},
2323
};
2424

2525
pub(crate) struct PyClassTypeObject {
@@ -260,7 +260,11 @@ impl PyTypeBuilder {
260260
}
261261

262262
get_dict = get_dict_impl;
263-
closure = dict_offset as _;
263+
if let PyObjectOffset::Absolute(offset) = dict_offset {
264+
closure = offset as _;
265+
} else {
266+
unreachable!("PyObjectOffset::Relative requires >=3.12");
267+
}
264268
}
265269

266270
property_defs.push(ffi::PyGetSetDef {
@@ -370,11 +374,16 @@ impl PyTypeBuilder {
370374
{
371375
#[inline(always)]
372376
fn offset_def(name: &'static CStr, offset: PyObjectOffset) -> ffi::PyMemberDef {
373-
let (offset, is_relative) = offset.to_value_and_is_relative();
374-
let flags = if is_relative {
375-
ffi::Py_READONLY | ffi::Py_RELATIVE_OFFSET
376-
} else {
377-
ffi::Py_READONLY
377+
let (offset, flags) = match offset {
378+
PyObjectOffset::Absolute(offset) => (offset, ffi::Py_READONLY),
379+
#[cfg(Py_3_12)]
380+
PyObjectOffset::Relative(offset) => {
381+
(offset, ffi::Py_READONLY | ffi::Py_RELATIVE_OFFSET)
382+
}
383+
#[cfg(not(Py_3_12))]
384+
PyObjectOffset::Relative(_) => {
385+
panic!("relative offsets not valid before python 3.12");
386+
}
378387
};
379388
ffi::PyMemberDef {
380389
name: name.as_ptr().cast(),
@@ -414,16 +423,19 @@ impl PyTypeBuilder {
414423
Some(PyObjectOffset::Absolute(offset)) => {
415424
(*type_object).tp_dictoffset = offset;
416425
}
417-
// PyObjectOffset::Relative requires >=3.12
418-
_ => {}
426+
Some(PyObjectOffset::Relative(_)) => {
427+
panic!("PyObjectOffset::Relative requires >=3.12")
428+
}
429+
None => {}
419430
}
420-
421431
match weaklist_offset {
422432
Some(PyObjectOffset::Absolute(offset)) => {
423433
(*type_object).tp_weaklistoffset = offset;
424434
}
425-
// PyObjectOffset::Relative requires >=3.12
426-
_ => {}
435+
Some(PyObjectOffset::Relative(_)) => {
436+
panic!("PyObjectOffset::Relative requires >=3.12")
437+
}
438+
None => {}
427439
}
428440
}));
429441
}
@@ -447,7 +459,7 @@ impl PyTypeBuilder {
447459

448460
// Safety: self.tp_base must be a valid PyTypeObject
449461
let is_metaclass =
450-
unsafe { ffi::PyType_IsSubtype(self.tp_base, &raw mut ffi::PyType_Type) } != 0;
462+
unsafe { ffi::PyType_IsSubtype(self.tp_base, addr_of_mut!(ffi::PyType_Type)) } != 0;
451463
if is_metaclass {
452464
// if the pyclass derives from `type` (is a metaclass) then `tp_new` must not be set.
453465
// Metaclasses that override tp_new are not supported.

src/type_object.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
33
use crate::ffi_ptr_ext::FfiPtrExt;
44
use crate::impl_::pyclass::PyClassImpl;
5-
use crate::pycell::impl_::InternalPyClassObjectLayout;
65
use crate::types::any::PyAnyMethods;
76
use crate::types::{PyAny, PyType};
87
use crate::{ffi, Bound, Python};
@@ -44,8 +43,9 @@ pub unsafe trait PyTypeInfo: Sized {
4443
/// Module name, if any.
4544
const MODULE: Option<&'static str>;
4645

47-
/// The type of object layout to use for ancestors or descendents of this type
48-
type Layout<T: PyClassImpl>: InternalPyClassObjectLayout<T>;
46+
/// The type of object layout to use for ancestors or descendants of this type.
47+
/// should implement `InternalPyClassObjectLayout<T>` in order to actually use it as a layout.
48+
type Layout<T: PyClassImpl>;
4949

5050
/// Returns the PyTypeObject instance for this type.
5151
fn type_object_raw(py: Python<'_>) -> *mut ffi::PyTypeObject;

tests/test_class_basics.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -466,10 +466,9 @@ fn no_dunder_dict_support_setattr() {
466466
.setattr("a", 1)
467467
.unwrap_err()
468468
.to_string();
469-
assert_eq!(
470-
&err,
469+
assert!(err.contains(
471470
"AttributeError: 'builtins.NoDunderDictSupport' object has no attribute 'a'"
472-
);
471+
));
473472
});
474473
}
475474

tests/test_compile_error.rs

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ fn test_compile_errors() {
2323
t.compile_fail("tests/ui/reject_generics.rs");
2424
t.compile_fail("tests/ui/deprecations.rs");
2525
t.compile_fail("tests/ui/invalid_closure.rs");
26+
// only possible to extend variable sized types after 3.12
27+
#[cfg(not(Py_3_12))]
28+
t.compile_fail("tests/ui/invalid_extend_variable_sized.rs");
2629
t.compile_fail("tests/ui/pyclass_send.rs");
2730
t.compile_fail("tests/ui/invalid_argument_attributes.rs");
2831
t.compile_fail("tests/ui/invalid_intopy_derive.rs");

tests/test_inheritance.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,10 @@ mod inheriting_type {
200200
slf: Bound<'_, Metaclass>,
201201
_args: Bound<'_, PyTuple>,
202202
_kwargs: Option<Bound<'_, PyDict>>,
203-
) -> PyResult<()> {
203+
) {
204204
let mut slf = slf.borrow_mut();
205205
assert_eq!(slf.counter, 999);
206206
slf.counter = 5;
207-
Ok(())
208207
}
209208

210209
fn __getitem__(&self, item: u64) -> u64 {
@@ -283,7 +282,7 @@ mod inheriting_type {
283282
}
284283

285284
#[test]
286-
#[should_panic = "Metaclasses must specify __init__"]
285+
#[should_panic(expected = "Metaclasses must specify __init__")]
287286
fn inherit_type_missing_init() {
288287
use pyo3::types::PyType;
289288

@@ -311,7 +310,7 @@ mod inheriting_type {
311310
}
312311

313312
#[test]
314-
#[should_panic = "Metaclasses must not specify __new__ (use __init__ instead)"]
313+
#[should_panic(expected = "Metaclasses must not specify __new__ (use __init__ instead)")]
315314
fn inherit_type_with_new() {
316315
use pyo3::types::PyType;
317316

@@ -332,8 +331,7 @@ mod inheriting_type {
332331
_slf: Bound<'_, MetaclassWithNew>,
333332
_args: Bound<'_, PyTuple>,
334333
_kwargs: Option<Bound<'_, PyDict>>,
335-
) -> PyResult<()> {
336-
Ok(())
334+
) {
337335
}
338336
}
339337

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use pyo3::prelude::*;
2+
use pyo3::types::{PyDict, PyTuple, PyType};
3+
4+
#[pyclass(extends=PyType)]
5+
#[derive(Default)]
6+
struct MyClass {}
7+
8+
#[pymethods]
9+
impl MyClass {
10+
#[pyo3(signature = (*_args, **_kwargs))]
11+
fn __init__(&mut self, _args: &Bound<'_, PyTuple>, _kwargs: Option<&Bound<'_, PyDict>>) {}
12+
}
13+
14+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0277]: the class layout is not valid
2+
--> tests/ui/invalid_extend_variable_sized.rs:4:1
3+
|
4+
4 | #[pyclass(extends=PyType)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required for `#[pyclass(extends=...)]`
6+
|
7+
= help: the trait `pyo3::pycell::impl_::InternalPyClassObjectLayout<MyClass>` is not implemented for `PyVariableClassObject<MyClass>`
8+
= note: the python version being built against influences which layouts are valid
9+
= help: the trait `pyo3::pycell::impl_::InternalPyClassObjectLayout<T>` is implemented for `PyStaticClassObject<T>`
10+
note: required by a bound in `PyClassImpl::Layout`
11+
--> src/impl_/pyclass.rs
12+
|
13+
| type Layout: InternalPyClassObjectLayout<Self>;
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `PyClassImpl::Layout`
15+
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)

0 commit comments

Comments
 (0)