Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r54154:7e37a82840df Date: 2012-04-02 18:14 +0200 http://bitbucket.org/pypy/pypy/changeset/7e37a82840df/
Log: Another case: iterating should work if the dict strategy changed "just" because of a __getitem__. It's workaroundesque but enough, I hope. diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py --- a/pypy/objspace/std/dictmultiobject.py +++ b/pypy/objspace/std/dictmultiobject.py @@ -273,8 +273,7 @@ def next(self): if self.dictimplementation is None: return None, None - if (self.len != self.dictimplementation.length() - or self.strategy is not self.dictimplementation.strategy): + if self.len != self.dictimplementation.length(): self.len = -1 # Make this error state sticky raise OperationError(self.space.w_RuntimeError, self.space.wrap("dictionary changed size during iteration")) @@ -282,7 +281,20 @@ if self.pos < self.len: result = self.next_entry() self.pos += 1 - return result + if self.strategy is self.dictimplementation.strategy: + return result # common case + else: + # waaa, obscure case: the strategy changed, but not the + # length of the dict. The (key, value) pair in 'result' + # might be out-of-date. We try to explicitly look up + # the key in the dict. + w_key = result[0] + w_value = self.dictimplementation.getitem(w_key) + if w_value is None: + self.len = -1 # Make this error state sticky + raise OperationError(self.space.w_RuntimeError, + self.space.wrap("dictionary changed during iteration")) + return (w_key, w_value) # no more entries self.dictimplementation = None return None, None diff --git a/pypy/objspace/std/test/test_dictmultiobject.py b/pypy/objspace/std/test/test_dictmultiobject.py --- a/pypy/objspace/std/test/test_dictmultiobject.py +++ b/pypy/objspace/std/test/test_dictmultiobject.py @@ -811,7 +811,17 @@ # 'd' is now length 4 raises(RuntimeError, it.next) - def test_iter_dict_strategy_only_change(self): + def test_iter_dict_strategy_only_change_1(self): + d = {1: 2, 3: 4, 5: 6} + it = d.iteritems() + class Foo(object): + def __eq__(self, other): + return False + d.get(Foo()) # this changes the strategy of 'd' + lst = list(it) # but iterating still works + assert sorted(lst) == [(1, 2), (3, 4), (5, 6)] + + def test_iter_dict_strategy_only_change_2(self): d = {1: 2, 3: 4, 5: 6} it = d.iteritems() d['foo'] = 'bar' _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit