[Python-checkins] cpython (3.3): Isuse #17720: Fix APPENDS handling in the Python implementation of Unpickler

alexandre.vassalotti python-checkins at python.org
Sat Apr 20 22:28:12 CEST 2013


http://hg.python.org/cpython/rev/37139694aed0
changeset:   83469:37139694aed0
branch:      3.3
parent:      83466:186f6bb3e46a
user:        Alexandre Vassalotti <alexandre at peadrop.com>
date:        Sat Apr 20 13:19:46 2013 -0700
summary:
  Isuse #17720: Fix APPENDS handling in the Python implementation of Unpickler
to correctly process the opcode when it is used on non-list objects.

files:
  Lib/pickle.py            |  10 +++-
  Lib/test/pickletester.py |  58 ++++++++++++++++++++++++++-
  Misc/NEWS                |   3 +
  3 files changed, 65 insertions(+), 6 deletions(-)


diff --git a/Lib/pickle.py b/Lib/pickle.py
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -1208,8 +1208,14 @@
     def load_appends(self):
         stack = self.stack
         mark = self.marker()
-        list = stack[mark - 1]
-        list.extend(stack[mark + 1:])
+        list_obj = stack[mark - 1]
+        items = stack[mark + 1:]
+        if isinstance(list_obj, list):
+            list_obj.extend(items)
+        else:
+            append = list_obj.append
+            for item in items:
+                append(item)
         del stack[mark:]
     dispatch[APPENDS[0]] = load_appends
 
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -1214,6 +1214,29 @@
         dumped = b'\x80\x03X\x01\x00\x00\x00ar\xff\xff\xff\xff.'
         self.assertRaises(ValueError, self.loads, dumped)
 
+    def _check_pickling_with_opcode(self, obj, opcode, proto):
+        pickled = self.dumps(obj, proto)
+        self.assertTrue(opcode_in_pickle(opcode, pickled))
+        unpickled = self.loads(pickled)
+        self.assertEqual(obj, unpickled)
+
+    def test_appends_on_non_lists(self):
+        # Issue #17720
+        obj = REX_six([1, 2, 3])
+        for proto in protocols:
+            if proto == 0:
+                self._check_pickling_with_opcode(obj, pickle.APPEND, proto)
+            else:
+                self._check_pickling_with_opcode(obj, pickle.APPENDS, proto)
+
+    def test_setitems_on_non_dicts(self):
+        obj = REX_seven({1: -1, 2: -2, 3: -3})
+        for proto in protocols:
+            if proto == 0:
+                self._check_pickling_with_opcode(obj, pickle.SETITEM, proto)
+            else:
+                self._check_pickling_with_opcode(obj, pickle.SETITEMS, proto)
+
 
 class BigmemPickleTests(unittest.TestCase):
 
@@ -1299,18 +1322,18 @@
 # Test classes for reduce_ex
 
 class REX_one(object):
+    """No __reduce_ex__ here, but inheriting it from object"""
     _reduce_called = 0
     def __reduce__(self):
         self._reduce_called = 1
         return REX_one, ()
-    # No __reduce_ex__ here, but inheriting it from object
 
 class REX_two(object):
+    """No __reduce__ here, but inheriting it from object"""
     _proto = None
     def __reduce_ex__(self, proto):
         self._proto = proto
         return REX_two, ()
-    # No __reduce__ here, but inheriting it from object
 
 class REX_three(object):
     _proto = None
@@ -1321,18 +1344,45 @@
         raise TestFailed("This __reduce__ shouldn't be called")
 
 class REX_four(object):
+    """Calling base class method should succeed"""
     _proto = None
     def __reduce_ex__(self, proto):
         self._proto = proto
         return object.__reduce_ex__(self, proto)
-    # Calling base class method should succeed
 
 class REX_five(object):
+    """This one used to fail with infinite recursion"""
     _reduce_called = 0
     def __reduce__(self):
         self._reduce_called = 1
         return object.__reduce__(self)
-    # This one used to fail with infinite recursion
+
+class REX_six(object):
+    """This class is used to check the 4th argument (list iterator) of the reduce
+    protocol.
+    """
+    def __init__(self, items=None):
+        self.items = items if items is not None else []
+    def __eq__(self, other):
+        return type(self) is type(other) and self.items == self.items
+    def append(self, item):
+        self.items.append(item)
+    def __reduce__(self):
+        return type(self), (), None, iter(self.items), None
+
+class REX_seven(object):
+    """This class is used to check the 5th argument (dict iterator) of the reduce
+    protocol.
+    """
+    def __init__(self, table=None):
+        self.table = table if table is not None else {}
+    def __eq__(self, other):
+        return type(self) is type(other) and self.table == self.table
+    def __setitem__(self, key, value):
+        self.table[key] = value
+    def __reduce__(self):
+        return type(self), (), None, None, iter(self.table.items())
+
 
 # Test classes for newobj
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -42,6 +42,9 @@
 - Issue #17707: multiprocessing.Queue's get() method does not block for short
   timeouts.
 
+- Isuse #17720: Fix the Python implementation of pickle.Unpickler to correctly
+  process the APPENDS opcode when it is used on non-list objects.
+
 - Issue #17012: shutil.which() no longer fallbacks to the PATH environment
   variable if empty path argument is specified.  Patch by Serhiy Storchaka.
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list