@@ -535,9 +535,14 @@ impl PyObject {
535535 derived. recursive_issubclass ( cls, vm)
536536 }
537537
538+ // _PyObject_RealIsInstance
539+ pub ( crate ) fn real_is_instance ( & self , cls : & PyObject , vm : & VirtualMachine ) -> PyResult < bool > {
540+ self . object_isinstance ( cls, vm)
541+ }
542+
538543 /// Real isinstance check without going through __instancecheck__
539544 /// This is equivalent to CPython's _PyObject_RealIsInstance/object_isinstance
540- pub fn real_is_instance ( & self , cls : & PyObject , vm : & VirtualMachine ) -> PyResult < bool > {
545+ fn object_isinstance ( & self , cls : & PyObject , vm : & VirtualMachine ) -> PyResult < bool > {
541546 if let Ok ( cls) = cls. try_to_ref :: < PyType > ( vm) {
542547 // PyType_Check(cls) - cls is a type object
543548 let mut retval = self . class ( ) . is_subtype ( cls) ;
@@ -576,8 +581,12 @@ impl PyObject {
576581
577582 /// Determines if `self` is an instance of `cls`, either directly, indirectly or virtually via
578583 /// the __instancecheck__ magic method.
579- // This is object_recursive_isinstance from CPython's Objects/abstract.c
580584 pub fn is_instance ( & self , cls : & PyObject , vm : & VirtualMachine ) -> PyResult < bool > {
585+ self . object_recursive_isinstance ( cls, vm)
586+ }
587+
588+ // This is object_recursive_isinstance from CPython's Objects/abstract.c
589+ fn object_recursive_isinstance ( & self , cls : & PyObject , vm : & VirtualMachine ) -> PyResult < bool > {
581590 // PyObject_TypeCheck(inst, (PyTypeObject *)cls)
582591 // This is an exact check of the type
583592 if self . class ( ) . is ( cls) {
@@ -586,29 +595,28 @@ impl PyObject {
586595
587596 // PyType_CheckExact(cls) optimization
588597 if cls. class ( ) . is ( vm. ctx . types . type_type ) {
589- // When cls is exactly a type (not a subclass), use real_is_instance
598+ // When cls is exactly a type (not a subclass), use object_isinstance
590599 // to avoid going through __instancecheck__ (matches CPython behavior)
591- return self . real_is_instance ( cls, vm) ;
600+ return self . object_isinstance ( cls, vm) ;
592601 }
593602
594603 // Check for Union type (e.g., int | str) - CPython checks this before tuple
595- if cls. class ( ) . is ( vm. ctx . types . union_type ) {
604+ let cls = if cls. class ( ) . is ( vm. ctx . types . union_type ) {
596605 // Match CPython's _Py_union_args which directly accesses the args field
597606 let union = cls
598607 . try_to_ref :: < crate :: builtins:: PyUnion > ( vm)
599608 . expect ( "checked by is" ) ;
600- let tuple = union. args ( ) ;
601- for typ in tuple. iter ( ) {
602- if vm. with_recursion ( "in __instancecheck__" , || self . is_instance ( typ, vm) ) ? {
603- return Ok ( true ) ;
604- }
605- }
606- }
609+ union. args ( ) . as_object ( )
610+ } else {
611+ cls
612+ } ;
607613
608614 // Check if cls is a tuple
609- if let Ok ( tuple) = cls. try_to_ref :: < PyTuple > ( vm) {
610- for typ in tuple {
611- if vm. with_recursion ( "in __instancecheck__" , || self . is_instance ( typ, vm) ) ? {
615+ if let Some ( tuple) = cls. downcast_ref :: < PyTuple > ( ) {
616+ for item in tuple {
617+ if vm. with_recursion ( "in __instancecheck__" , || {
618+ self . object_recursive_isinstance ( item, vm)
619+ } ) ? {
612620 return Ok ( true ) ;
613621 }
614622 }
@@ -624,7 +632,7 @@ impl PyObject {
624632 }
625633
626634 // Fall back to object_isinstance (without going through __instancecheck__ again)
627- self . real_is_instance ( cls, vm)
635+ self . object_isinstance ( cls, vm)
628636 }
629637
630638 pub fn hash ( & self , vm : & VirtualMachine ) -> PyResult < PyHash > {
0 commit comments