mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Reject __abc_tpflags__ with both SEQUENCE and MAPPING bits (#7700)
CPython's _abc._abc_init validates that __abc_tpflags__ doesn't combine Py_TPFLAGS_SEQUENCE and Py_TPFLAGS_MAPPING (Modules/_abc.c::_abc_init). RustPython's check_abc_tpflags copied the bits onto slots.flags without that validation, so a class declaring both produced an inconsistent type silently. Validate the masked __abc_tpflags__ value against COLLECTION_FLAGS and return a String error (which the existing new_heap_inner caller turns into a TypeError) when both bits are present. Move the inheritance-skip check after the self-attrs branch so a child overriding __abc_tpflags__ is still validated even when the parent already set one of the bits.
This commit is contained in:
1 parent
1d42ee565f
commit
b8f7ae4265
2 files changed
+35
-14
No files matched your search
Vendored
-1
@@ -2028,7 +2028,6 @@ class TestCollectionABCs(ABCTestCase):
|
||||
self.assertEqual(len(mss), len(mss2))
|
||||
self.assertEqual(list(mss), list(mss2))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: TypeError not raised
|
||||
def test_illegal_patma_flags(self):
|
||||
with self.assertRaises(TypeError):
|
||||
class Both(Collection):
|
||||
|
||||
@@ -604,38 +604,60 @@ impl PyType {
|
||||
attrs: &PyAttributes,
|
||||
bases: &[PyRef<Self>],
|
||||
ctx: &Context,
|
||||
) {
|
||||
) -> Result<(), String> {
|
||||
const COLLECTION_FLAGS: PyTypeFlags = PyTypeFlags::from_bits_truncate(
|
||||
PyTypeFlags::SEQUENCE.bits() | PyTypeFlags::MAPPING.bits(),
|
||||
);
|
||||
|
||||
// Don't override if flags are already set
|
||||
if slots.flags.intersects(COLLECTION_FLAGS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// First check in our own attributes
|
||||
// Always validate this class's own __abc_tpflags__ even when slot
|
||||
// flags were already inherited, otherwise a child setting both
|
||||
// Py_TPFLAGS_SEQUENCE and Py_TPFLAGS_MAPPING would slip through.
|
||||
let abc_tpflags_name = ctx.intern_str("__abc_tpflags__");
|
||||
if let Some(abc_tpflags_obj) = attrs.get(abc_tpflags_name)
|
||||
&& let Some(int_obj) = abc_tpflags_obj.downcast_ref::<crate::builtins::int::PyInt>()
|
||||
{
|
||||
let flags_val = int_obj.as_bigint().to_i64().unwrap_or(0);
|
||||
let abc_flags = PyTypeFlags::from_bits_truncate(flags_val as u64);
|
||||
slots.flags |= abc_flags & COLLECTION_FLAGS;
|
||||
return;
|
||||
let masked = abc_flags & COLLECTION_FLAGS;
|
||||
if masked == COLLECTION_FLAGS {
|
||||
return Err(
|
||||
"__abc_tpflags__ cannot be both Py_TPFLAGS_SEQUENCE and Py_TPFLAGS_MAPPING"
|
||||
.to_owned(),
|
||||
);
|
||||
}
|
||||
// Don't override flags already inherited from a base class.
|
||||
if !slots.flags.intersects(COLLECTION_FLAGS) {
|
||||
slots.flags |= masked;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Then check in base classes
|
||||
// No __abc_tpflags__ on this class — inheritance already happened
|
||||
// in inherit_patma_flags, so nothing more to do if those bits are set.
|
||||
if slots.flags.intersects(COLLECTION_FLAGS) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Then check in base classes (legacy path for cases that bypass
|
||||
// inherit_patma_flags).
|
||||
for base in bases {
|
||||
if let Some(abc_tpflags_obj) = base.find_name_in_mro(abc_tpflags_name)
|
||||
&& let Some(int_obj) = abc_tpflags_obj.downcast_ref::<crate::builtins::int::PyInt>()
|
||||
{
|
||||
let flags_val = int_obj.as_bigint().to_i64().unwrap_or(0);
|
||||
let abc_flags = PyTypeFlags::from_bits_truncate(flags_val as u64);
|
||||
slots.flags |= abc_flags & COLLECTION_FLAGS;
|
||||
return;
|
||||
let masked = abc_flags & COLLECTION_FLAGS;
|
||||
if masked == COLLECTION_FLAGS {
|
||||
return Err(
|
||||
"__abc_tpflags__ cannot be both Py_TPFLAGS_SEQUENCE and Py_TPFLAGS_MAPPING"
|
||||
.to_owned(),
|
||||
);
|
||||
}
|
||||
slots.flags |= masked;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -671,7 +693,7 @@ impl PyType {
|
||||
Self::inherit_patma_flags(&mut slots, &bases);
|
||||
|
||||
// Check for __abc_tpflags__ from ABCMeta (for collections.abc.Sequence, Mapping, etc.)
|
||||
Self::check_abc_tpflags(&mut slots, &attrs, &bases, ctx);
|
||||
Self::check_abc_tpflags(&mut slots, &attrs, &bases, ctx)?;
|
||||
|
||||
if slots.basicsize == 0 {
|
||||
slots.basicsize = base.slots.basicsize;
|
||||
|
||||
Reference in new issue
Block a user