Skip to content

Commit 3865fdb

Browse files
authored
Revert class lookup (RustPython#7306)
* 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. * Collapse nested if in object_isinstance for clippy
1 parent baba1f9 commit 3865fdb

1 file changed

Lines changed: 15 additions & 31 deletions

File tree

crates/vm/src/protocol/object.rs

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
use crate::{
55
AsObject, Py, PyObject, PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine,
66
builtins::{
7-
PyBaseObject, PyBytes, PyDict, PyDictRef, PyGenericAlias, PyInt, PyList, PyStr, PyTuple,
8-
PyTupleRef, PyType, PyTypeRef, PyUtf8Str, pystr::AsPyStr,
7+
PyBytes, PyDict, PyDictRef, PyGenericAlias, PyInt, PyList, PyStr, PyTuple, PyTupleRef,
8+
PyType, PyTypeRef, PyUtf8Str, pystr::AsPyStr,
99
},
1010
common::{hash::PyHash, str::to_ascii},
1111
convert::{ToPyObject, ToPyResult},
@@ -580,18 +580,13 @@ impl PyObject {
580580
if let Ok(cls) = cls.try_to_ref::<PyType>(vm) {
581581
// PyType_Check(cls) - cls is a type object
582582
let mut retval = self.class().is_subtype(cls);
583-
if !retval {
584-
// __class__ is a data descriptor on object that always returns
585-
// obj.class() under standard __getattribute__. Only do the
586-
// expensive attribute lookup when getattro is overridden.
587-
if !self.has_standard_getattro()
588-
&& let Some(i_cls) =
589-
vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))?
590-
&& let Ok(i_cls_type) = PyTypeRef::try_from_object(vm, i_cls)
591-
&& !i_cls_type.is(self.class())
592-
{
593-
retval = i_cls_type.is_subtype(cls);
594-
}
583+
if !retval
584+
&& let Some(i_cls) =
585+
vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))?
586+
&& let Ok(i_cls_type) = PyTypeRef::try_from_object(vm, i_cls)
587+
&& !i_cls_type.is(self.class())
588+
{
589+
retval = i_cls_type.is_subtype(cls);
595590
}
596591
Ok(retval)
597592
} else {
@@ -603,15 +598,13 @@ impl PyObject {
603598
)
604599
})?;
605600

606-
let i_cls: PyObjectRef = if self.has_standard_getattro() {
607-
self.class().to_owned().into()
601+
if let Some(i_cls) =
602+
vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))?
603+
{
604+
i_cls.abstract_issubclass(cls, vm)
608605
} else {
609-
match vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class__))? {
610-
Some(cls) => cls,
611-
None => return Ok(false),
612-
}
613-
};
614-
i_cls.abstract_issubclass(cls, vm)
606+
Ok(false)
607+
}
615608
}
616609
}
617610

@@ -789,15 +782,6 @@ impl PyObject {
789782
Err(vm.new_type_error(format!("'{}' does not support item deletion", self.class())))
790783
}
791784

792-
/// Returns true if the object uses the standard `__getattribute__`
793-
/// (i.e. `object.__getattribute__`), meaning attribute access follows
794-
/// the normal descriptor protocol without custom interception.
795-
#[inline]
796-
fn has_standard_getattro(&self) -> bool {
797-
let getattro = self.class().slots.getattro.load().unwrap();
798-
getattro as usize == PyBaseObject::getattro as *const () as usize
799-
}
800-
801785
/// Equivalent to CPython's _PyObject_LookupSpecial
802786
/// Looks up a special method in the type's MRO without checking instance dict.
803787
/// Returns None if not found (masking AttributeError like CPython).

0 commit comments

Comments
 (0)