diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index 940512d0e..89a321008 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -2,11 +2,12 @@ //! https://docs.python.org/3/c-api/object.html use crate::{ - builtins::{pystr::IntoPyStrRef, PyBytes, PyInt, PyStrRef}, + builtins::{pystr::IntoPyStrRef, PyBytes, PyInt, PyStrRef, PyTupleRef}, bytesinner::ByteInnerNewOptions, common::{hash::PyHash, str::to_ascii}, function::OptionalArg, protocol::PyIter, + pyobject::IdProtocol, pyref_type_error, types::{Constructor, PyComparisonOp}, PyObjectRef, PyResult, TryFromObject, TypeProtocol, VirtualMachine, @@ -127,8 +128,35 @@ impl PyObjectRef { vm.issubclass(self, cls) } + /// Determines if `self` is an instance of `cls`, either directly, indirectly or virtually via + /// the __instancecheck__ magic method. pub fn is_instance(&self, cls: &PyObjectRef, vm: &VirtualMachine) -> PyResult { - vm.isinstance(self, cls) + // cpython first does an exact check on the type, although documentation doesn't state that + // https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408 + if self.class().is(cls) { + return Ok(true); + } + + if cls.class().is(&vm.ctx.types.type_type) { + return vm.abstract_isinstance(self, cls); + } + + if let Ok(tuple) = PyTupleRef::try_from_object(vm, cls.clone()) { + for typ in tuple.as_slice().iter() { + if vm.with_recursion("in __instancecheck__", || self.is_instance(typ, vm))? { + return Ok(true); + } + } + return Ok(false); + } + + if let Ok(meth) = vm.get_special_method(cls.clone(), "__instancecheck__")? { + let ret = + vm.with_recursion("in __instancecheck__", || meth.invoke((self.clone(),), vm))?; + return ret.try_to_bool(vm); + } + + vm.abstract_isinstance(self, cls) } pub fn hash(&self, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index bb60bd7e0..33854a83b 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -1013,7 +1013,7 @@ impl VirtualMachine { }) } - fn abstract_isinstance(&self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { + pub fn abstract_isinstance(&self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { if let Ok(typ) = PyTypeRef::try_from_object(self, cls.clone()) { if obj.class().issubclass(typ.clone()) { Ok(true) @@ -1046,37 +1046,6 @@ impl VirtualMachine { } } - /// Determines if `obj` is an instance of `cls`, either directly, indirectly or virtually via - /// the __instancecheck__ magic method. - pub fn isinstance(&self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult { - // cpython first does an exact check on the type, although documentation doesn't state that - // https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408 - if obj.class().is(cls) { - return Ok(true); - } - - if cls.class().is(&self.ctx.types.type_type) { - return self.abstract_isinstance(obj, cls); - } - - if let Ok(tuple) = PyTupleRef::try_from_object(self, cls.clone()) { - for typ in tuple.as_slice().iter() { - if self.with_recursion("in __instancecheck__", || self.isinstance(obj, typ))? { - return Ok(true); - } - } - return Ok(false); - } - - if let Ok(meth) = self.get_special_method(cls.clone(), "__instancecheck__")? { - let ret = - self.with_recursion("in __instancecheck__", || meth.invoke((obj.clone(),), self))?; - return ret.try_to_bool(self); - } - - self.abstract_isinstance(obj, cls) - } - fn abstract_issubclass(&self, subclass: PyObjectRef, cls: &PyObjectRef) -> PyResult { let mut derived = subclass; loop {