Skip to content

Update isintance #5868

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions vm/src/builtins/union.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ impl PyUnion {
Self { args, parameters }
}

/// Direct access to args field, matching CPython's _Py_union_args
#[inline]
pub fn args(&self) -> &PyTupleRef {
&self.args
}

fn repr(&self, vm: &VirtualMachine) -> PyResult<String> {
fn repr_item(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
if obj.is(vm.ctx.types.none_type) {
Expand Down
55 changes: 27 additions & 28 deletions vm/src/protocol/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,45 +497,46 @@ impl PyObject {
/// via the __subclasscheck__ magic method.
/// PyObject_IsSubclass/object_issubclass
pub fn is_subclass(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
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::<crate::builtins::PyUnion>()
.expect("union is already checked");
union.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<PyTuple>>(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::<PyTuple>() {
for item in tuple {
if vm.with_recursion("in __subclasscheck__", || derived.is_subclass(item, vm))? {
return Ok(true);
}
}
return Ok(false);
}
Comment on lines +522 to 529
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Inconsistent tuple handling between methods.

The is_subclass method uses downcast_ref::<PyTuple>() while is_instance (line 618) uses try_to_ref::<PyTuple>(vm). This inconsistency could lead to different error handling behavior.

Standardize tuple handling across both methods. Consider using the same approach:

-        if let Some(tuple) = cls.downcast_ref::<PyTuple>() {
+        if let Ok(tuple) = cls.try_to_ref::<PyTuple>(vm) {

Or alternatively, update is_instance to match this pattern for consistency.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if let Some(tuple) = cls.downcast_ref::<PyTuple>() {
for item in tuple {
if vm.with_recursion("in __subclasscheck__", || derived.is_subclass(item, vm))? {
return Ok(true);
}
}
return Ok(false);
}
if let Ok(tuple) = cls.try_to_ref::<PyTuple>(vm) {
for item in tuple {
if vm.with_recursion("in __subclasscheck__", || derived.is_subclass(item, vm))? {
return Ok(true);
}
}
return Ok(false);
}
🤖 Prompt for AI Agents
In vm/src/protocol/object.rs around lines 522 to 529, the method uses
downcast_ref::<PyTuple>() to handle tuples, while is_instance at line 618 uses
try_to_ref::<PyTuple>(vm), causing inconsistent tuple handling and error
behavior. To fix this, standardize tuple handling by either changing is_subclass
to use try_to_ref with the vm parameter like is_instance, or update is_instance
to use downcast_ref without vm, ensuring both methods handle tuples the same way
and maintain consistent error handling.


// 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__
Expand Down Expand Up @@ -601,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::<PyTuple>(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::<crate::builtins::PyUnion>(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);
}
}
}
Expand Down
Loading