Skip to content

Commit

Permalink
Fix 'Dictionary changed during iteration' exception raising (#628)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people committed Sep 30, 2021
1 parent 9dde0bd commit 0cd7837
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGES/620.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +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.
19 changes: 9 additions & 10 deletions multidict/_multidict_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,6 @@ def __length_hint__(self):
class _ViewBase:
def __init__(self, impl):
self._impl = impl
self._version = impl._version

def __len__(self):
return len(self._impl._items)
Expand All @@ -451,11 +450,11 @@ def __contains__(self, item):
return False

def __iter__(self):
return _Iter(len(self), self._iter())
return _Iter(len(self), self._iter(self._impl._version))

def _iter(self):
def _iter(self, version):
for i, k, v in self._impl._items:
if self._version != self._impl._version:
if version != self._impl._version:
raise RuntimeError("Dictionary changed during iteration")
yield k, v

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

def __iter__(self):
return _Iter(len(self), self._iter())
return _Iter(len(self), self._iter(self._impl._version))

def _iter(self):
def _iter(self, version):
for item in self._impl._items:
if self._version != self._impl._version:
if version != self._impl._version:
raise RuntimeError("Dictionary changed during iteration")
yield item[2]

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

def __iter__(self):
return _Iter(len(self), self._iter())
return _Iter(len(self), self._iter(self._impl._version))

def _iter(self):
def _iter(self, version):
for item in self._impl._items:
if self._version != self._impl._version:
if version != self._impl._version:
raise RuntimeError("Dictionary changed during iteration")
yield item[1]

Expand Down
24 changes: 24 additions & 0 deletions tests/test_mutable_multidict.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,3 +484,27 @@ def test_sizeof(self, cls):
def test_min_sizeof(self, cls):
md = cls()
assert sys.getsizeof(md) < 1024

def test_issue_620_items(self, cls):
# https://github.com/aio-libs/multidict/issues/620
d = cls({"a": "123, 456", "b": "789"})
before_mutation_items = d.items()
d["c"] = "000"
# This causes an error on pypy.
list(before_mutation_items)

def test_issue_620_keys(self, cls):
# https://github.com/aio-libs/multidict/issues/620
d = cls({"a": "123, 456", "b": "789"})
before_mutation_keys = d.keys()
d["c"] = "000"
# This causes an error on pypy.
list(before_mutation_keys)

def test_issue_620_values(self, cls):
# https://github.com/aio-libs/multidict/issues/620
d = cls({"a": "123, 456", "b": "789"})
before_mutation_values = d.values()
d["c"] = "000"
# This causes an error on pypy.
list(before_mutation_values)

0 comments on commit 0cd7837

Please sign in to comment.