@@ -31,6 +31,7 @@ use crate::{
3131};
3232use indexmap::{IndexMap, map::Entry};
3333use itertools::Itertools;
34+ use num_traits::ToPrimitive;
3435use std::{borrow::Borrow, collections::HashSet, ops::Deref, pin::Pin, ptr::NonNull};
3536
3637#[pyclass(module = false, name = "type", traverse = "manual")]
@@ -231,6 +232,58 @@ impl PyType {
231232 linearise_mro(mros)
232233 }
233234
235+ /// Inherit SEQUENCE and MAPPING flags from base class (CPython: inherit_patma_flags)
236+ fn inherit_patma_flags(slots: &mut PyTypeSlots, base: &PyRef<Self>) {
237+ const COLLECTION_FLAGS: PyTypeFlags = PyTypeFlags::from_bits_truncate(
238+ PyTypeFlags::SEQUENCE.bits() | PyTypeFlags::MAPPING.bits(),
239+ );
240+ if !slots.flags.intersects(COLLECTION_FLAGS) {
241+ slots.flags |= base.slots.flags & COLLECTION_FLAGS;
242+ }
243+ }
244+
245+ /// Check for __abc_tpflags__ and set the appropriate flags
246+ /// This checks in attrs and all base classes for __abc_tpflags__
247+ fn check_abc_tpflags(
248+ slots: &mut PyTypeSlots,
249+ attrs: &PyAttributes,
250+ bases: &[PyRef<Self>],
251+ ctx: &Context,
252+ ) {
253+ const COLLECTION_FLAGS: PyTypeFlags = PyTypeFlags::from_bits_truncate(
254+ PyTypeFlags::SEQUENCE.bits() | PyTypeFlags::MAPPING.bits(),
255+ );
256+
257+ // Don't override if flags are already set
258+ if slots.flags.intersects(COLLECTION_FLAGS) {
259+ return;
260+ }
261+
262+ // First check in our own attributes
263+ let abc_tpflags_name = ctx.intern_str("__abc_tpflags__");
264+ if let Some(abc_tpflags_obj) = attrs.get(abc_tpflags_name) {
265+ if let Some(int_obj) = abc_tpflags_obj.downcast_ref::<crate::builtins::int::PyInt>() {
266+ let flags_val = int_obj.as_bigint().to_i64().unwrap_or(0);
267+ let abc_flags = PyTypeFlags::from_bits_truncate(flags_val as u64);
268+ slots.flags |= abc_flags & COLLECTION_FLAGS;
269+ return;
270+ }
271+ }
272+
273+ // Then check in base classes
274+ for base in bases {
275+ if let Some(abc_tpflags_obj) = base.find_name_in_mro(abc_tpflags_name) {
276+ if let Some(int_obj) = abc_tpflags_obj.downcast_ref::<crate::builtins::int::PyInt>()
277+ {
278+ let flags_val = int_obj.as_bigint().to_i64().unwrap_or(0);
279+ let abc_flags = PyTypeFlags::from_bits_truncate(flags_val as u64);
280+ slots.flags |= abc_flags & COLLECTION_FLAGS;
281+ return;
282+ }
283+ }
284+ }
285+ }
286+
234287 #[allow(clippy::too_many_arguments)]
235288 fn new_heap_inner(
236289 base: PyRef<Self>,
@@ -246,6 +299,13 @@ impl PyType {
246299 if base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
247300 slots.flags |= PyTypeFlags::HAS_DICT
248301 }
302+
303+ // Inherit SEQUENCE and MAPPING flags from base class
304+ Self::inherit_patma_flags(&mut slots, &base);
305+
306+ // Check for __abc_tpflags__ from ABCMeta (for collections.abc.Sequence, Mapping, etc.)
307+ Self::check_abc_tpflags(&mut slots, &attrs, &bases, ctx);
308+
249309 if slots.basicsize == 0 {
250310 slots.basicsize = base.slots.basicsize;
251311 }
@@ -297,6 +357,10 @@ impl PyType {
297357 if base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
298358 slots.flags |= PyTypeFlags::HAS_DICT
299359 }
360+
361+ // Inherit SEQUENCE and MAPPING flags from base class
362+ Self::inherit_patma_flags(&mut slots, &base);
363+
300364 if slots.basicsize == 0 {
301365 slots.basicsize = base.slots.basicsize;
302366 }
0 commit comments