Skip to content

Commit acca386

Browse files
committed
Upgrade test_isinstance to 3.13.5
1 parent a417a1f commit acca386

File tree

2 files changed

+41
-52
lines changed

2 files changed

+41
-52
lines changed

Lib/test/test_isinstance.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
# testing of error conditions uncovered when using extension types.
44

55
import unittest
6-
import sys
76
import typing
87
from test import support
98

109

11-
10+
1211
class TestIsInstanceExceptions(unittest.TestCase):
1312
# Test to make sure that an AttributeError when accessing the instance's
1413
# class's bases is masked. This was actually a bug in Python 2.2 and
@@ -97,7 +96,7 @@ def getclass(self):
9796
class D: pass
9897
self.assertRaises(RuntimeError, isinstance, c, D)
9998

100-
99+
101100
# These tests are similar to above, but tickle certain code paths in
102101
# issubclass() instead of isinstance() -- really PyObject_IsSubclass()
103102
# vs. PyObject_IsInstance().
@@ -147,7 +146,7 @@ def getbases(self):
147146
self.assertRaises(TypeError, issubclass, B, C())
148147

149148

150-
149+
151150
# meta classes for creating abstract classes and instances
152151
class AbstractClass(object):
153152
def __init__(self, bases):
@@ -179,7 +178,7 @@ class Super:
179178

180179
class Child(Super):
181180
pass
182-
181+
183182
class TestIsInstanceIsSubclass(unittest.TestCase):
184183
# Tests to ensure that isinstance and issubclass work on abstract
185184
# classes and instances. Before the 2.2 release, TypeErrors were
@@ -225,7 +224,7 @@ def test_isinstance_with_or_union(self):
225224
with self.assertRaises(TypeError):
226225
isinstance(2, list[int] | int)
227226
with self.assertRaises(TypeError):
228-
isinstance(2, int | str | list[int] | float)
227+
isinstance(2, float | str | list[int] | int)
229228

230229

231230

@@ -311,7 +310,7 @@ class X:
311310
@property
312311
def __bases__(self):
313312
return self.__bases__
314-
with support.infinite_recursion():
313+
with support.infinite_recursion(25):
315314
self.assertRaises(RecursionError, issubclass, X(), int)
316315
self.assertRaises(RecursionError, issubclass, int, X())
317316
self.assertRaises(RecursionError, isinstance, 1, X())
@@ -345,18 +344,18 @@ class B:
345344
pass
346345
A.__getattr__ = B.__getattr__ = X.__getattr__
347346
return (A(), B())
348-
with support.infinite_recursion():
347+
with support.infinite_recursion(25):
349348
self.assertRaises(RecursionError, issubclass, X(), int)
350349

351350

352351
def blowstack(fxn, arg, compare_to):
353352
# Make sure that calling isinstance with a deeply nested tuple for its
354353
# argument will raise RecursionError eventually.
355354
tuple_arg = (compare_to,)
356-
for cnt in range(sys.getrecursionlimit()+5):
355+
for cnt in range(support.exceeds_recursion_limit()):
357356
tuple_arg = (tuple_arg,)
358357
fxn(arg, tuple_arg)
359358

360-
359+
361360
if __name__ == '__main__':
362361
unittest.main()

vm/src/protocol/object.rs

Lines changed: 32 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -428,30 +428,51 @@ impl PyObject {
428428
if let (Ok(obj), Ok(cls)) = (self.try_to_ref::<PyType>(vm), cls.try_to_ref::<PyType>(vm)) {
429429
Ok(obj.fast_issubclass(cls))
430430
} else {
431+
// Check if derived is a class
431432
self.check_cls(self, vm, || {
432433
format!("issubclass() arg 1 must be a class, not {}", self.class())
433-
})
434-
.and(self.check_cls(cls, vm, || {
435-
format!(
436-
"issubclass() arg 2 must be a class, a tuple of classes, or a union, not {}",
437-
cls.class()
438-
)
439-
}))
440-
.and(self.abstract_issubclass(cls, vm))
434+
})?;
435+
436+
// Check if cls is a class, tuple, or union
437+
if !cls.class().is(vm.ctx.types.union_type) {
438+
self.check_cls(cls, vm, || {
439+
format!(
440+
"issubclass() arg 2 must be a class, a tuple of classes, or a union, not {}",
441+
cls.class()
442+
)
443+
})?;
444+
}
445+
446+
self.abstract_issubclass(cls, vm)
441447
}
442448
}
443449

