@@ -497,45 +497,46 @@ impl PyObject {
497
497
/// via the __subclasscheck__ magic method.
498
498
/// PyObject_IsSubclass/object_issubclass
499
499
pub fn is_subclass ( & self , cls : & PyObject , vm : & VirtualMachine ) -> PyResult < bool > {
500
+ let derived = self ;
500
501
// PyType_CheckExact(cls)
501
502
if cls. class ( ) . is ( vm. ctx . types . type_type ) {
502
- if self . is ( cls) {
503
+ if derived . is ( cls) {
503
504
return Ok ( true ) ;
504
505
}
505
- return self . recursive_issubclass ( cls, vm) ;
506
+ return derived . recursive_issubclass ( cls, vm) ;
506
507
}
507
508
508
509
// Check for Union type - CPython handles this before tuple
509
- let cls_to_check = if cls. class ( ) . is ( vm. ctx . types . union_type ) {
510
+ let cls = if cls. class ( ) . is ( vm. ctx . types . union_type ) {
510
511
// Get the __args__ attribute which contains the union members
511
- if let Ok ( args ) = cls . get_attr ( identifier ! ( vm , __args__ ) , vm ) {
512
- args
513
- } else {
514
- cls . to_owned ( )
515
- }
512
+ // Match CPython's _Py_union_args which directly accesses the args field
513
+ let union = cls
514
+ . downcast_ref :: < crate :: builtins :: PyUnion > ( )
515
+ . expect ( "union is already checked" ) ;
516
+ union . args ( ) . as_object ( )
516
517
} else {
517
- cls. to_owned ( )
518
+ cls
518
519
} ;
519
520
520
- // Check if cls_to_check is a tuple
521
- if let Ok ( tuple) = cls_to_check . try_to_value :: < & Py < PyTuple > > ( vm ) {
522
- for typ in tuple {
523
- if vm. with_recursion ( "in __subclasscheck__" , || self . is_subclass ( typ , vm) ) ? {
521
+ // Check if cls is a tuple
522
+ if let Some ( tuple) = cls . downcast_ref :: < PyTuple > ( ) {
523
+ for item in tuple {
524
+ if vm. with_recursion ( "in __subclasscheck__" , || derived . is_subclass ( item , vm) ) ? {
524
525
return Ok ( true ) ;
525
526
}
526
527
}
527
528
return Ok ( false ) ;
528
529
}
529
530
530
531
// Check for __subclasscheck__ method
531
- if let Some ( meth ) = vm. get_special_method ( cls, identifier ! ( vm, __subclasscheck__) ) ? {
532
- let ret = vm. with_recursion ( "in __subclasscheck__" , || {
533
- meth . invoke ( ( self . to_owned ( ) , ) , vm)
532
+ if let Some ( checker ) = vm. get_special_method ( cls, identifier ! ( vm, __subclasscheck__) ) ? {
533
+ let res = vm. with_recursion ( "in __subclasscheck__" , || {
534
+ checker . invoke ( ( derived . to_owned ( ) , ) , vm)
534
535
} ) ?;
535
- return ret . try_to_bool ( vm) ;
536
+ return res . try_to_bool ( vm) ;
536
537
}
537
538
538
- self . recursive_issubclass ( cls, vm)
539
+ derived . recursive_issubclass ( cls, vm)
539
540
}
540
541
541
542
/// Real isinstance check without going through __instancecheck__
@@ -601,16 +602,14 @@ impl PyObject {
601
602
602
603
// Check for Union type (e.g., int | str) - CPython checks this before tuple
603
604
if cls. class ( ) . is ( vm. ctx . types . union_type ) {
604
- if let Ok ( args) = cls. get_attr ( identifier ! ( vm, __args__) , vm) {
605
- if let Ok ( tuple) = args. try_to_ref :: < PyTuple > ( vm) {
606
- for typ in tuple {
607
- if vm
608
- . with_recursion ( "in __instancecheck__" , || self . is_instance ( typ, vm) ) ?
609
- {
610
- return Ok ( true ) ;
611
- }
612
- }
613
- return Ok ( false ) ;
605
+ // Match CPython's _Py_union_args which directly accesses the args field
606
+ let union = cls
607
+ . try_to_ref :: < crate :: builtins:: PyUnion > ( vm)
608
+ . expect ( "checked by is" ) ;
609
+ let tuple = union. args ( ) ;
610
+ for typ in tuple. iter ( ) {
611
+ if vm. with_recursion ( "in __instancecheck__" , || self . is_instance ( typ, vm) ) ? {
612
+ return Ok ( true ) ;
614
613
}
615
614
}
616
615
}
0 commit comments