Commits

Armin Rigo committed 8331c42

Test and fix (thanks ionelmc on irc).

Comments (0)

Files changed (4)

pypy/objspace/descroperation.py

     return w_iter
 list_iter._annspecialcase_ = 'specialize:memo'
 
+def tuple_iter(space):
+    "Utility that returns the app-level descriptor tuple.__iter__."
+    w_src, w_iter = space.lookup_in_type_where(space.w_tuple,
+                                               '__iter__')
+    return w_iter
+tuple_iter._annspecialcase_ = 'specialize:memo'
+
 def raiseattrerror(space, w_obj, name, w_descr=None):
     if w_descr is None:
         raise operationerrfmt(space.w_AttributeError,

pypy/objspace/std/listobject.py

 
     def _extend_from_iterable(self, w_list, w_iterable):
         space = self.space
-        if isinstance(w_iterable, W_AbstractTupleObject):
+        if (isinstance(w_iterable, W_AbstractTupleObject)
+                and space._uses_tuple_iter(w_iterable)):
             w_list.__init__(space, w_iterable.getitems_copy())
             return
 

pypy/objspace/std/objspace.py

                 self.wrap("expected length %d, got %d" % (expected, got)))
 
     def unpackiterable(self, w_obj, expected_length=-1):
-        if isinstance(w_obj, W_AbstractTupleObject):
+        if isinstance(w_obj, W_AbstractTupleObject) and self._uses_tuple_iter(w_obj):
             t = w_obj.getitems_copy()
         elif type(w_obj) is W_ListObject:
             t = w_obj.getitems_copy()
     def fixedview(self, w_obj, expected_length=-1, unroll=False):
         """ Fast paths
         """
-        if isinstance(w_obj, W_AbstractTupleObject):
+        if isinstance(w_obj, W_AbstractTupleObject) and self._uses_tuple_iter(w_obj):
             t = w_obj.tolist()
         elif type(w_obj) is W_ListObject:
             if unroll:
     def listview(self, w_obj, expected_length=-1):
         if type(w_obj) is W_ListObject:
             t = w_obj.getitems()
-        elif isinstance(w_obj, W_AbstractTupleObject):
+        elif isinstance(w_obj, W_AbstractTupleObject) and self._uses_tuple_iter(w_obj):
             t = w_obj.getitems_copy()
         elif isinstance(w_obj, W_ListObject) and self._uses_list_iter(w_obj):
             t = w_obj.getitems()
             return w_obj.listview_str()
         if type(w_obj) is W_SetObject or type(w_obj) is W_FrozensetObject:
             return w_obj.listview_str()
-        if isinstance(w_obj, W_StringObject):
+        if isinstance(w_obj, W_StringObject) and self._uses_no_iter(w_obj):
             return w_obj.listview_str()
         if isinstance(w_obj, W_ListObject) and self._uses_list_iter(w_obj):
             return w_obj.getitems_str()
             return w_obj.listview_unicode()
         if type(w_obj) is W_SetObject or type(w_obj) is W_FrozensetObject:
             return w_obj.listview_unicode()
-        if isinstance(w_obj, W_UnicodeObject):
+        if isinstance(w_obj, W_UnicodeObject) and self._uses_no_iter(w_obj):
             return w_obj.listview_unicode()
         if isinstance(w_obj, W_ListObject) and self._uses_list_iter(w_obj):
             return w_obj.getitems_unicode()
         from pypy.objspace.descroperation import list_iter
         return self.lookup(w_obj, '__iter__') is list_iter(self)
 
+    def _uses_tuple_iter(self, w_obj):
+        from pypy.objspace.descroperation import tuple_iter
+        return self.lookup(w_obj, '__iter__') is tuple_iter(self)
+
+    def _uses_no_iter(self, w_obj):
+        return self.lookup(w_obj, '__iter__') is None
+
     def sliceindices(self, w_slice, w_length):
         if isinstance(w_slice, W_SliceObject):
             a, b, c = w_slice.indices3(self, self.int_w(w_length))

pypy/objspace/std/test/test_listobject.py

         non_list = NonList()
         assert [] != non_list
 
+    def test_extend_from_empty_list_with_subclasses(self):
+        # some of these tests used to fail by ignoring the
+        # custom __iter__() --- but only if the list has so
+        # far the empty strategy, as opposed to .extend()ing
+        # a non-empty list.
+        class T(tuple):
+            def __iter__(self):
+                yield "ok"
+        assert list(T([5, 6])) == ["ok"]
+        #
+        class L(list):
+            def __iter__(self):
+                yield "ok"
+        assert list(L([5, 6])) == ["ok"]
+        assert list(L([5.2, 6.3])) == ["ok"]
+        #
+        class S(str):
+            def __iter__(self):
+                yield "ok"
+        assert list(S("don't see me")) == ["ok"]
+        #
+        class U(unicode):
+            def __iter__(self):
+                yield "ok"
+        assert list(U(u"don't see me")) == ["ok"]
+
 
 class AppTestForRangeLists(AppTestW_ListObject):
     spaceconfig = {"objspace.std.withrangelist": True}