Commits

Armin Rigo committed 7e37a82

Another case: iterating should work if the dict strategy changed "just"
because of a __getitem__. It's workaroundesque but enough, I hope.

  • Participants
  • Parent commits ae4a7ab

Comments (0)

Files changed (2)

File pypy/objspace/std/dictmultiobject.py

     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"))
         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

File pypy/objspace/std/test/test_dictmultiobject.py

         # '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'