Skip to content

Commit cac9da4

Browse files
authored
Merge pull request RustPython#748 from RustPython/attributes_and_dictionaries
Attributes and dictionaries
2 parents 20c09f0 + 55e0fb1 commit cac9da4

16 files changed

+444
-422
lines changed

vm/src/builtins.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use num_traits::{Signed, ToPrimitive};
1111
use crate::compile;
1212
use crate::import::import_module;
1313
use crate::obj::objbool;
14-
use crate::obj::objdict;
1514
use crate::obj::objint;
1615
use crate::obj::objiter;
1716
use crate::obj::objstr::{self, PyStringRef};
@@ -29,14 +28,7 @@ use crate::obj::objcode::PyCodeRef;
2928
use crate::stdlib::io::io_open;
3029

3130
fn get_locals(vm: &VirtualMachine) -> PyObjectRef {
32-
let d = vm.new_dict();
33-
// TODO: implement dict_iter_items?
34-
let locals = vm.get_locals();
35-
let key_value_pairs = objdict::get_key_value_pairs(&locals);
36-
for (key, value) in key_value_pairs {
37-
objdict::set_item(&d, vm, &key, &value);
38-
}
39-
d
31+
vm.get_locals()
4032
}
4133

4234
fn dir_locals(vm: &VirtualMachine) -> PyObjectRef {
@@ -823,9 +815,9 @@ pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResu
823815
let prepare = vm.get_attribute(metaclass.clone().into_object(), "__prepare__")?;
824816
let namespace = vm.invoke(prepare, vec![name_arg.clone(), bases.clone()])?;
825817

826-
let cells = vm.new_dict();
818+
let cells = vm.ctx.new_dict();
827819

828-
vm.invoke_with_locals(function, cells.clone(), namespace.clone())?;
820+
vm.invoke_with_locals(function, cells.clone().into_object(), namespace.clone())?;
829821
let class = vm.call_method(
830822
metaclass.as_object(),
831823
"__call__",

vm/src/frame.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ impl Scope {
119119
}
120120

121121
pub fn child_scope(&self, ctx: &PyContext) -> Scope {
122-
self.child_scope_with_locals(ctx.new_dict())
122+
self.child_scope_with_locals(ctx.new_dict().into_object())
123123
}
124124
}
125125

@@ -142,7 +142,7 @@ impl NameProtocol for Scope {
142142
return Some(value);
143143
}
144144

145-
vm.builtins.get_item(name)
145+
vm.get_attribute(vm.builtins.clone(), name).ok()
146146
}
147147

