Skip to content

Commit 848714e

Browse files
committed
save passed object into the first .args parameter for dict/mappingproxy KeyError
1 parent 6d5f381 commit 848714e

File tree

10 files changed

+60
-31
lines changed

10 files changed

+60
-31
lines changed

tests/snippets/dict.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@
5252
with assertRaises(StopIteration):
5353
next(it)
5454

55+
with assertRaises(KeyError) as cm:
56+
del x[10]
57+
assert cm.exception.args[0] == 10
58+
5559
# Iterating a dictionary is just its keys:
5660
assert ['a', 'b', 'd'] == list(x)
5761

@@ -95,9 +99,6 @@
9599
x[2] = 2
96100
x[(5, 6)] = 5
97101

98-
with assertRaises(KeyError):
99-
x["not here"]
100-
101102
with assertRaises(TypeError):
102103
x[[]] # Unhashable type.
103104

@@ -167,19 +168,42 @@ def __eq__(self, other):
167168
y.update(y)
168169
assert y == {'a': 2, 'b': 12, 'c': 19, 'd': -1} # hasn't changed
169170

171+
# KeyError has object that used as key as an .args[0]
172+
with assertRaises(KeyError) as cm:
173+
x['not here']
174+
assert cm.exception.args[0] == "not here"
175+
with assertRaises(KeyError) as cm:
176+
x.pop('not here')
177+
assert cm.exception.args[0] == "not here"
178+
179+
with assertRaises(KeyError) as cm:
180+
x[10]
181+
assert cm.exception.args[0] == 10
182+
with assertRaises(KeyError) as cm:
183+
x.pop(10)
184+
assert cm.exception.args[0] == 10
185+
186+
class MyClass: pass
187+
obj = MyClass()
188+
189+
with assertRaises(KeyError) as cm:
190+
x[obj]
191+
assert cm.exception.args[0] == obj
192+
with assertRaises(KeyError) as cm:
193+
x.pop(obj)
194+
assert cm.exception.args[0] == obj
195+
170196
x = {1: 'a', '1': None}
171197
assert x.pop(1) == 'a'
172198
assert x.pop('1') is None
173199
assert x == {}
174200

175-
with assertRaises(KeyError):
176-
x.pop("not here")
177-
178201
x = {1: 'a'}
179202
assert (1, 'a') == x.popitem()
180-
with assertRaises(KeyError):
181-
x.popitem()
182203
assert x == {}
204+
with assertRaises(KeyError) as cm:
205+
x.popitem()
206+
assert cm.exception.args == ('popitem(): dictionary is empty',)
183207

184208
x = {'a': 4}
185209
assert 4 == x.setdefault('a', 0)

tests/snippets/mappingproxy.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ def b():
99

1010

1111
assert A.__dict__['a'] == A.a
12-
with assertRaises(KeyError):
13-
A.__dict__['not_here']
12+
with assertRaises(KeyError) as cm:
13+
A.__dict__['not here']
14+
15+
assert cm.exception.args[0] == "not here"
1416

1517
assert 'b' in A.__dict__
1618
assert 'c' not in A.__dict__

tests/snippets/testutils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def assert_raises(exc_type, expr, msg=None):
2323
class assertRaises:
2424
def __init__(self, expected):
2525
self.expected = expected
26+
self.exception = None
2627

2728
def __enter__(self):
2829
return self
@@ -33,6 +34,8 @@ def __exit__(self, exc_type, exc_val, exc_tb):
3334
assert False, failmsg
3435
if not issubclass(exc_type, self.expected):
3536
return False
37+
38+
self.exception = exc_val
3639
return True
3740

3841

vm/src/dictdatatype.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,7 @@ impl<T: Clone> Dict<T> {
129129
if self.delete_if_exists(vm, key)? {
130130
Ok(())
131131
} else {
132-
let key_repr = vm.to_pystr(key)?;
133-
Err(vm.new_key_error(format!("Key not found: {}", key_repr)))
132+
Err(vm.new_key_error(key.clone()))
134133
}
135134
}
136135

@@ -247,8 +246,7 @@ impl<T: Clone> Dict<T> {
247246
self.unchecked_delete(index);
248247
Ok(value)
249248
} else {
250-
let key_repr = vm.to_pystr(key)?;
251-
Err(vm.new_key_error(format!("Key not found: {}", key_repr)))
249+
Err(vm.new_key_error(key.clone()))
252250
}
253251
}
254252

vm/src/obj/objdict.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ impl PyDictRef {
210210
let method = method_or_err?;
211211
return vm.invoke(method, vec![key]);
212212
}
213-
Err(vm.new_key_error(format!("Key not found: {}", vm.to_pystr(&key)?)))
213+
Err(vm.new_key_error(key.clone()))
214214
}
215215

