diff --git a/tests/snippets/dict.py b/tests/snippets/dict.py index e28fd9da4..c04165f16 100644 --- a/tests/snippets/dict.py +++ b/tests/snippets/dict.py @@ -66,6 +66,8 @@ assert type(x) == LengthDict assert x['word'] == 4 assert x.get('word') is None +assert 5 == eval("a + word", LengthDict()) + # An object that hashes to the same value always, and compares equal if any its values match. class Hashable(object): def __init__(self, *args): diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 5aa5ee226..210d12b49 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -20,7 +20,7 @@ use crate::obj::objtype::{self, PyClassRef}; use crate::frame::Scope; use crate::function::{Args, OptionalArg, PyFuncArgs}; use crate::pyobject::{ - DictProtocol, IdProtocol, PyIterable, PyObjectRef, PyResult, PyValue, TryFromObject, + IdProtocol, ItemProtocol, PyIterable, PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -804,6 +804,6 @@ pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResu "__call__", vec![name_arg, bases, namespace.into_object()], )?; - cells.set_item("__class__", class.clone(), vm); + cells.set_item("__class__", class.clone(), vm)?; Ok(class) } diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 2f525afdd..ca3e54d07 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -21,8 +21,8 @@ use crate::obj::objstr; use crate::obj::objtype; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - DictProtocol, IdProtocol, ItemProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, - TryFromObject, TypeProtocol, + IdProtocol, ItemProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, + TypeProtocol, }; use crate::vm::VirtualMachine; use itertools::Itertools; @@ -133,12 +133,12 @@ pub trait NameProtocol { impl NameProtocol for Scope { fn load_name(&self, vm: &VirtualMachine, name: &str) -> Option { for dict in self.locals.iter() { - if let Some(value) = dict.get_item(name, vm) { + if let Some(value) = dict.get_item_option(name, vm).unwrap() { return Some(value); } } - if let Some(value) = self.globals.get_item(name, vm) { + if let Some(value) = self.globals.get_item_option(name, vm).unwrap() { return Some(value); } @@ -147,7 +147,7 @@ impl NameProtocol for Scope { fn load_cell(&self, vm: &VirtualMachine, name: &str) -> Option { for dict in self.locals.iter().skip(1) { - if let Some(value) = dict.get_item(name, vm) { + if let Some(value) = dict.get_item_option(name, vm).unwrap() { return Some(value); } } @@ -155,11 +155,11 @@ impl NameProtocol for Scope { } fn store_name(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) { - self.get_locals().set_item(key, value, vm) + self.get_locals().set_item(key, value, vm).unwrap(); } fn delete_name(&self, vm: &VirtualMachine, key: &str) { - self.get_locals().del_item(key, vm) + self.get_locals().del_item(key, vm).unwrap(); } } @@ -394,12 +394,12 @@ impl Frame { obj.downcast().expect("Need a dictionary to build a map."); let dict_elements = dict.get_key_value_pairs(); for (key, value) in dict_elements.iter() { - map_obj.set_item(key.clone(), value.clone(), vm); + map_obj.set_item(key.clone(), value.clone(), vm).unwrap(); } } } else { for (key, value) in self.pop_multiple(2 * size).into_iter().tuples() { - map_obj.set_item(key, value, vm) + map_obj.set_item(key, value, vm).unwrap(); } } diff --git a/vm/src/import.rs b/vm/src/import.rs index 713393deb..14c0a7566 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; use crate::compile; use crate::frame::Scope; use crate::obj::{objsequence, objstr}; -use crate::pyobject::{DictProtocol, ItemProtocol, PyResult}; +use crate::pyobject::{ItemProtocol, PyResult}; use crate::util; use crate::vm::VirtualMachine; @@ -39,7 +39,7 @@ fn import_uncached_module(vm: &VirtualMachine, current_path: PathBuf, module: &s // trace!("Code object: {:?}", code_obj); let attrs = vm.ctx.new_dict(); - attrs.set_item("__name__", vm.new_str(module.to_string()), vm); + attrs.set_item("__name__", vm.new_str(module.to_string()), vm)?; vm.run_code_obj(code_obj, Scope::new(None, attrs.clone()))?; Ok(vm.ctx.new_module(module, attrs)) } diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 0e1a2aedd..02f8443b5 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -3,8 +3,7 @@ use std::fmt; use crate::function::{KwArgs, OptionalArg}; use crate::pyobject::{ - DictProtocol, IntoPyObject, ItemProtocol, PyAttributes, PyContext, PyObjectRef, PyRef, - PyResult, PyValue, + IntoPyObject, ItemProtocol, PyAttributes, PyContext, PyObjectRef, PyRef, PyResult, PyValue, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -174,8 +173,8 @@ impl PyDictRef { self.entries.borrow().get_items() } - fn setitem(self, key: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) { - self.set_item(key, value, vm) + fn setitem(self, key: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + self.entries.borrow_mut().insert(vm, &key, value) } fn getitem(self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult { @@ -223,21 +222,26 @@ impl PyDictRef { } } -impl DictProtocol for PyDictRef { - fn get_item(&self, key: T, vm: &VirtualMachine) -> Option { - let key = key.into_pyobject(vm).unwrap(); - self.entries.borrow().get(vm, &key).unwrap() +impl ItemProtocol for PyDictRef { + fn get_item(&self, key: T, vm: &VirtualMachine) -> PyResult { + vm.call_method(self.as_object(), "__getitem__", key.into_pyobject(vm)?) } - // Item set/get: - fn set_item(&self, key: T, value: PyObjectRef, vm: &VirtualMachine) { - let key = key.into_pyobject(vm).unwrap(); - self.entries.borrow_mut().insert(vm, &key, value).unwrap() + fn set_item( + &self, + key: T, + value: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult { + vm.call_method( + self.as_object(), + "__setitem__", + vec![key.into_pyobject(vm)?, value], + ) } - fn del_item(&self, key: T, vm: &VirtualMachine) { - let key = key.into_pyobject(vm).unwrap(); - self.entries.borrow_mut().delete(vm, &key).unwrap(); + fn del_item(&self, key: T, vm: &VirtualMachine) -> PyResult { + vm.call_method(self.as_object(), "__delitem__", key.into_pyobject(vm)?) } } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index c03ff31a1..a3bc41103 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -6,7 +6,7 @@ use crate::function::PyFuncArgs; use crate::obj::objproperty::PropertyBuilder; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - DictProtocol, IdProtocol, PyAttributes, PyContext, PyObject, PyObjectRef, PyResult, PyValue, + IdProtocol, ItemProtocol, PyAttributes, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -77,7 +77,7 @@ fn object_setattr( } if let Some(ref dict) = obj.clone().dict { - dict.set_item(attr_name, value, vm); + dict.set_item(attr_name, value, vm)?; Ok(()) } else { Err(vm.new_attribute_error(format!( @@ -98,7 +98,7 @@ fn object_delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine) } if let Some(ref dict) = obj.dict { - dict.del_item(attr_name, vm); + dict.del_item(attr_name, vm)?; Ok(()) } else { Err(vm.new_attribute_error(format!( @@ -208,7 +208,7 @@ fn object_getattribute(obj: PyObjectRef, name_str: PyStringRef, vm: &VirtualMach } } - if let Some(obj_attr) = object_getattr(&obj, &name, &vm) { + if let Some(obj_attr) = object_getattr(&obj, &name, &vm)? { Ok(obj_attr) } else if let Some(attr) = objtype::class_get_attr(&cls, &name) { vm.call_get_descriptor(attr, obj) @@ -219,11 +219,15 @@ fn object_getattribute(obj: PyObjectRef, name_str: PyStringRef, vm: &VirtualMach } } -fn object_getattr(obj: &PyObjectRef, attr_name: &str, vm: &VirtualMachine) -> Option { +fn object_getattr( + obj: &PyObjectRef, + attr_name: &str, + vm: &VirtualMachine, +) -> PyResult> { if let Some(ref dict) = obj.dict { - dict.get_item(attr_name, vm) + dict.get_item_option(attr_name, vm) } else { - None + Ok(None) } } diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 19c7c2ea8..94fe0fc7b 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -12,7 +12,7 @@ use crate::obj::objfunction::PyMethod; use crate::obj::objstr; use crate::obj::objtype::{PyClass, PyClassRef}; use crate::pyobject::{ - DictProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, + ItemProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -124,7 +124,7 @@ fn super_new( } else { let frame = vm.current_frame().expect("no current frame for super()"); if let Some(first_arg) = frame.code.arg_names.get(0) { - match vm.get_locals().get_item(first_arg, vm) { + match vm.get_locals().get_item_option(first_arg, vm)? { Some(obj) => obj.clone(), _ => { return Err(vm diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index bf5cf3c86..ee765f987 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -912,12 +912,6 @@ impl TypeProtocol for PyRef { } } -pub trait DictProtocol { - fn get_item(&self, key: T, vm: &VirtualMachine) -> Option; - fn set_item(&self, key: T, value: PyObjectRef, vm: &VirtualMachine); - fn del_item(&self, key: T, vm: &VirtualMachine); -} - pub trait ItemProtocol { fn get_item(&self, key: T, vm: &VirtualMachine) -> PyResult; fn set_item( @@ -927,6 +921,22 @@ pub trait ItemProtocol { vm: &VirtualMachine, ) -> PyResult; fn del_item(&self, key: T, vm: &VirtualMachine) -> PyResult; + fn get_item_option( + &self, + key: T, + vm: &VirtualMachine, + ) -> PyResult> { + match self.get_item(key, vm) { + Ok(value) => Ok(Some(value)), + Err(exc) => { + if objtype::isinstance(&exc, &vm.ctx.exceptions.key_error) { + Ok(None) + } else { + Err(exc) + } + } + } + } } impl ItemProtocol for PyObjectRef { diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index b7cb704b1..f4061bc2a 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -13,9 +13,7 @@ use crate::obj::{ objstr::{self, PyString}, objtype, }; -use crate::pyobject::{ - create_type, DictProtocol, IdProtocol, ItemProtocol, PyObjectRef, PyResult, TypeProtocol, -}; +use crate::pyobject::{create_type, IdProtocol, ItemProtocol, PyObjectRef, PyResult, TypeProtocol}; use crate::VirtualMachine; use num_traits::cast::ToPrimitive; @@ -178,7 +176,7 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { Some(PyString { ref value }) => value.clone(), _ => unimplemented!("map keys must be strings"), }; - dict.set_item(&key, value, self.vm); + dict.set_item(&key, value, self.vm).unwrap(); } Ok(dict.into_object()) } diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 772dfe198..2f8690448 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -3,7 +3,7 @@ use std::{env, mem}; use crate::frame::FrameRef; use crate::function::{OptionalArg, PyFuncArgs}; -use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::pyobject::{ItemProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; /* @@ -139,6 +139,6 @@ settrace() -- set the global debug tracing function "modules" => modules.clone(), }); - modules.set_item("sys", module.clone(), vm); - modules.set_item("builtins", builtins.clone(), vm); + modules.set_item("sys", module.clone(), vm).unwrap(); + modules.set_item("builtins", builtins.clone(), vm).unwrap(); } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 0bd255574..90daaa668 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -30,7 +30,7 @@ use crate::obj::objtuple::PyTuple; use crate::obj::objtype; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TryFromObject, TryIntoRef, + IdProtocol, ItemProtocol, PyContext, PyObjectRef, PyResult, PyValue, TryFromObject, TryIntoRef, TypeProtocol, }; use crate::stdlib; @@ -423,7 +423,7 @@ impl VirtualMachine { for i in 0..n { let arg_name = &code_object.arg_names[i]; let arg = &args.args[i]; - locals.set_item(arg_name, arg.clone(), self); + locals.set_item(arg_name, arg.clone(), self)?; } // Pack other positional arguments in to *args: @@ -436,7 +436,7 @@ impl VirtualMachine { } let vararg_value = self.ctx.new_tuple(last_args); - locals.set_item(vararg_name, vararg_value, self); + locals.set_item(vararg_name, vararg_value, self)?; } bytecode::Varargs::Unnamed => { // just ignore the rest of the args @@ -456,7 +456,7 @@ impl VirtualMachine { let kwargs = match code_object.varkeywords { bytecode::Varargs::Named(ref kwargs_name) => { let d = self.ctx.new_dict(); - locals.set_item(kwargs_name, d.as_object().clone(), self); + locals.set_item(kwargs_name, d.as_object().clone(), self)?; Some(d) } bytecode::Varargs::Unnamed => Some(self.ctx.new_dict()), @@ -474,9 +474,9 @@ impl VirtualMachine { ); } - locals.set_item(&name, value, self); + locals.set_item(&name, value, self)?; } else if let Some(d) = &kwargs { - d.set_item(&name, value, self); + d.set_item(&name, value, self)?; } else { return Err( self.new_type_error(format!("Got an unexpected keyword argument '{}'", name)) @@ -520,7 +520,7 @@ impl VirtualMachine { for (default_index, i) in (required_args..nexpected_args).enumerate() { let arg_name = &code_object.arg_names[i]; if !locals.contains_key(arg_name, self) { - locals.set_item(arg_name, available_defaults[default_index].clone(), self); + locals.set_item(arg_name, available_defaults[default_index].clone(), self)?; } } }; diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 717c9a973..25024734c 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -4,7 +4,7 @@ use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; use rustpython_vm::function::PyFuncArgs; use rustpython_vm::obj::{objbytes, objint, objsequence, objtype}; -use rustpython_vm::pyobject::{DictProtocol, PyObjectRef, PyResult, PyValue}; +use rustpython_vm::pyobject::{ItemProtocol, PyObjectRef, PyResult, PyValue}; use rustpython_vm::VirtualMachine; use crate::browser_module; @@ -192,7 +192,8 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef { for pair in object_entries(&Object::from(js_val)) { let (key, val) = pair.expect("iteration over object to not fail"); let py_val = js_to_py(vm, val); - dict.set_item(&String::from(js_sys::JsString::from(key)), py_val, vm); + dict.set_item(&String::from(js_sys::JsString::from(key)), py_val, vm) + .unwrap(); } dict.into_object() }