[pypy-commit] pypy default: fix iters that fail due to modification to return a length_hint of 0, and add a

pjenvey noreply at buildbot.pypy.org
Fri Jan 25 01:30:09 CET 2013


Author: Philip Jenvey <pjenvey at underboss.org>
Branch: 
Changeset: r60434:cbb68962961a
Date: 2013-01-24 16:28 -0800
http://bitbucket.org/pypy/pypy/changeset/cbb68962961a/

Log:	fix iters that fail due to modification to return a length_hint of
	0, and add a couple PEP 424 workarounds to make test_iterlen pass

diff --git a/lib-python/2.7/test/test_iterlen.py b/lib-python/2.7/test/test_iterlen.py
--- a/lib-python/2.7/test/test_iterlen.py
+++ b/lib-python/2.7/test/test_iterlen.py
@@ -94,7 +94,11 @@
 
     def test_no_len_for_infinite_repeat(self):
         # The repeat() object can also be infinite
-        self.assertRaises(TypeError, len, repeat(None))
+        if test_support.check_impl_detail(pypy=True):
+            # 3.4 (PEP 424) behavior
+            self.assertEqual(len(repeat(None)), NotImplemented)
+        else:
+            self.assertRaises(TypeError, len, repeat(None))
 
 class TestXrange(TestInvariantWithoutMutations):
 
@@ -230,6 +234,7 @@
         self.assertRaises(RuntimeError, b.extend, BadLen())
         self.assertRaises(RuntimeError, b.extend, BadLengthHint())
 
+    @test_support.impl_detail("PEP 424 disallows None results", pypy=False)
     def test_invalid_hint(self):
         # Make sure an invalid result doesn't muck-up the works
         self.assertEqual(list(NoneLengthHint()), list(range(10)))
diff --git a/lib-python/conftest.py b/lib-python/conftest.py
--- a/lib-python/conftest.py
+++ b/lib-python/conftest.py
@@ -281,7 +281,6 @@
     RegrTest('test_ioctl.py'),
     RegrTest('test_isinstance.py', core=True),
     RegrTest('test_iter.py', core=True),
-    RegrTest('test_iterlen.py', skip="undocumented internal API behavior __length_hint__"),
     RegrTest('test_itertools.py', core=True, usemodules="itertools struct"),
     RegrTest('test_json.py'),
     RegrTest('test_kqueue.py'),
diff --git a/pypy/module/_collections/interp_deque.py b/pypy/module/_collections/interp_deque.py
--- a/pypy/module/_collections/interp_deque.py
+++ b/pypy/module/_collections/interp_deque.py
@@ -521,7 +521,11 @@
         return self.space.wrap(self.counter)
 
     def next(self):
-        self.deque.checklock(self.lock)
+        if self.lock is not self.deque.lock:
+            self.counter = 0
+            raise OperationError(
+                self.space.w_RuntimeError,
+                self.space.wrap("deque mutated during iteration"))
         if self.counter == 0:
             raise OperationError(self.space.w_StopIteration, self.space.w_None)
         self.counter -= 1
@@ -560,7 +564,11 @@
         return self.space.wrap(self.counter)
 
     def next(self):
-        self.deque.checklock(self.lock)
+        if self.lock is not self.deque.lock:
+            self.counter = 0
+            raise OperationError(
+                self.space.w_RuntimeError,
+                self.space.wrap("deque mutated during iteration"))
         if self.counter == 0:
             raise OperationError(self.space.w_StopIteration, self.space.w_None)
         self.counter -= 1
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
@@ -352,7 +352,7 @@
         self.pos = 0
 
     def length(self):
-        if self.dictimplementation is not None:
+        if self.dictimplementation is not None and self.len != -1:
             return self.len - self.pos
         return 0
 
diff --git a/pypy/objspace/std/setobject.py b/pypy/objspace/std/setobject.py
--- a/pypy/objspace/std/setobject.py
+++ b/pypy/objspace/std/setobject.py
@@ -898,7 +898,7 @@
         raise NotImplementedError
 
     def length(self):
