Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Lib/test/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,31 @@ def test_reversed_dict_after_clear_and_restore(self):
for it in iterators:
self.assertEqual(list(it), [])

def test_reversed_dict_keys_changed_during_iteration(self):
d = dict.fromkeys(range(10))
for i in range(7):
del d[i]

iterators = (
reversed(d),
reversed(d.keys()),
reversed(d.values()),
reversed(d.items()),
)
for it in iterators:
next(it)

# Same size as before, but with different keys below
# the iterators' current position.
d.clear()
d.update(dict.fromkeys(range(10)))
for i in range(3, 10):
del d[i]

for it in iterators:
with self.assertRaisesRegex(RuntimeError, 'keys changed'):
list(it)

def test_dict_copy_order(self):
# bpo-34320
od = collections.OrderedDict([('a', 1), ('b', 2)])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Reverse iterators over a :class:`dict` and its views now raise
:exc:`RuntimeError` if the dictionary's keys change during iteration, like
forward iterators, instead of yielding entries for the new keys.
6 changes: 6 additions & 0 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -6254,6 +6254,12 @@ dictreviter_iter_lock_held(PyDictObject *d, PyObject *self)
value = entry_ptr->me_value;
}
}
// We found an element, but did not expect it
if (di->len == 0) {
PyErr_SetString(PyExc_RuntimeError,
"dictionary keys changed during iteration");
goto fail;
}
di->di_pos = i-1;
di->len--;

Expand Down
Loading