single_or_tuple_any uses TryFromObject instead of PyValue

This commit is contained in:
Jeong YunWon
2020-04-13 03:14:39 +09:00
parent 1c93a36891
commit 0d2b817dd3
4 changed files with 46 additions and 46 deletions

View File

@@ -363,10 +363,14 @@ fn builtin_input(prompt: OptionalArg<PyStringRef>, vm: &VirtualMachine) -> PyRes
}
}
fn builtin_isinstance(obj: PyObjectRef, typ: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
pub fn builtin_isinstance(
obj: PyObjectRef,
typ: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<bool> {
single_or_tuple_any(
typ,
|cls: PyClassRef| vm.isinstance(&obj, &cls),
|cls: &PyClassRef| vm.isinstance(&obj, cls),
|o| {
format!(
"isinstance() arg 2 must be a type or tuple of types, not {}",
@@ -384,7 +388,7 @@ fn builtin_issubclass(
) -> PyResult<bool> {
single_or_tuple_any(
typ,
|cls: PyClassRef| vm.issubclass(&subclass, &cls),
|cls: &PyClassRef| vm.issubclass(&subclass, cls),
|o| {
format!(
"issubclass() arg 2 must be a class or tuple of classes, not {}",

View File

@@ -5,9 +5,10 @@ use std::sync::Mutex;
use indexmap::IndexMap;
use itertools::Itertools;
use crate::builtins::builtin_isinstance;
use crate::bytecode;
use crate::exceptions::{self, ExceptionCtor, PyBaseExceptionRef};
use crate::function::{single_or_tuple_any, PyFuncArgs};
use crate::function::PyFuncArgs;
use crate::obj::objasyncgenerator::PyAsyncGenWrappedValue;
use crate::obj::objbool;
use crate::obj::objcode::PyCodeRef;
@@ -1353,25 +1354,6 @@ impl ExecutingFrame<'_> {
!a.is(&b)
}
fn exc_match(
&self,
vm: &VirtualMachine,
exc: PyObjectRef,
exc_type: PyObjectRef,
) -> PyResult<bool> {
single_or_tuple_any(
exc_type,
|cls: PyClassRef| vm.isinstance(&exc, &cls),
|o| {
format!(
"isinstance() arg 2 must be a type or tuple of types, not {}",
o.class()
)
},
vm,
)
}
#[cfg_attr(feature = "flame-it", flame("Frame"))]
fn execute_compare(
&mut self,
@@ -1391,7 +1373,9 @@ impl ExecutingFrame<'_> {
bytecode::ComparisonOperator::IsNot => vm.new_bool(self._is_not(a, b)),
bytecode::ComparisonOperator::In => vm.new_bool(self._in(vm, a, b)?),
bytecode::ComparisonOperator::NotIn => vm.new_bool(self._not_in(vm, a, b)?),
bytecode::ComparisonOperator::ExceptionMatch => vm.new_bool(self.exc_match(vm, a, b)?),
bytecode::ComparisonOperator::ExceptionMatch => {
vm.new_bool(builtin_isinstance(a, b, vm)?)
}
};
self.push_value(value);

View File

@@ -7,7 +7,7 @@ use result_like::impl_option_like;
use smallbox::{smallbox, space::S1, SmallBox};
use crate::exceptions::PyBaseExceptionRef;
use crate::obj::objtuple::PyTuple;
use crate::obj::objtuple::PyTupleRef;
use crate::obj::objtype::{isinstance, PyClassRef};
use crate::pyobject::{
IntoPyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol,
@@ -564,43 +564,55 @@ into_py_native_func_tuple!((a, A), (b, B), (c, C), (d, D), (e, E));
/// test that any of the values contained within the tuples satisfies the predicate. Type parameter
/// T specifies the type that is expected, if the input value is not of that type or a tuple of
/// values of that type, then a TypeError is raised.
pub fn single_or_tuple_any<T: PyValue, F: Fn(PyRef<T>) -> PyResult<bool>>(
pub fn single_or_tuple_any<T, F, M>(
obj: PyObjectRef,
predicate: F,
message: fn(&PyObjectRef) -> String,
message: M,
vm: &VirtualMachine,
) -> PyResult<bool> {
) -> PyResult<bool>
where
T: TryFromObject,
F: Fn(&T) -> PyResult<bool>,
M: Fn(&PyObjectRef) -> String,
{
// TODO: figure out some way to have recursive calls without... this
use std::marker::PhantomData;
struct Checker<'vm, T: PyValue, F: Fn(PyRef<T>) -> PyResult<bool>> {
struct Checker<T, F, M>
where
F: Fn(&T) -> PyResult<bool>,
M: Fn(&PyObjectRef) -> String,
{
predicate: F,
message: fn(&PyObjectRef) -> String,
vm: &'vm VirtualMachine,
t: PhantomData<T>,
message: M,
t: std::marker::PhantomData<T>,
}
impl<T: PyValue, F: Fn(PyRef<T>) -> PyResult<bool>> Checker<'_, T, F> {
fn check(&self, obj: PyObjectRef) -> PyResult<bool> {
match_class!(match obj {
obj @ T => (self.predicate)(obj),
tuple @ PyTuple => {
impl<T, F, M> Checker<T, F, M>
where
T: TryFromObject,
F: Fn(&T) -> PyResult<bool>,
M: Fn(&PyObjectRef) -> String,
{
fn check(&self, obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
match T::try_from_object(vm, obj.clone()) {
Ok(single) => (self.predicate)(&single),
Err(_) => {
let tuple = PyTupleRef::try_from_object(vm, obj.clone())
.map_err(|_| vm.new_type_error((self.message)(&obj)))?;
for obj in tuple.as_slice().iter() {
if self.check(obj.clone())? {
if self.check(&obj, vm)? {
return Ok(true);
}
}
Ok(false)
}
obj => Err(self.vm.new_type_error((self.message)(&obj))),
})
}
}
}
let checker = Checker {
predicate,
message,
vm,
t: PhantomData,
t: std::marker::PhantomData,
};
checker.check(obj)
checker.check(&obj, vm)
}
#[cfg(test)]

View File

@@ -530,7 +530,7 @@ impl PyString {
let value = &self.value[range];
single_or_tuple_any(
suffix,
|s: PyStringRef| Ok(value.ends_with(&s.value)),
|s: &PyStringRef| Ok(value.ends_with(&s.value)),
|o| {
format!(
"endswith first arg must be str or a tuple of str, not {}",
@@ -557,7 +557,7 @@ impl PyString {
let value = &self.value[range];
single_or_tuple_any(
prefix,
|s: PyStringRef| Ok(value.starts_with(&s.value)),
|s: &PyStringRef| Ok(value.starts_with(&s.value)),
|o| {
format!(
"startswith first arg must be str or a tuple of str, not {}",