Skip to content
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
2 changes: 1 addition & 1 deletion vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1783,7 +1783,7 @@ impl ExecutingFrame<'_> {
bytecode::ComparisonOperator::In => vm.ctx.new_bool(self._in(vm, a, b)?).into(),
bytecode::ComparisonOperator::NotIn => vm.ctx.new_bool(self._not_in(vm, a, b)?).into(),
bytecode::ComparisonOperator::ExceptionMatch => {
vm.ctx.new_bool(vm.isinstance(&a, &b)?).into()
vm.ctx.new_bool(a.is_instance(&b, vm)?).into()
}
};

Expand Down
32 changes: 30 additions & 2 deletions vm/src/protocol/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<bool> {
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<PyHash> {
Expand Down
2 changes: 1 addition & 1 deletion vm/src/stdlib/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ mod builtins {

#[pyfunction]
fn isinstance(obj: PyObjectRef, typ: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
vm.isinstance(&obj, &typ)
obj.is_instance(&typ, vm)
}

#[pyfunction]
Expand Down
33 changes: 1 addition & 32 deletions vm/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ impl VirtualMachine {
})
}

fn abstract_isinstance(&self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult<bool> {
pub fn abstract_isinstance(&self, obj: &PyObjectRef, cls: &PyObjectRef) -> PyResult<bool> {
if let Ok(typ) = PyTypeRef::try_from_object(self, cls.clone()) {
if obj.class().issubclass(typ.clone()) {
Ok(true)
Expand Down Expand Up @@ -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<bool> {
// 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<bool> {
let mut derived = subclass;
loop {
Expand Down