444450
/// Determines if `self` is a subclass of `cls`, either directly, indirectly or virtually
445451
/// via the __subclasscheck__ magic method.
452+
/// PyObject_IsSubclass/object_issubclass
446453
pub fn is_subclass(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
454+
// PyType_CheckExact(cls)
447455
if cls.class().is(vm.ctx.types.type_type) {
448456
if self.is(cls) {
449457
return Ok(true);
450458
}
451459
return self.recursive_issubclass(cls, vm);
452460
}
453461

454-
if let Ok(tuple) = cls.try_to_value::<&Py<PyTuple>>(vm) {
462+
// Check for Union type - CPython handles this before tuple
463+
let cls_to_check = if cls.class().is(vm.ctx.types.union_type) {
464+
// Get the __args__ attribute which contains the union members
465+
if let Ok(args) = cls.get_attr(identifier!(vm, __args__), vm) {
466+
args
467+
} else {
468+
cls.to_owned()
469+
}
470+
} else {
471+
cls.to_owned()
472+
};
473+
474+
// Check if cls_to_check is a tuple
475+
if let Ok(tuple) = cls_to_check.try_to_value::<&Py<PyTuple>>(vm) {
455476
for typ in tuple {
456477
if vm.with_recursion("in __subclasscheck__", || self.is_subclass(typ, vm))? {
457478
return Ok(true);
@@ -460,6 +481,7 @@ impl PyObject {
460481
return Ok(false);
461482
}
462483

484+
// Check for __subclasscheck__ method
463485
if let Some(meth) = vm.get_special_method(cls, identifier!(vm, __subclasscheck__))? {
464486
let ret = vm.with_recursion("in __subclasscheck__", || {
465487
meth.invoke((self.to_owned(),), vm)
@@ -514,38 +536,6 @@ impl PyObject {
514536
}
515537
}
516538

517-
fn abstract_isinstance(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
518-
let r = if let Ok(typ) = cls.try_to_ref::<PyType>(vm) {
519-
if self.class().fast_issubclass(typ) {
520-
true
521-
} else if let Ok(i_cls) =
522-
PyTypeRef::try_from_object(vm, self.get_attr(identifier!(vm, __class__), vm)?)
523-
{
524-
if i_cls.is(self.class()) {
525-
false
526-
} else {
527-
i_cls.fast_issubclass(typ)
528-
}
529-
} else {
530-
false
531-
}
532-
} else {
533-
self.check_cls(cls, vm, || {
534-
format!(
535-
"isinstance() arg 2 must be a type or tuple of types, not {}",
536-
cls.class()
537-
)
538-
})?;
539-
let i_cls: PyObjectRef = self.get_attr(identifier!(vm, __class__), vm)?;
540-
if vm.is_none(&i_cls) {
541-
false
542-
} else {
543-
i_cls.abstract_issubclass(cls, vm)?
544-
}
545-
};
546-
Ok(r)
547-
}
548-
549539
/// Determines if `self` is an instance of `cls`, either directly, indirectly or virtually via
550540
/// the __instancecheck__ magic method.
551541
// This is object_recursive_isinstance from CPython's Objects/abstract.c
@@ -563,7 +553,7 @@ impl PyObject {
563553
return self.real_is_instance(cls, vm);
564554
}
565555

566-
// Check for Union type (e.g., int | str)
556+
// Check for Union type (e.g., int | str) - CPython checks this before tuple
567557
if cls.class().is(vm.ctx.types.union_type) {
568558
if let Ok(args) = cls.get_attr(identifier!(vm, __args__), vm) {
569559
if let Ok(tuple) = args.try_to_ref::<PyTuple>(vm) {

0 commit comments

Comments
 (0)