Skip to content

Commit 0ef22ab

Browse files
authored
Fix type.__subclasscheck__ (RustPython#5864)
1 parent 4ebecc0 commit 0ef22ab

File tree

3 files changed

+11
-5
lines changed

3 files changed

+11
-5
lines changed

Lib/test/test_typing.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3296,8 +3296,6 @@ class Foo(collections.abc.Mapping, Protocol):
32963296

32973297
self.assertNotIsInstance([], collections.abc.Mapping)
32983298

3299-
# TODO: RUSTPYTHON
3300-
@unittest.expectedFailure
33013299
def test_issubclass_and_isinstance_on_Protocol_itself(self):
33023300
class C:
33033301
def x(self): pass

vm/src/builtins/type.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ impl Py<PyType> {
442442
/// Determines if `subclass` is actually a subclass of `cls`, this doesn't call __subclasscheck__,
443443
/// so only use this if `cls` is known to have not overridden the base __subclasscheck__ magic
444444
/// method.
445-
pub fn fast_issubclass(&self, cls: &impl Borrow<crate::PyObject>) -> bool {
445+
pub fn fast_issubclass(&self, cls: &impl Borrow<PyObject>) -> bool {
446446
self.as_object().is(cls.borrow()) || self.mro.read().iter().any(|c| c.is(cls.borrow()))
447447
}
448448

@@ -1216,8 +1216,10 @@ impl Py<PyType> {
12161216
}
12171217

12181218
#[pymethod]
1219-
fn __subclasscheck__(&self, subclass: PyTypeRef) -> bool {
1220-
subclass.fast_issubclass(self)
1219+
fn __subclasscheck__(&self, subclass: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
1220+
// Use real_is_subclass to avoid going through __subclasscheck__ recursion
1221+
// This matches CPython's type___subclasscheck___impl which calls _PyObject_RealIsSubclass
1222+
subclass.real_is_subclass(self.as_object(), vm)
12211223
}
12221224

12231225
#[pyclassmethod]

vm/src/protocol/object.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,12 @@ impl PyObject {
447447
}
448448
}
449449

450+
/// Real issubclass check without going through __subclasscheck__
451+
/// This is equivalent to CPython's _PyObject_RealIsSubclass which just calls recursive_issubclass
452+
pub fn real_is_subclass(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
453+
self.recursive_issubclass(cls, vm)
454+
}
455+
450456
/// Determines if `self` is a subclass of `cls`, either directly, indirectly or virtually
451457
/// via the __subclasscheck__ magic method.
452458
/// PyObject_IsSubclass/object_issubclass

0 commit comments

Comments
 (0)