-        if self.setimplementation is not None:
+        if self.setimplementation is not None and self.len != -1:
             return self.len - self.pos
         return 0
 
diff --git a/pypy/objspace/std/test/test_lengthhint.py b/pypy/objspace/std/test/test_lengthhint.py
--- a/pypy/objspace/std/test/test_lengthhint.py
+++ b/pypy/objspace/std/test/test_lengthhint.py
@@ -6,7 +6,7 @@
     SIZE = 4
     ITEMS = range(SIZE)
 
-    def _test_length_hint(self, w_obj):
+    def _test_length_hint(self, w_obj, w_mutate=None):
         space = self.space
         assert space.length_hint(w_obj, 8) == self.SIZE
 
@@ -18,6 +18,13 @@
         space.next(w_iter)
         assert space.length_hint(w_iter, 8) == self.SIZE - 1
 
+        if w_mutate is not None:
+            # Test handling of collections that enforce length
+            # immutability during iteration
+            space.call_function(w_mutate)
+            space.raises_w(space.w_RuntimeError, space.next, w_iter)
+            assert space.length_hint(w_iter, 8) == 0
+
     def test_bytearray(self):
         space = self.space
         w_bytearray = space.call_function(space.w_bytearray,
@@ -31,16 +38,20 @@
         self._test_length_hint(w_dict)
 
     def test_dict_iterkeys(self):
-        w_iterkeys = self.space.appexec([], """():
-            return dict.fromkeys(%r).iterkeys()
-        """ % self.ITEMS)
-        self._test_length_hint(w_iterkeys)
+        space = self.space
+        w_iterkeys, w_mutate = space.fixedview(space.appexec([], """():
+            d = dict.fromkeys(%r)
+            return d.iterkeys(), d.popitem
+        """ % self.ITEMS), 2)
+        self._test_length_hint(w_iterkeys, w_mutate)
 
     def test_dict_values(self):
-        w_itervalues = self.space.appexec([], """():
-            return dict.fromkeys(%r).itervalues()
-        """ % self.ITEMS)
-        self._test_length_hint(w_itervalues)
+        space = self.space
+        w_itervalues, w_mutate = space.fixedview(space.appexec([], """():
+            d = dict.fromkeys(%r)
+            return d.itervalues(), d.popitem
+        """ % self.ITEMS), 2)
+        self._test_length_hint(w_itervalues, w_mutate)
 
     def test_frozenset(self):
         space = self.space
@@ -50,7 +61,8 @@
     def test_set(self):
         space = self.space
         w_set = space.call_function(space.w_set, space.wrap(self.ITEMS))
-        self._test_length_hint(w_set)
+        w_mutate = space.getattr(w_set, space.wrap('pop'))
+        self._test_length_hint(w_set, w_mutate)
 
     def test_list(self):
         self._test_length_hint(self.space.wrap(self.ITEMS))
@@ -101,12 +113,19 @@
         self._test_length_hint(W_Repeat(space, space.wrap(22),
                                         space.wrap(self.SIZE)))
 
-    def test_collections_deque(self):
+    def _setup_deque(self):
         space = self.space
         w_deque = W_Deque(space)
         space.call_method(w_deque, '__init__', space.wrap(self.ITEMS))
-        self._test_length_hint(w_deque)
-        self._test_length_hint(w_deque.reviter())
+        w_mutate = space.getattr(w_deque, space.wrap('pop'))
+        return w_deque, w_mutate
+
+    def test_collections_deque(self):
+        self._test_length_hint(*self._setup_deque())
+
+    def test_collections_deque_rev(self):
+        w_deque, w_mutate = self._setup_deque()
+        self._test_length_hint(w_deque.reviter(), w_mutate)
 
     def test_default(self):
         space = self.space


More information about the pypy-commit mailing list