refactor length now use sequence and mapping protocol

This commit is contained in:
Kangzhi Shi
2021-11-16 22:06:05 +02:00
committed by Jeong YunWon
parent dc31fd3901
commit 52b458d524
5 changed files with 25 additions and 43 deletions

View File

@@ -32,6 +32,17 @@ impl<'a> From<&'a PyObject> for PyMapping<'a> {
methods: OnceCell::new(),
}
}
pub fn length(&self, vm: &VirtualMachine) -> PyResult<usize> {
if let Some(f) = self.methods(vm).length {
f(self.0.clone(), vm)
} else {
Err(vm.new_type_error(format!(
"object of type '{}' has no len() or not a mapping",
self.0.class().name()
)))
}
}
}
impl AsRef<PyObject> for PyMapping<'_> {

View File

@@ -408,12 +408,12 @@ impl PyObject {
// int PyObject_TypeCheck(PyObject *o, PyTypeObject *type)
pub fn length(&self, vm: &VirtualMachine) -> PyResult<usize> {
vm.obj_len_opt(self).unwrap_or_else(|| {
Err(vm.new_type_error(format!(
"object of type '{}' has no len()",
self.class().name()
)))
})
let seq = PySequence::from(self);
if let Ok(len) = seq.length(vm) {
Ok(len)
} else {
PyMapping::try_from_object(vm, self.to_owned())?.length(vm)
}
}
pub fn get_item<K: DictKey + IntoPyObject + Clone>(

View File

@@ -297,7 +297,7 @@ impl PySequence<'_> {
if let Some(tuple) = self.obj.downcast_ref_if_exact::<PyTuple>(vm) {
Ok(tuple.to_owned())
} else if let Some(list) = self.obj.downcast_ref_if_exact::<PyList>(vm) {
Ok(vm.ctx.new_tuple(list.borrow_vec().to_vec()).into())
Ok(vm.ctx.new_tuple(list.borrow_vec().to_vec()))
} else {
let iter = self.obj.to_owned().get_iter(vm)?;
let iter = iter.iter(vm)?;

View File

@@ -166,7 +166,7 @@ macro_rules! then_some_closure {
pub use crate::builtins::object::{generic_getattr, generic_setattr};
fn slot_length(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
let ret = vm.call_special_method(obj.to_owned(), "__len__", ())?;
let ret = vm.call_special_method(obj, "__len__", ())?;
let len = ret.payload::<PyInt>().ok_or_else(|| {
vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",

View File

@@ -30,7 +30,7 @@ use crate::{
PyObjectWrap, PyRef, PyRefExact, PyResult, PyValue, TryFromObject, TypeProtocol,
};
use crossbeam_utils::atomic::AtomicCell;
use num_traits::{Signed, ToPrimitive};
use num_traits::ToPrimitive;
use std::borrow::Cow;
use std::cell::{Cell, Ref, RefCell};
use std::collections::{HashMap, HashSet};
@@ -1672,41 +1672,12 @@ impl VirtualMachine {
.invoke((), self)
}
pub fn obj_len_opt(&self, obj: &PyObject) -> Option<PyResult<usize>> {
self.get_special_method(obj.to_owned(), "__len__")
.map(Result::ok)
.transpose()
.map(|meth| {
let len = meth?.invoke((), self)?;
let len = len
.payload_if_subclass::<PyInt>(self)
.ok_or_else(|| {
self.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
len.class().name()
))
})?
.as_bigint();
if len.is_negative() {
return Err(self.new_value_error("__len__() should return >= 0".to_owned()));
}
let len = len.to_isize().ok_or_else(|| {
self.new_overflow_error(
"cannot fit 'int' into an index-sized integer".to_owned(),
)
})?;
Ok(len as usize)
})
}
pub fn length_hint_opt(&self, iter: PyObjectRef) -> PyResult<Option<usize>> {
if let Some(len) = self.obj_len_opt(&iter) {
match len {
Ok(len) => return Ok(Some(len)),
Err(e) => {
if !e.isinstance(&self.ctx.exceptions.type_error) {
return Err(e);
}
match iter.length(self) {
Ok(len) => return Ok(Some(len)),
Err(e) => {
if !e.isinstance(&self.ctx.exceptions.type_error) {
return Err(e);
}
}
}