148148
fn load_cell(&self, _vm: &VirtualMachine, name: &str) -> Option<PyObjectRef> {
@@ -386,7 +386,7 @@ impl Frame {
386386
Ok(None)
387387
}
388388
bytecode::Instruction::BuildMap { size, unpack } => {
389-
let map_obj = vm.ctx.new_dict();
389+
let map_obj = vm.ctx.new_dict().into_object();
390390
for _x in 0..*size {
391391
let obj = self.pop_value();
392392
if *unpack {
@@ -572,7 +572,7 @@ impl Frame {
572572
let annotations = if flags.contains(bytecode::FunctionOpArg::HAS_ANNOTATIONS) {
573573
self.pop_value()
574574
} else {
575-
vm.new_dict()
575+
vm.ctx.new_dict().into_object()
576576
};
577577

578578
let defaults = if flags.contains(bytecode::FunctionOpArg::HAS_DEFAULTS) {
@@ -839,8 +839,10 @@ impl Frame {
839839
let module = vm.import(module)?;
840840

841841
// Grab all the names from the module and put them in the context
842-
for (k, v) in module.get_key_value_pairs().iter() {
843-
self.scope.store_name(&vm, &objstr::get_value(k), v.clone());
842+
if let Some(dict) = &module.dict {
843+
for (k, v) in dict.get_key_value_pairs().iter() {
844+
self.scope.store_name(&vm, &objstr::get_value(k), v.clone());
845+
}
844846
}
845847
Ok(None)
846848
}

vm/src/function.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,15 @@ where
247247
}
248248
}
249249

250+
impl<T> IntoIterator for KwArgs<T> {
251+
type Item = (String, T);
252+
type IntoIter = std::collections::hash_map::IntoIter<String, T>;
253+
254+
fn into_iter(self) -> Self::IntoIter {
255+
self.0.into_iter()
256+
}
257+
}
258+
250259
/// A list of positional argument values.
251260
///
252261
/// A built-in function with a `Args` parameter is analagous to a Python

vm/src/import.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ fn import_uncached_module(vm: &VirtualMachine, current_path: PathBuf, module: &s
4040

4141
let attrs = vm.ctx.new_dict();
4242
attrs.set_item(&vm.ctx, "__name__", vm.new_str(module.to_string()));
43-
vm.run_code_obj(code_obj, Scope::new(None, attrs.clone()))?;
43+
vm.run_code_obj(code_obj, Scope::new(None, attrs.clone().into_object()))?;
4444
Ok(vm.ctx.new_module(module, attrs))
4545
}
4646

vm/src/obj/objdict.rs

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use std::collections::HashMap;
33
use std::fmt;
44
use std::ops::{Deref, DerefMut};
55

6-
use crate::function::{OptionalArg, PyFuncArgs};
6+
use crate::function::{KwArgs, OptionalArg};
77
use crate::pyobject::{
8-
PyAttributes, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
8+
DictProtocol, PyAttributes, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue,
99
};
1010
use crate::vm::{ReprGuard, VirtualMachine};
1111

@@ -124,57 +124,48 @@ pub fn py_dict_to_attributes(dict: &PyObjectRef) -> PyAttributes {
124124
attrs
125125
}
126126

127-
pub fn attributes_to_py_dict(vm: &VirtualMachine, attributes: PyAttributes) -> PyResult {
128-
let dict = vm.ctx.new_dict();
129-
for (key, value) in attributes {
130-
let key = vm.ctx.new_str(key);
131-
set_item(&dict, vm, &key, &value);
132-
}
133-
Ok(dict)
134-
}
135-
136127
// Python dict methods:
137-
fn dict_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
138-
arg_check!(
139-
vm,
140-
args,
141-
required = [(_ty, Some(vm.ctx.type_type()))],
142-
optional = [(dict_obj, None)]
143-
);
144-
let dict = vm.ctx.new_dict();
145-
if let Some(dict_obj) = dict_obj {
146-
if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) {
147-
for (needle, value) in get_key_value_pairs(&dict_obj) {
148-
set_item(&dict, vm, &needle, &value);
149-
}
150-
} else {
151-
let iter = objiter::get_iter(vm, dict_obj)?;
152-
loop {
153-
fn err(vm: &VirtualMachine) -> PyObjectRef {
154-
vm.new_type_error("Iterator must have exactly two elements".to_string())
128+
impl PyDictRef {
129+
fn new(
130+
_class: PyClassRef, // TODO Support subclasses of int.
131+
dict_obj: OptionalArg<PyObjectRef>,
132+
kwargs: KwArgs,
133+
vm: &VirtualMachine,
134+
) -> PyResult<PyDictRef> {
135+
let dict = vm.ctx.new_dict();
136+
if let OptionalArg::Present(dict_obj) = dict_obj {
137+
if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) {
138+
for (needle, value) in get_key_value_pairs(&dict_obj) {
139+
set_item(dict.as_object(), vm, &needle, &value);
155140
}
156-
let element = match objiter::get_next_object(vm, &iter)? {
157-
Some(obj) => obj,
158-
None => break,
159-
};
160-
let elem_iter = objiter::get_iter(vm, &element)?;
161-
let needle = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?;
162-
let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?;
163-
if objiter::get_next_object(vm, &elem_iter)?.is_some() {
164-
return Err(err(vm));
141+
} else {
142+
let iter = objiter::get_iter(vm, &dict_obj)?;
143+
loop {
144+
fn err(vm: &VirtualMachine) -> PyObjectRef {
145+
vm.new_type_error("Iterator must have exactly two elements".to_string())
146+
}
147+
let element = match objiter::get_next_object(vm, &iter)? {
148+
Some(obj) => obj,
149+
None => break,
150+
};
151+
let elem_iter = objiter::get_iter(vm, &element)?;
152+
let needle =
153+
objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?;
154+
let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?;
155+
if objiter::get_next_object(vm, &elem_iter)?.is_some() {
156+
return Err(err(vm));
157+
}
158+
set_item(dict.as_object(), vm, &needle, &value);
165159
}
166-
set_item(&dict, vm, &needle, &value);
167160
}
168161
}
162+
for (needle, value) in kwargs.into_iter() {
163+
let py_needle = vm.new_str(needle);
164+
set_item(&dict.as_object(), vm, &py_needle, &value);
165+
}
166+
Ok(dict)
169167
}
170-
for (needle, value) in args.kwargs {
171-
let py_needle = vm.new_str(needle);
172-
set_item(&dict, vm, &py_needle, &value);
173-
}
174-
Ok(dict)
175-
}
176168

177-
impl PyDictRef {
178169
fn bool(self, _vm: &VirtualMachine) -> bool {
179170
!self.entries.borrow().is_empty()
180171
}
@@ -302,6 +293,31 @@ impl PyDictRef {
302293
}
303294
}
304295

296+
impl DictProtocol for PyDictRef {
297+
fn contains_key(&self, k: &str) -> bool {
298+
content_contains_key_str(&self.entries.borrow(), k)
299+
}
300+
301+
fn get_item(&self, k: &str) -> Option<PyObjectRef> {
302+
content_get_key_str(&self.entries.borrow(), k)
303+
}
304+
305+
fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> {
306+
get_key_value_pairs(self.as_object())
307+
}
308+
309+
// Item set/get:
310+
fn set_item(&self, ctx: &PyContext, key: &str, v: PyObjectRef) {
311+
let key = ctx.new_str(key.to_string());
312+
set_item_in_content(&mut self.entries.borrow_mut(), &key, &v);
313+
}
314+
315+
fn del_item(&self, key: &str) {
316+
let mut elements = get_mut_elements(self.as_object());
317+
elements.remove(key).unwrap();
318+
}
319+
}
320+
305321
pub fn init(context: &PyContext) {
306322
extend_class!(context, &context.dict_type, {
307323
"__bool__" => context.new_rustfunc(PyDictRef::bool),
@@ -310,7 +326,7 @@ pub fn init(context: &PyContext) {
310326
"__delitem__" => context.new_rustfunc(PyDictRef::delitem),
311327
"__getitem__" => context.new_rustfunc(PyDictRef::getitem),
312328
"__iter__" => context.new_rustfunc(PyDictRef::iter),
313-
"__new__" => context.new_rustfunc(dict_new),
329+
"__new__" => context.new_rustfunc(PyDictRef::new),
314330
"__repr__" => context.new_rustfunc(PyDictRef::repr),
315331
"__setitem__" => context.new_rustfunc(PyDictRef::setitem),
316332
"clear" => context.new_rustfunc(PyDictRef::clear),

vm/src/obj/objmodule.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
use crate::obj::objstr::PyStringRef;
21
use crate::obj::objtype::PyClassRef;
3-
use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
2+
use crate::pyobject::{DictProtocol, PyContext, PyRef, PyResult, PyValue};
43
use crate::vm::VirtualMachine;
54

65
#[derive(Debug)]
76
pub struct PyModule {
87
pub name: String,
9-
pub dict: PyObjectRef,
108
}
119
pub type PyModuleRef = PyRef<PyModule>;
1210

@@ -18,23 +16,21 @@ impl PyValue for PyModule {
1816

1917
impl PyModuleRef {
2018
fn dir(self: PyModuleRef, vm: &VirtualMachine) -> PyResult {
21-
let keys = self
22-
.dict
23-
.get_key_value_pairs()
24-
.iter()
25-
.map(|(k, _v)| k.clone())
26-
.collect();
27-
Ok(vm.ctx.new_list(keys))
28-
}
29-
30-
fn set_attr(self, attr: PyStringRef, value: PyObjectRef, vm: &VirtualMachine) {
31-
self.dict.set_item(&vm.ctx, &attr.value, value)
19+
if let Some(dict) = &self.into_object().dict {
20+
let keys = dict
21+
.get_key_value_pairs()
22+
.iter()
23+
.map(|(k, _v)| k.clone())
24+
.collect();
25+
Ok(vm.ctx.new_list(keys))
26+
} else {
27+
panic!("Modules should definitely have a dict.");
28+
}
3229
}
3330
}
3431

3532
pub fn init(context: &PyContext) {
3633
extend_class!(&context, &context.module_type, {
3734
"__dir__" => context.new_rustfunc(PyModuleRef::dir),
38-
"__setattr__" => context.new_rustfunc(PyModuleRef::set_attr)
3935
});
4036
}

vm/src/obj/objobject.rs

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
use super::objdict::{self, PyDictRef};
12
use super::objlist::PyList;
2-
use super::objmodule::PyModule;
33
use super::objstr::{self, PyStringRef};
44
use super::objtype;
55
use crate::function::PyFuncArgs;
@@ -23,11 +23,12 @@ impl PyValue for PyInstance {
2323
pub fn new_instance(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult {
2424
// more or less __new__ operator
2525
let cls = PyClassRef::try_from_object(vm, args.shift())?;
26-
Ok(if cls.is(&vm.ctx.object) {
27-
PyObject::new_without_dict(PyInstance, cls)
26+
let dict = if cls.is(&vm.ctx.object) {
27+
None
2828
} else {
29-
PyObject::new(PyInstance, cls)
30-
})
29+
Some(vm.ctx.new_dict())
30+
};
31+
Ok(PyObject::new(PyInstance, cls, dict))
3132
}
3233

3334
fn object_eq(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -114,7 +115,7 @@ fn object_setattr(
114115
}
115116

116117
if let Some(ref dict) = obj.clone().dict {
117-
dict.borrow_mut().insert(attr_name.value.clone(), value);
118+
dict.set_item(&vm.ctx, &attr_name.value, value);
118119
Ok(())
119120
} else {
120121
let type_name = objtype::get_type_name(obj.type_ref());
@@ -135,7 +136,7 @@ fn object_delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine)
135136
}
136137

137138
if let Some(ref dict) = obj.dict {
138-
dict.borrow_mut().remove(&attr_name.value);
139+
dict.del_item(&attr_name.value);
139140
Ok(())
140141
} else {
141142
let type_name = objtype::get_type_name(obj.type_ref());
@@ -227,13 +228,9 @@ fn object_class_setter(
227228
Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr)))
228229
}
229230

230-
fn object_dict(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
231-
if let Some(ref dict) = args.args[0].dict {
232-
let new_dict = vm.new_dict();
233-
for (attr, value) in dict.borrow().iter() {
234-
new_dict.set_item(&vm.ctx, &attr, value.clone());
235-
}
236-
Ok(new_dict)
231+
fn object_dict(object: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyDictRef> {
232+
if let Some(ref dict) = object.dict {
233+
Ok(dict.clone())
237234
} else {
238235
Err(vm.new_type_error("TypeError: no dictionary.".to_string()))
239236
}
@@ -273,15 +270,8 @@ fn object_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
273270
}
274271

275272
fn object_getattr(obj: &PyObjectRef, attr_name: &str) -> Option<PyObjectRef> {
276-
// TODO:
277-
// This is an all kinds of wrong work-around for the temporary difference in
278-
// shape between modules and object. It will disappear once that is fixed.
279-
if let Some(PyModule { ref dict, .. }) = obj.payload::<PyModule>() {
280-
return dict.get_item(attr_name);
281-
}
282-
283273
if let Some(ref dict) = obj.dict {
284-
dict.borrow().get(attr_name).cloned()
274+
dict.get_item(attr_name)
285275
} else {
286276
None
287277
}
@@ -293,8 +283,8 @@ pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes {
293283

294284
// Get instance attributes:
295285
if let Some(dict) = &obj.dict {
296-
for (name, value) in dict.borrow().iter() {
297-
attributes.insert(name.to_string(), value.clone());
286+
for (key, value) in objdict::get_key_value_pairs(dict.as_object()) {
287+
attributes.insert(key.to_string(), value.clone());
298288
}
299289
}
300290

0 commit comments

Comments
 (0)