From 844cb03c0330bd922d4d8b99db1529d2429ed30c Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 1 Jan 2026 19:36:35 +0900 Subject: [PATCH 1/3] fix sequence_repeat wrappers --- crates/vm/src/builtins/descriptor.rs | 6 +++--- crates/vm/src/types/slot.rs | 26 ++++++++++++++++++++------ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/crates/vm/src/builtins/descriptor.rs b/crates/vm/src/builtins/descriptor.rs index aa9da6e2d44..50baf77987d 100644 --- a/crates/vm/src/builtins/descriptor.rs +++ b/crates/vm/src/builtins/descriptor.rs @@ -5,7 +5,7 @@ use crate::{ class::PyClassImpl, common::hash::PyHash, convert::{ToPyObject, ToPyResult}, - function::{FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue}, + function::{ArgSize, FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue}, protocol::{PyNumberBinaryFunc, PyNumberTernaryFunc, PyNumberUnaryFunc}, types::{ Callable, Comparable, DelFunc, DescrGetFunc, DescrSetFunc, GenericMethod, GetDescriptor, @@ -593,8 +593,8 @@ impl SlotFunc { func(obj.sequence_unchecked(), &other, vm) } SlotFunc::SeqRepeat(func) => { - let (n,): (isize,) = args.bind(vm)?; - func(obj.sequence_unchecked(), n, vm) + let (n,): (ArgSize,) = args.bind(vm)?; + func(obj.sequence_unchecked(), n.into(), vm) } SlotFunc::SeqItem(func) => { let (index,): (isize,) = args.bind(vm)?; diff --git a/crates/vm/src/types/slot.rs b/crates/vm/src/types/slot.rs index 17b2b4e2f8a..9e09973c95e 100644 --- a/crates/vm/src/types/slot.rs +++ b/crates/vm/src/types/slot.rs @@ -438,6 +438,16 @@ fn sequence_contains_wrapper( contains_wrapper(seq.obj, needle, vm) } +#[inline(never)] +fn sequence_repeat_wrapper(seq: PySequence<'_>, n: isize, vm: &VirtualMachine) -> PyResult { + vm.call_special_method(seq.obj, identifier!(vm, __mul__), (n,)) +} + +#[inline(never)] +fn sequence_inplace_repeat_wrapper(seq: PySequence<'_>, n: isize, vm: &VirtualMachine) -> PyResult { + vm.call_special_method(seq.obj, identifier!(vm, __imul__), (n,)) +} + fn repr_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult> { let ret = vm.call_special_method(zelf, identifier!(vm, __repr__), ())?; ret.downcast::().map_err(|obj| { @@ -1294,12 +1304,16 @@ impl PyType { accessor.inherit_from_mro(self); } } - SlotAccessor::SqRepeat | SlotAccessor::SqInplaceRepeat => { - // Sequence repeat uses sq_repeat slot - no generic wrapper needed - // (handled by number protocol fallback) - if !ADD { - accessor.inherit_from_mro(self); - } + SlotAccessor::SqRepeat => { + update_sub_slot!(as_sequence, repeat, sequence_repeat_wrapper, SeqRepeat) + } + SlotAccessor::SqInplaceRepeat => { + update_sub_slot!( + as_sequence, + inplace_repeat, + sequence_inplace_repeat_wrapper, + SeqRepeat + ) } SlotAccessor::SqItem => { update_sub_slot!(as_sequence, item, sequence_getitem_wrapper, SeqItem) From 6df4f0fc4c12c4640d8bb4fd0904986000f80864 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 1 Jan 2026 15:09:26 +0900 Subject: [PATCH 2/3] slot for numeric ops --- crates/stdlib/src/array.rs | 5 ----- crates/vm/src/builtins/bytearray.rs | 15 ++------------- crates/vm/src/builtins/bytes.rs | 13 ++----------- crates/vm/src/builtins/dict.rs | 11 ----------- crates/vm/src/builtins/genericalias.rs | 2 -- crates/vm/src/builtins/list.rs | 5 ----- crates/vm/src/builtins/mappingproxy.rs | 3 --- crates/vm/src/builtins/str.rs | 13 ++----------- crates/vm/src/builtins/tuple.rs | 3 --- crates/vm/src/builtins/type.rs | 2 -- crates/vm/src/builtins/union.rs | 2 -- crates/vm/src/stdlib/collections.rs | 4 ---- crates/vm/src/stdlib/ctypes/pointer.rs | 1 - crates/vm/src/stdlib/ctypes/simple.rs | 1 - crates/vm/src/stdlib/ctypes/structure.rs | 1 - 15 files changed, 6 insertions(+), 75 deletions(-) diff --git a/crates/stdlib/src/array.rs b/crates/stdlib/src/array.rs index b7a6fbd8b4f..c80908ea1b4 100644 --- a/crates/stdlib/src/array.rs +++ b/crates/stdlib/src/array.rs @@ -1060,7 +1060,6 @@ mod array { self.delitem_inner(&needle, vm) } - #[pymethod] fn __add__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult> { if let Some(other) = other.downcast_ref::() { self.read() @@ -1074,7 +1073,6 @@ mod array { } } - #[pymethod] fn __iadd__( zelf: PyRef, other: PyObjectRef, @@ -1093,15 +1091,12 @@ mod array { Ok(zelf) } - #[pymethod(name = "__rmul__")] - #[pymethod] fn __mul__(&self, value: isize, vm: &VirtualMachine) -> PyResult> { self.read() .mul(value, vm) .map(|x| Self::from(x).into_ref(&vm.ctx)) } - #[pymethod] fn __imul__(zelf: PyRef, value: isize, vm: &VirtualMachine) -> PyResult> { zelf.try_resizable(vm)?.imul(value, vm)?; Ok(zelf) diff --git a/crates/vm/src/builtins/bytearray.rs b/crates/vm/src/builtins/bytearray.rs index dc5ee100acf..d3f1b0bd4e8 100644 --- a/crates/vm/src/builtins/bytearray.rs +++ b/crates/vm/src/builtins/bytearray.rs @@ -215,7 +215,6 @@ impl PyByteArray { size_of::() + self.borrow_buf().len() * size_of::() } - #[pymethod] fn __add__(&self, other: ArgBytesLike) -> Self { self.inner().add(&other.borrow_buf()).into() } @@ -229,7 +228,6 @@ impl PyByteArray { self.inner().contains(needle, vm) } - #[pymethod] fn __iadd__( zelf: PyRef, other: ArgBytesLike, @@ -524,29 +522,20 @@ impl PyByteArray { self.inner().title().into() } - #[pymethod(name = "__rmul__")] - #[pymethod] fn __mul__(&self, value: ArgSize, vm: &VirtualMachine) -> PyResult { self.repeat(value.into(), vm) } - #[pymethod] fn __imul__(zelf: PyRef, value: ArgSize, vm: &VirtualMachine) -> PyResult> { Self::irepeat(&zelf, value.into(), vm)?; Ok(zelf) } - #[pymethod(name = "__mod__")] - fn mod_(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn __mod__(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult { let formatted = self.inner().cformat(values, vm)?; Ok(formatted.into()) } - #[pymethod] - fn __rmod__(&self, _values: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - vm.ctx.not_implemented() - } - #[pymethod] fn reverse(&self) { self.borrow_buf_mut().reverse(); @@ -822,7 +811,7 @@ impl AsNumber for PyByteArray { static AS_NUMBER: PyNumberMethods = PyNumberMethods { remainder: Some(|a, b, vm| { if let Some(a) = a.downcast_ref::() { - a.mod_(b.to_owned(), vm).to_pyresult(vm) + a.__mod__(b.to_owned(), vm).to_pyresult(vm) } else { Ok(vm.ctx.not_implemented()) } diff --git a/crates/vm/src/builtins/bytes.rs b/crates/vm/src/builtins/bytes.rs index 7e5b98b1ece..f5b11166b8e 100644 --- a/crates/vm/src/builtins/bytes.rs +++ b/crates/vm/src/builtins/bytes.rs @@ -224,7 +224,6 @@ impl PyBytes { size_of::() + self.len() * size_of::() } - #[pymethod] fn __add__(&self, other: ArgBytesLike) -> Vec { self.inner.add(&other.borrow_buf()) } @@ -512,23 +511,15 @@ impl PyBytes { self.inner.title().into() } - #[pymethod(name = "__rmul__")] - #[pymethod] fn __mul__(zelf: PyRef, value: ArgIndex, vm: &VirtualMachine) -> PyResult> { zelf.repeat(value.try_to_primitive(vm)?, vm) } - #[pymethod(name = "__mod__")] - fn mod_(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn __mod__(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult { let formatted = self.inner.cformat(values, vm)?; Ok(formatted.into()) } - #[pymethod] - fn __rmod__(&self, _values: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - vm.ctx.not_implemented() - } - #[pymethod] fn __getnewargs__(&self, vm: &VirtualMachine) -> PyTupleRef { let param: Vec = self.elements().map(|x| x.to_pyobject(vm)).collect(); @@ -677,7 +668,7 @@ impl AsNumber for PyBytes { static AS_NUMBER: PyNumberMethods = PyNumberMethods { remainder: Some(|a, b, vm| { if let Some(a) = a.downcast_ref::() { - a.mod_(b.to_owned(), vm).to_pyresult(vm) + a.__mod__(b.to_owned(), vm).to_pyresult(vm) } else { Ok(vm.ctx.not_implemented()) } diff --git a/crates/vm/src/builtins/dict.rs b/crates/vm/src/builtins/dict.rs index 693505b1f82..41f6779c212 100644 --- a/crates/vm/src/builtins/dict.rs +++ b/crates/vm/src/builtins/dict.rs @@ -293,7 +293,6 @@ impl PyDict { Ok(()) } - #[pymethod] fn __or__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { let other_dict: Result = other.downcast(); if let Ok(other) = other_dict { @@ -403,13 +402,11 @@ impl PyRef { PyDictReverseKeyIterator::new(self) } - #[pymethod] fn __ior__(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { self.merge_object(other, vm)?; Ok(self) } - #[pymethod] fn __ror__(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { let other_dict: Result = other.downcast(); if let Ok(other) = other_dict { @@ -1046,38 +1043,30 @@ trait ViewSetOps: DictView { PySetInner::from_iter(iter, vm) } - #[pymethod(name = "__rxor__")] - #[pymethod] fn __xor__(zelf: PyRef, other: ArgIterable, vm: &VirtualMachine) -> PyResult { let zelf = Self::to_set(zelf, vm)?; let inner = zelf.symmetric_difference(other, vm)?; Ok(PySet { inner }) } - #[pymethod(name = "__rand__")] - #[pymethod] fn __and__(zelf: PyRef, other: ArgIterable, vm: &VirtualMachine) -> PyResult { let zelf = Self::to_set(zelf, vm)?; let inner = zelf.intersection(other, vm)?; Ok(PySet { inner }) } - #[pymethod(name = "__ror__")] - #[pymethod] fn __or__(zelf: PyRef, other: ArgIterable, vm: &VirtualMachine) -> PyResult { let zelf = Self::to_set(zelf, vm)?; let inner = zelf.union(other, vm)?; Ok(PySet { inner }) } - #[pymethod] fn __sub__(zelf: PyRef, other: ArgIterable, vm: &VirtualMachine) -> PyResult { let zelf = Self::to_set(zelf, vm)?; let inner = zelf.difference(other, vm)?; Ok(PySet { inner }) } - #[pymethod] fn __rsub__(zelf: PyRef, other: ArgIterable, vm: &VirtualMachine) -> PyResult { let left = PySetInner::from_iter(other.iter(vm)?, vm)?; let right = ArgIterable::try_from_object(vm, Self::iter(zelf, vm)?)?; diff --git a/crates/vm/src/builtins/genericalias.rs b/crates/vm/src/builtins/genericalias.rs index 5596aca9da2..99c1eacc3ec 100644 --- a/crates/vm/src/builtins/genericalias.rs +++ b/crates/vm/src/builtins/genericalias.rs @@ -236,12 +236,10 @@ impl PyGenericAlias { Err(vm.new_type_error("issubclass() argument 2 cannot be a parameterized generic")) } - #[pymethod] fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { type_::or_(other, zelf, vm) } - #[pymethod] fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { type_::or_(zelf, other, vm) } diff --git a/crates/vm/src/builtins/list.rs b/crates/vm/src/builtins/list.rs index 52df0756498..514b38b6c28 100644 --- a/crates/vm/src/builtins/list.rs +++ b/crates/vm/src/builtins/list.rs @@ -145,7 +145,6 @@ impl PyList { Ok(Self::from(elements).into_ref(&vm.ctx)) } - #[pymethod] fn __add__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult> { self.concat(&other, vm) } @@ -160,7 +159,6 @@ impl PyList { Ok(zelf.to_owned().into()) } - #[pymethod] fn __iadd__( zelf: PyRef, other: PyObjectRef, @@ -240,13 +238,10 @@ impl PyList { self._setitem(&needle, value, vm) } - #[pymethod] - #[pymethod(name = "__rmul__")] fn __mul__(&self, n: ArgSize, vm: &VirtualMachine) -> PyResult> { self.repeat(n.into(), vm) } - #[pymethod] fn __imul__(zelf: PyRef, n: ArgSize, vm: &VirtualMachine) -> PyResult> { Self::irepeat(zelf, n.into(), vm) } diff --git a/crates/vm/src/builtins/mappingproxy.rs b/crates/vm/src/builtins/mappingproxy.rs index 34a598b03e0..0cd335c3283 100644 --- a/crates/vm/src/builtins/mappingproxy.rs +++ b/crates/vm/src/builtins/mappingproxy.rs @@ -187,7 +187,6 @@ impl PyMappingProxy { ) } - #[pymethod] fn __ior__(&self, _args: PyObjectRef, vm: &VirtualMachine) -> PyResult { Err(vm.new_type_error(format!( r#""'|=' is not supported by {}; use '|' instead""#, @@ -195,8 +194,6 @@ impl PyMappingProxy { ))) } - #[pymethod(name = "__ror__")] - #[pymethod] fn __or__(&self, args: PyObjectRef, vm: &VirtualMachine) -> PyResult { vm._or(self.copy(vm)?.as_ref(), args.as_ref()) } diff --git a/crates/vm/src/builtins/str.rs b/crates/vm/src/builtins/str.rs index 0357e81b365..e101ef2a52b 100644 --- a/crates/vm/src/builtins/str.rs +++ b/crates/vm/src/builtins/str.rs @@ -541,7 +541,6 @@ impl Py { ) )] impl PyStr { - #[pymethod] fn __add__(zelf: PyRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if let Some(other) = other.downcast_ref::() { let bytes = zelf.as_wtf8().py_add(other.as_wtf8()); @@ -636,8 +635,6 @@ impl PyStr { core::mem::size_of::() + self.byte_len() * core::mem::size_of::() } - #[pymethod(name = "__rmul__")] - #[pymethod] fn __mul__(zelf: PyRef, value: ArgSize, vm: &VirtualMachine) -> PyResult> { Self::repeat(zelf, value.into(), vm) } @@ -947,16 +944,10 @@ impl PyStr { && self.char_all(|c| GeneralCategory::of(c) == GeneralCategory::DecimalNumber) } - #[pymethod(name = "__mod__")] - fn modulo(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn __mod__(&self, values: PyObjectRef, vm: &VirtualMachine) -> PyResult { cformat_string(vm, self.as_wtf8(), values) } - #[pymethod] - fn __rmod__(&self, _values: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - vm.ctx.not_implemented() - } - #[pymethod] fn format(&self, args: FuncArgs, vm: &VirtualMachine) -> PyResult { let format_str = @@ -1564,7 +1555,7 @@ impl AsNumber for PyStr { static AS_NUMBER: PyNumberMethods = PyNumberMethods { remainder: Some(|a, b, vm| { if let Some(a) = a.downcast_ref::() { - a.modulo(b.to_owned(), vm).to_pyresult(vm) + a.__mod__(b.to_owned(), vm).to_pyresult(vm) } else { Ok(vm.ctx.not_implemented()) } diff --git a/crates/vm/src/builtins/tuple.rs b/crates/vm/src/builtins/tuple.rs index 910b0c8a204..f3da8b26163 100644 --- a/crates/vm/src/builtins/tuple.rs +++ b/crates/vm/src/builtins/tuple.rs @@ -263,7 +263,6 @@ impl PyTuple> { with(AsMapping, AsNumber, AsSequence, Hashable, Comparable, Iterable, Constructor, Representable) )] impl PyTuple { - #[pymethod] fn __add__( zelf: PyRef, other: PyObjectRef, @@ -302,8 +301,6 @@ impl PyTuple { self.elements.len() } - #[pymethod(name = "__rmul__")] - #[pymethod] fn __mul__(zelf: PyRef, value: ArgSize, vm: &VirtualMachine) -> PyResult> { Self::repeat(zelf, value.into(), vm) } diff --git a/crates/vm/src/builtins/type.rs b/crates/vm/src/builtins/type.rs index 178131c96d6..e2e69c7bffc 100644 --- a/crates/vm/src/builtins/type.rs +++ b/crates/vm/src/builtins/type.rs @@ -951,12 +951,10 @@ impl PyType { ) } - #[pymethod] pub fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { or_(other, zelf, vm) } - #[pymethod] pub fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { or_(zelf, other, vm) } diff --git a/crates/vm/src/builtins/union.rs b/crates/vm/src/builtins/union.rs index 0342442b83d..b5e12dcb3c8 100644 --- a/crates/vm/src/builtins/union.rs +++ b/crates/vm/src/builtins/union.rs @@ -136,8 +136,6 @@ impl PyUnion { } } - #[pymethod(name = "__ror__")] - #[pymethod] fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { type_::or_(zelf, other, vm) } diff --git a/crates/vm/src/stdlib/collections.rs b/crates/vm/src/stdlib/collections.rs index 9b7a78f7237..4688121c9b5 100644 --- a/crates/vm/src/stdlib/collections.rs +++ b/crates/vm/src/stdlib/collections.rs @@ -332,8 +332,6 @@ mod _collections { Ok(deque) } - #[pymethod] - #[pymethod(name = "__rmul__")] fn __mul__(&self, n: isize, vm: &VirtualMachine) -> PyResult { let deque = self._mul(n, vm)?; Ok(Self { @@ -343,7 +341,6 @@ mod _collections { }) } - #[pymethod] fn __imul__(zelf: PyRef, n: isize, vm: &VirtualMachine) -> PyResult> { let mul_deque = zelf._mul(n, vm)?; *zelf.borrow_deque_mut() = mul_deque; @@ -379,7 +376,6 @@ mod _collections { } } - #[pymethod] fn __iadd__( zelf: PyRef, other: PyObjectRef, diff --git a/crates/vm/src/stdlib/ctypes/pointer.rs b/crates/vm/src/stdlib/ctypes/pointer.rs index aad21f8fe45..4f935945e88 100644 --- a/crates/vm/src/stdlib/ctypes/pointer.rs +++ b/crates/vm/src/stdlib/ctypes/pointer.rs @@ -137,7 +137,6 @@ impl PyCPointerType { ))) } - #[pymethod] fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult { use super::array::array_type_from_ctype; diff --git a/crates/vm/src/stdlib/ctypes/simple.rs b/crates/vm/src/stdlib/ctypes/simple.rs index 17d3aa17ad1..de13dab2202 100644 --- a/crates/vm/src/stdlib/ctypes/simple.rs +++ b/crates/vm/src/stdlib/ctypes/simple.rs @@ -488,7 +488,6 @@ impl PyCSimpleType { } } - #[pymethod] fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult { PyCSimple::repeat(cls, n, vm) } diff --git a/crates/vm/src/stdlib/ctypes/structure.rs b/crates/vm/src/stdlib/ctypes/structure.rs index 732f3c66801..cd36ce85560 100644 --- a/crates/vm/src/stdlib/ctypes/structure.rs +++ b/crates/vm/src/stdlib/ctypes/structure.rs @@ -393,7 +393,6 @@ impl PyCStructType { Ok(()) } - #[pymethod] fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult { use super::array::array_type_from_ctype; From 9dfc0291af6128ca0ef4f329bd647cbe077bc30a Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 1 Jan 2026 21:15:30 +0900 Subject: [PATCH 3/3] fix bpo-37619 --- crates/vm/src/builtins/descriptor.rs | 9 +++++++++ crates/vm/src/types/slot.rs | 28 ++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/crates/vm/src/builtins/descriptor.rs b/crates/vm/src/builtins/descriptor.rs index 50baf77987d..a9218c8d689 100644 --- a/crates/vm/src/builtins/descriptor.rs +++ b/crates/vm/src/builtins/descriptor.rs @@ -774,6 +774,15 @@ impl Callable for PyMethodWrapper { type Args = FuncArgs; fn call(zelf: &Py, args: FuncArgs, vm: &VirtualMachine) -> PyResult { + // bpo-37619: Check type compatibility before calling wrapped slot + if !zelf.obj.fast_isinstance(zelf.wrapper.typ) { + return Err(vm.new_type_error(format!( + "descriptor '{}' requires a '{}' object but received a '{}'", + zelf.wrapper.name.as_str(), + zelf.wrapper.typ.name(), + zelf.obj.class().name() + ))); + } zelf.wrapper.wrapped.call(zelf.obj.clone(), args, vm) } } diff --git a/crates/vm/src/types/slot.rs b/crates/vm/src/types/slot.rs index 9e09973c95e..2df440b5b9f 100644 --- a/crates/vm/src/types/slot.rs +++ b/crates/vm/src/types/slot.rs @@ -1362,28 +1362,44 @@ impl PyType { ) -> Option { use crate::builtins::descriptor::PyWrapper; + // Helper to check if a class is a subclass of another by checking MRO + let is_subclass_of = |subclass_mro: &[PyRef], superclass: &Py| -> bool { + subclass_mro.iter().any(|c| c.is(superclass)) + }; + // Helper to extract slot from an attribute if it's a wrapper descriptor - let try_extract = |attr: &PyObjectRef| -> Option { + // and the wrapper's type is compatible with the given class. + // bpo-37619: wrapper descriptor from wrong class should not be used directly. + let try_extract = |attr: &PyObjectRef, for_class_mro: &[PyRef]| -> Option { if attr.class().is(ctx.types.wrapper_descriptor_type) { - attr.downcast_ref::() - .and_then(|wrapper| extract(&wrapper.wrapped)) + attr.downcast_ref::().and_then(|wrapper| { + // Only extract slot if for_class is a subclass of wrapper.typ + if is_subclass_of(for_class_mro, wrapper.typ) { + extract(&wrapper.wrapped) + } else { + None + } + }) } else { None } }; + let mro = self.mro.read(); + // Look up in self's dict first if let Some(attr) = self.attributes.read().get(name).cloned() { - if let Some(func) = try_extract(&attr) { + if let Some(func) = try_extract(&attr, &mro) { return Some(func); } return None; } // Look up in MRO (mro[0] is self, so skip it) - for cls in self.mro.read()[1..].iter() { + for (i, cls) in mro[1..].iter().enumerate() { if let Some(attr) = cls.attributes.read().get(name).cloned() { - if let Some(func) = try_extract(&attr) { + // Use the slice starting from this class in MRO + if let Some(func) = try_extract(&attr, &mro[i + 1..]) { return Some(func); } return None;