Skip to content

Commit 0cd7837

Browse files
asvetloviscai-msftDreamsorcerer
authored
Fix 'Dictionary changed during iteration' exception raising (#628)
* add pypy test * Lint * Fix #620: Don't raise 'Dictionary changed during iteration' when the error should not be raised Co-authored-by: iscai-msft <iscai@microsoft.com> Co-authored-by: Sam Bull <aa6bs0@sambull.org>
1 parent 9dde0bd commit 0cd7837

File tree

3 files changed

+34
-10
lines changed

3 files changed

+34
-10
lines changed

CHANGES/620.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix pure-python implementation that used to raise "Dictionary changed during iteration" error when iterated view (``.keys()``, ``.values()`` or ``.items()``) was created before the dictionary's content change.

multidict/_multidict_py.py

+9-10
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,6 @@ def __length_hint__(self):
435435
class _ViewBase:
436436
def __init__(self, impl):
437437
self._impl = impl
438-
self._version = impl._version
439438

440439
def __len__(self):
441440
return len(self._impl._items)
@@ -451,11 +450,11 @@ def __contains__(self, item):
451450
return False
452451

453452
def __iter__(self):
454-
return _Iter(len(self), self._iter())
453+
return _Iter(len(self), self._iter(self._impl._version))
455454

456-
def _iter(self):
455+
def _iter(self, version):
457456
for i, k, v in self._impl._items:
458-
if self._version != self._impl._version:
457+
if version != self._impl._version:
459458
raise RuntimeError("Dictionary changed during iteration")
460459
yield k, v
461460

@@ -475,11 +474,11 @@ def __contains__(self, value):
475474
return False
476475

477476
def __iter__(self):
478-
return _Iter(len(self), self._iter())
477+
return _Iter(len(self), self._iter(self._impl._version))
479478

480-
def _iter(self):
479+
def _iter(self, version):
481480
for item in self._impl._items:
482-
if self._version != self._impl._version:
481+
if version != self._impl._version:
483482
raise RuntimeError("Dictionary changed during iteration")
484483
yield item[2]
485484

@@ -499,11 +498,11 @@ def __contains__(self, key):
499498
return False
500499

501500
def __iter__(self):
502-
return _Iter(len(self), self._iter())
501+
return _Iter(len(self), self._iter(self._impl._version))
503502

504-
def _iter(self):
503+
def _iter(self, version):
505504
for item in self._impl._items:
506-
if self._version != self._impl._version:
505+
if version != self._impl._version:
507506
raise RuntimeError("Dictionary changed during iteration")
508507
yield item[1]
509508

tests/test_mutable_multidict.py

+24
Original file line numberDiff line numberDiff line change
@@ -484,3 +484,27 @@ def test_sizeof(self, cls):
484484
def test_min_sizeof(self, cls):
485485
md = cls()
486486
assert sys.getsizeof(md) < 1024
487+
488+
def test_issue_620_items(self, cls):
489+
# https://github.com/aio-libs/multidict/issues/620
490+
d = cls({"a": "123, 456", "b": "789"})
491+
before_mutation_items = d.items()
492+
d["c"] = "000"
493+
# This causes an error on pypy.
494+
list(before_mutation_items)
495+
496+
def test_issue_620_keys(self, cls):
497+
# https://github.com/aio-libs/multidict/issues/620
498+
d = cls({"a": "123, 456", "b": "789"})
499+
before_mutation_keys = d.keys()
500+
d["c"] = "000"
501+
# This causes an error on pypy.
502+
list(before_mutation_keys)
503+
504+
def test_issue_620_values(self, cls):
505+
# https://github.com/aio-libs/multidict/issues/620
506+
d = cls({"a": "123, 456", "b": "789"})
507+
before_mutation_values = d.values()
508+
d["c"] = "000"
509+
# This causes an error on pypy.
510+
list(before_mutation_values)

0 commit comments

Comments
 (0)