216216
fn get(
@@ -266,7 +266,8 @@ impl PyDictRef {
266266
if let Some((key, value)) = entries.pop_front() {
267267
Ok(vm.ctx.new_tuple(vec![key, value]))
268268
} else {
269-
Err(vm.new_key_error("popitem(): dictionary is empty".to_string()))
269+
let err_msg = vm.new_str("popitem(): dictionary is empty".to_string());
270+
Err(vm.new_key_error(err_msg))
270271
}
271272
}
272273

vm/src/obj/objmappingproxy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl PyMappingProxy {
2828
if let Some(value) = objtype::class_get_attr(&self.class, key.as_str()) {
2929
return Ok(value);
3030
}
31-
Err(vm.new_key_error(format!("Key not found: {}", key)))
31+
Err(vm.new_key_error(key.into_object()))
3232
}
3333

3434
#[pymethod(name = "__contains__")]

vm/src/obj/objset.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ impl PySetInner {
269269
if let Some((key, _)) = self.content.pop_front() {
270270
Ok(key)
271271
} else {
272-
Err(vm.new_key_error("pop from an empty set".to_string()))
272+
let err_msg = vm.new_str("pop from an empty set".to_string());
273+
Err(vm.new_key_error(err_msg))
273274
}
274275
}
275276

vm/src/obj/objstr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,7 +1335,7 @@ fn perform_format(
13351335
let result = match arguments.get_optional_kwarg(&keyword) {
13361336
Some(argument) => call_object_format(vm, argument.clone(), &format_spec)?,
13371337
None => {
1338-
return Err(vm.new_key_error(format!("'{}'", keyword)));
1338+
return Err(vm.new_key_error(vm.new_str(keyword.to_string())));
13391339
}
13401340
};
13411341
get_value(&result)

vm/src/stdlib/pwd.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ fn pwd_getpwnam(name: PyStringRef, vm: &VirtualMachine) -> PyResult<Passwd> {
4848
Ok(Some(passwd)) => Ok(passwd),
4949
_ => {
5050
let name_repr = vm.to_repr(name.as_object())?;
51-
let message = format!("getpwnam(): name not found: {}", name_repr);
51+
let message = vm.new_str(format!("getpwnam(): name not found: {}", name_repr));
5252
Err(vm.new_key_error(message))
5353
}
5454
}
@@ -58,7 +58,7 @@ fn pwd_getpwuid(uid: u32, vm: &VirtualMachine) -> PyResult<Passwd> {
5858
match Passwd::from_uid(uid) {
5959
Some(passwd) => Ok(passwd),
6060
_ => {
61-
let message = format!("getpwuid(): uid not found: {}", uid);
61+
let message = vm.new_str(format!("getpwuid(): uid not found: {}", uid));
6262
Err(vm.new_key_error(message))
6363
}
6464
}

vm/src/vm.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,20 +167,20 @@ impl VirtualMachine {
167167
self.ctx.new_bool(b)
168168
}
169169

170-
pub fn new_empty_exception(&self, exc_type: PyClassRef) -> PyResult {
170+
fn new_exception_obj(&self, exc_type: PyClassRef, args: Vec<PyObjectRef>) -> PyResult {
171+
// TODO: add repr of args into logging?
171172
info!("New exception created: {}", exc_type.name);
172-
let args = PyFuncArgs::default();
173173
self.invoke(exc_type.into_object(), args)
174174
}
175175

176+
pub fn new_empty_exception(&self, exc_type: PyClassRef) -> PyResult {
177+
self.new_exception_obj(exc_type, vec![])
178+
}
179+
176180
/// Create Python instance of `exc_type` with message as first element of `args` tuple
177181
pub fn new_exception(&self, exc_type: PyClassRef, msg: String) -> PyObjectRef {
178-
// TODO: exc_type may be user-defined exception, so we should return PyResult
179-
// TODO: maybe there is a clearer way to create an instance:
180-
info!("New exception created: {}('{}')", exc_type.name, msg);
181-
let pymsg = self.new_str(msg);
182-
let args: Vec<PyObjectRef> = vec![pymsg];
183-
self.invoke(exc_type.into_object(), args).unwrap()
182+
let pystr_msg = self.new_str(msg);
183+
self.new_exception_obj(exc_type, vec![pystr_msg]).unwrap()
184184
}
185185

186186
pub fn new_attribute_error(&self, msg: String) -> PyObjectRef {
@@ -224,9 +224,9 @@ impl VirtualMachine {
224224
self.new_exception(value_error, msg)
225225
}
226226

227-
pub fn new_key_error(&self, msg: String) -> PyObjectRef {
227+
pub fn new_key_error(&self, obj: PyObjectRef) -> PyObjectRef {
228228
let key_error = self.ctx.exceptions.key_error.clone();
229-
self.new_exception(key_error, msg)
229+
self.new_exception_obj(key_error, vec![obj]).unwrap()
230230
}
231231

232232
pub fn new_index_error(&self, msg: String) -> PyObjectRef {

0 commit comments

Comments
 (0)