Skip to content

Commit 1984856

Browse files
committed
Fix defaultdict
1 parent bd31a7f commit 1984856

File tree

2 files changed

+37
-10
lines changed

2 files changed

+37
-10
lines changed

Lib/test/test_patma.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,6 @@ def test_patma_132(self):
13371337
self.assertEqual(x, [0, 1, 2])
13381338
self.assertEqual(y, 0)
13391339

1340-
@unittest.expectedFailure # TODO: RUSTPYTHON
13411340
def test_patma_133(self):
13421341
x = collections.defaultdict(int, {0: 1})
13431342
match x:
@@ -1350,7 +1349,6 @@ def test_patma_133(self):
13501349
self.assertEqual(x, {0: 1})
13511350
self.assertEqual(y, 2)
13521351

1353-
@unittest.expectedFailure # TODO: RUSTPYTHON
13541352
def test_patma_134(self):
13551353
x = collections.defaultdict(int, {0: 1})
13561354
match x:
@@ -1364,7 +1362,6 @@ def test_patma_134(self):
13641362
self.assertEqual(y, 2)
13651363
self.assertEqual(z, {0: 1})
13661364

1367-
@unittest.expectedFailure # TODO: RUSTPYTHON
13681365
def test_patma_135(self):
13691366
x = collections.defaultdict(int, {0: 1})
13701367
match x:

vm/src/frame.rs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,14 +1332,44 @@ impl ExecutingFrame<'_> {
13321332
let mut values = Vec::new();
13331333
let mut all_match = true;
13341334

1335-
for key in keys {
1336-
match subject.get_item(key.as_object(), vm) {
1337-
Ok(value) => values.push(value),
1338-
Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
1339-
all_match = false;
1340-
break;
1335+
// We use the two argument form of map.get(key, default) for two reasons:
1336+
// - Atomically check for a key and get its value without error handling.
1337+
// - Don't cause key creation or resizing in dict subclasses like
1338+
// collections.defaultdict that define __missing__ (or similar).
1339+
// See CPython's _PyEval_MatchKeys
1340+
1341+
if let Ok(get_method) = subject.get_attr("get", vm) {
1342+
// dummy = object()
1343+
// CPython: dummy = _PyObject_CallNoArgs((PyObject *)&PyBaseObject_Type);
1344+
let dummy = vm
1345+
.ctx
1346+
.new_base_object(vm.ctx.types.object_type.to_owned(), None);
1347+
1348+
for key in keys {
1349+
// value = map.get(key, dummy)
1350+
match get_method.call((key.as_object(), dummy.clone()), vm) {
1351+
Ok(value) => {
1352+
// if value == dummy: key not in map!
1353+
if value.is(&dummy) {
1354+
all_match = false;
1355+
break;
1356+
}
1357+
values.push(value);
1358+
}
1359+
Err(e) => return Err(e),
1360+
}
1361+
}
1362+
} else {
1363+
// Fallback if .get() method is not available (shouldn't happen for mappings)
1364+
for key in keys {
1365+
match subject.get_item(key.as_object(), vm) {
1366+
Ok(value) => values.push(value),
1367+
Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
1368+
all_match = false;
1369+
break;
1370+
}
1371+
Err(e) => return Err(e),
13411372
}
1342-
Err(e) => return Err(e),
13431373
}
13441374
}
13451375

0 commit comments

Comments
 (0)