Skip to content

Commit f4543f5

Browse files
committed
Fix defaultdict
1 parent be54bc0 commit f4543f5

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-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: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,14 +1332,47 @@ 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 Some(get_method) = vm
1342+
.get_method(subject.to_owned(), vm.ctx.intern_str("get"))
1343+
.transpose()?
1344+
{
1345+
// dummy = object()
1346+
// CPython: dummy = _PyObject_CallNoArgs((PyObject *)&PyBaseObject_Type);
1347+
let dummy = vm
1348+
.ctx
1349+
.new_base_object(vm.ctx.types.object_type.to_owned(), None);
1350+
1351+
for key in keys {
1352+
// value = map.get(key, dummy)
1353+
match get_method.call((key.as_object(), dummy.clone()), vm) {
1354+
Ok(value) => {
1355+
// if value == dummy: key not in map!
1356+
if value.is(&dummy) {
1357+
all_match = false;
1358+
break;
1359+
}
1360+
values.push(value);
1361+
}
1362+
Err(e) => return Err(e),
1363+
}
1364+
}
1365+
} else {
1366+
// Fallback if .get() method is not available (shouldn't happen for mappings)
1367+
for key in keys {
1368+
match subject.get_item(key.as_object(), vm) {
1369+
Ok(value) => values.push(value),
1370+
Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => {
1371+
all_match = false;
1372+
break;
1373+
}
1374+
Err(e) => return Err(e),
13411375
}
1342-
Err(e) => return Err(e),
13431376
}
13441377
}
13451378

0 commit comments

Comments
 (0)