From 863fd73034fed799fa1c067e95a71b999a69cabd Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Mon, 2 Mar 2026 01:29:31 +0900 Subject: [PATCH 1/2] Revert __class__ lookup skip in object_isinstance The optimization to skip __class__ lookup based on getattro check was incorrect: a class can override __class__ as a property while still using standard __getattribute__. Revert to always performing the lookup, matching CPython's object_isinstance behavior. --- crates/vm/src/protocol/object.rs | 35 +++++++++----------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/crates/vm/src/protocol/object.rs b/crates/vm/src/protocol/object.rs index 3fd61087837..fa6af120d73 100644 --- a/crates/vm/src/protocol/object.rs +++ b/crates/vm/src/protocol/object.rs @@ -4,8 +4,8 @@ use crate::{ AsObject, Py, PyObject, PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine, builtins::{ - PyBaseObject, PyBytes, PyDict, PyDictRef, PyGenericAlias, PyInt, PyList, PyStr, PyTuple, - PyTupleRef, PyType, PyTypeRef, PyUtf8Str, pystr::AsPyStr, + PyBytes, PyDict, PyDictRef, PyGenericAlias, PyInt, PyList, PyStr, PyTuple, PyTupleRef, + PyType, PyTypeRef, PyUtf8Str, pystr::AsPyStr, }, common::{hash::PyHash, str::to_ascii}, convert::{ToPyObject, ToPyResult}, @@ -581,12 +581,8 @@ impl PyObject { // PyType_Check(cls) - cls is a type object let mut retval = self.class().is_subtype(cls); if !retval { - // __class__ is a data descriptor on object that always returns - // obj.class() under standard __getattribute__. Only do the - // expensive attribute lookup when getattro is overridden. - if !self.has_standard_getattro() - && let Some(i_cls) = - vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))? + if let Some(i_cls) = + vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))? && let Ok(i_cls_type) = PyTypeRef::try_from_object(vm, i_cls) && !i_cls_type.is(self.class()) { @@ -603,15 +599,13 @@ impl PyObject { ) })?; - let i_cls: PyObjectRef = if self.has_standard_getattro() { - self.class().to_owned().into() + if let Some(i_cls) = + vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))? + { + i_cls.abstract_issubclass(cls, vm) } else { - match vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))? { - Some(cls) => cls, - None => return Ok(false), - } - }; - i_cls.abstract_issubclass(cls, vm) + Ok(false) + } } } @@ -789,15 +783,6 @@ impl PyObject { Err(vm.new_type_error(format!("'{}' does not support item deletion", self.class()))) } - /// Returns true if the object uses the standard `__getattribute__` - /// (i.e. `object.__getattribute__`), meaning attribute access follows - /// the normal descriptor protocol without custom interception. - #[inline] - fn has_standard_getattro(&self) -> bool { - let getattro = self.class().slots.getattro.load().unwrap(); - getattro as usize == PyBaseObject::getattro as *const () as usize - } - /// Equivalent to CPython's _PyObject_LookupSpecial /// Looks up a special method in the type's MRO without checking instance dict. /// Returns None if not found (masking AttributeError like CPython). From 864bf57b0c6e4891d203f82e8982c697f588e07a Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Mon, 2 Mar 2026 13:27:12 +0900 Subject: [PATCH 2/2] Collapse nested if in object_isinstance for clippy --- crates/vm/src/protocol/object.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/vm/src/protocol/object.rs b/crates/vm/src/protocol/object.rs index fa6af120d73..922acaa411b 100644 --- a/crates/vm/src/protocol/object.rs +++ b/crates/vm/src/protocol/object.rs @@ -580,14 +580,13 @@ impl PyObject { if let Ok(cls) = cls.try_to_ref::(vm) { // PyType_Check(cls) - cls is a type object let mut retval = self.class().is_subtype(cls); - if !retval { - if let Some(i_cls) = + if !retval + && let Some(i_cls) = vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))? - && let Ok(i_cls_type) = PyTypeRef::try_from_object(vm, i_cls) - && !i_cls_type.is(self.class()) - { - retval = i_cls_type.is_subtype(cls); - } + && let Ok(i_cls_type) = PyTypeRef::try_from_object(vm, i_cls) + && !i_cls_type.is(self.class()) + { + retval = i_cls_type.is_subtype(cls); } Ok(retval) } else {