From 1556da3b5e9807f704edfb95a73f51742150711f Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 29 Jun 2025 13:28:48 +0900 Subject: [PATCH 1/3] PyUnion::get_args --- vm/src/builtins/union.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs index 996d3f66543..be1a957ba29 100644 --- a/vm/src/builtins/union.rs +++ b/vm/src/builtins/union.rs @@ -40,6 +40,11 @@ impl PyUnion { Self { args, parameters } } + /// Direct access to args field, matching CPython's _Py_union_args + pub(crate) fn get_args(&self) -> &PyTupleRef { + &self.args + } + fn repr(&self, vm: &VirtualMachine) -> PyResult { fn repr_item(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { if obj.is(vm.ctx.types.none_type) { From c8e5e89c25a4a66719691840b9e5cdbac4707964 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 29 Jun 2025 11:33:31 +0900 Subject: [PATCH 2/3] issubclass --- vm/src/protocol/object.rs | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index 61973def4b4..07ff92e32b8 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -497,30 +497,31 @@ impl PyObject { /// via the __subclasscheck__ magic method. /// PyObject_IsSubclass/object_issubclass pub fn is_subclass(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult { + let derived = self; // PyType_CheckExact(cls) if cls.class().is(vm.ctx.types.type_type) { - if self.is(cls) { + if derived.is(cls) { return Ok(true); } - return self.recursive_issubclass(cls, vm); + return derived.recursive_issubclass(cls, vm); } // Check for Union type - CPython handles this before tuple - let cls_to_check = if cls.class().is(vm.ctx.types.union_type) { + let cls = if cls.class().is(vm.ctx.types.union_type) { // Get the __args__ attribute which contains the union members - if let Ok(args) = cls.get_attr(identifier!(vm, __args__), vm) { - args - } else { - cls.to_owned() - } + // Match CPython's _Py_union_args which directly accesses the args field + let union = cls + .downcast_ref::() + .expect("union is already checked"); + union.get_args().as_object() } else { - cls.to_owned() + cls }; - // Check if cls_to_check is a tuple - if let Ok(tuple) = cls_to_check.try_to_value::<&Py>(vm) { - for typ in tuple { - if vm.with_recursion("in __subclasscheck__", || self.is_subclass(typ, vm))? { + // Check if cls is a tuple + if let Some(tuple) = cls.downcast_ref::() { + for item in tuple { + if vm.with_recursion("in __subclasscheck__", || derived.is_subclass(item, vm))? { return Ok(true); } } @@ -528,14 +529,14 @@ impl PyObject { } // Check for __subclasscheck__ method - if let Some(meth) = vm.get_special_method(cls, identifier!(vm, __subclasscheck__))? { - let ret = vm.with_recursion("in __subclasscheck__", || { - meth.invoke((self.to_owned(),), vm) + if let Some(checker) = vm.get_special_method(cls, identifier!(vm, __subclasscheck__))? { + let res = vm.with_recursion("in __subclasscheck__", || { + checker.invoke((derived.to_owned(),), vm) })?; - return ret.try_to_bool(vm); + return res.try_to_bool(vm); } - self.recursive_issubclass(cls, vm) + derived.recursive_issubclass(cls, vm) } /// Real isinstance check without going through __instancecheck__ From f26054ab4e1180e40a7ec8ac65091d48f865ae3a Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 29 Jun 2025 11:33:40 +0900 Subject: [PATCH 3/3] isinstance --- vm/src/builtins/union.rs | 3 ++- vm/src/protocol/object.rs | 20 +++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs index be1a957ba29..b8e34a7268f 100644 --- a/vm/src/builtins/union.rs +++ b/vm/src/builtins/union.rs @@ -41,7 +41,8 @@ impl PyUnion { } /// Direct access to args field, matching CPython's _Py_union_args - pub(crate) fn get_args(&self) -> &PyTupleRef { + #[inline] + pub fn args(&self) -> &PyTupleRef { &self.args } diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index 07ff92e32b8..98046bab820 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -513,7 +513,7 @@ impl PyObject { let union = cls .downcast_ref::() .expect("union is already checked"); - union.get_args().as_object() + union.args().as_object() } else { cls }; @@ -602,16 +602,14 @@ impl PyObject { // Check for Union type (e.g., int | str) - CPython checks this before tuple if cls.class().is(vm.ctx.types.union_type) { - if let Ok(args) = cls.get_attr(identifier!(vm, __args__), vm) { - if let Ok(tuple) = args.try_to_ref::(vm) { - for typ in tuple { - if vm - .with_recursion("in __instancecheck__", || self.is_instance(typ, vm))? - { - return Ok(true); - } - } - return Ok(false); + // Match CPython's _Py_union_args which directly accesses the args field + let union = cls + .try_to_ref::(vm) + .expect("checked by is"); + let tuple = union.args(); + for typ in tuple.iter() { + if vm.with_recursion("in __instancecheck__", || self.is_instance(typ, vm))? { + return Ok(true); } } }