@@ -428,30 +428,51 @@ impl PyObject {
428
428
if let ( Ok ( obj) , Ok ( cls) ) = ( self . try_to_ref :: < PyType > ( vm) , cls. try_to_ref :: < PyType > ( vm) ) {
429
429
Ok ( obj. fast_issubclass ( cls) )
430
430
} else {
431
+ // Check if derived is a class
431
432
self . check_cls ( self , vm, || {
432
433
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)
441
447
}
442
448
}
443
449
444
450
/// Determines if `self` is a subclass of `cls`, either directly, indirectly or virtually
445
451
/// via the __subclasscheck__ magic method.
452
+ /// PyObject_IsSubclass/object_issubclass
446
453
pub fn is_subclass ( & self , cls : & PyObject , vm : & VirtualMachine ) -> PyResult < bool > {
454
+ // PyType_CheckExact(cls)
447
455
if cls. class ( ) . is ( vm. ctx . types . type_type ) {
448
456
if self . is ( cls) {
449
457
return Ok ( true ) ;
450
458
}
451
459
return self . recursive_issubclass ( cls, vm) ;
452
460
}
453
461
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) {
455
476
for typ in tuple {
456
477
if vm. with_recursion ( "in __subclasscheck__" , || self . is_subclass ( typ, vm) ) ? {
457
478
return Ok ( true ) ;
@@ -460,6 +481,7 @@ impl PyObject {
460
481
return Ok ( false ) ;
461
482
}
462
483
484
+ // Check for __subclasscheck__ method
463
485
if let Some ( meth) = vm. get_special_method ( cls, identifier ! ( vm, __subclasscheck__) ) ? {
464
486
let ret = vm. with_recursion ( "in __subclasscheck__" , || {
465
487
meth. invoke ( ( self . to_owned ( ) , ) , vm)
@@ -514,38 +536,6 @@ impl PyObject {
514
536
}
515
537
}
516
538
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
-
549
539
/// Determines if `self` is an instance of `cls`, either directly, indirectly or virtually via
550
540
/// the __instancecheck__ magic method.
551
541
// This is object_recursive_isinstance from CPython's Objects/abstract.c
@@ -563,7 +553,7 @@ impl PyObject {
563
553
return self . real_is_instance ( cls, vm) ;
564
554
}
565
555
566
- // Check for Union type (e.g., int | str)
556
+ // Check for Union type (e.g., int | str) - CPython checks this before tuple
567
557
if cls. class ( ) . is ( vm. ctx . types . union_type ) {
568
558
if let Ok ( args) = cls. get_attr ( identifier ! ( vm, __args__) , vm) {
569
559
if let Ok ( tuple) = args. try_to_ref :: < PyTuple > ( vm) {
0 commit comments