Commit 1f7492c2 authored by Alexandre Vassalotti's avatar Alexandre Vassalotti

Isuse #17720: Fix APPENDS handling in the Python implementation of Unpickler

to correctly process the opcode when it is used on non-list objects.
parent bdf940d3
...@@ -1208,8 +1208,14 @@ class _Unpickler: ...@@ -1208,8 +1208,14 @@ class _Unpickler:
def load_appends(self): def load_appends(self):
stack = self.stack stack = self.stack
mark = self.marker() mark = self.marker()
list = stack[mark - 1] list_obj = stack[mark - 1]
list.extend(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:] del stack[mark:]
dispatch[APPENDS[0]] = load_appends dispatch[APPENDS[0]] = load_appends
......
...@@ -1214,6 +1214,29 @@ class AbstractPickleTests(unittest.TestCase): ...@@ -1214,6 +1214,29 @@ class AbstractPickleTests(unittest.TestCase):
dumped = b'\x80\x03X\x01\x00\x00\x00ar\xff\xff\xff\xff.' dumped = b'\x80\x03X\x01\x00\x00\x00ar\xff\xff\xff\xff.'
self.assertRaises(ValueError, self.loads, dumped) 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): class BigmemPickleTests(unittest.TestCase):
...@@ -1299,18 +1322,18 @@ class BigmemPickleTests(unittest.TestCase): ...@@ -1299,18 +1322,18 @@ class BigmemPickleTests(unittest.TestCase):
# Test classes for reduce_ex # Test classes for reduce_ex
class REX_one(object): class REX_one(object):
"""No __reduce_ex__ here, but inheriting it from object"""
_reduce_called = 0 _reduce_called = 0
def __reduce__(self): def __reduce__(self):
self._reduce_called = 1 self._reduce_called = 1
return REX_one, () return REX_one, ()
# No __reduce_ex__ here, but inheriting it from object
class REX_two(object): class REX_two(object):
"""No __reduce__ here, but inheriting it from object"""
_proto = None _proto = None
def __reduce_ex__(self, proto): def __reduce_ex__(self, proto):
self._proto = proto self._proto = proto
return REX_two, () return REX_two, ()
# No __reduce__ here, but inheriting it from object
class REX_three(object): class REX_three(object):
_proto = None _proto = None
...@@ -1321,18 +1344,45 @@ class REX_three(object): ...@@ -1321,18 +1344,45 @@ class REX_three(object):
raise TestFailed("This __reduce__ shouldn't be called") raise TestFailed("This __reduce__ shouldn't be called")
class REX_four(object): class REX_four(object):
"""Calling base class method should succeed"""
_proto = None _proto = None
def __reduce_ex__(self, proto): def __reduce_ex__(self, proto):
self._proto = proto self._proto = proto
return object.__reduce_ex__(self, proto) return object.__reduce_ex__(self, proto)
# Calling base class method should succeed
class REX_five(object): class REX_five(object):
"""This one used to fail with infinite recursion"""
_reduce_called = 0 _reduce_called = 0
def __reduce__(self): def __reduce__(self):
self._reduce_called = 1 self._reduce_called = 1
return object.__reduce__(self) 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 # Test classes for newobj
......
...@@ -42,6 +42,9 @@ Library ...@@ -42,6 +42,9 @@ Library
- Issue #17707: multiprocessing.Queue's get() method does not block for short - Issue #17707: multiprocessing.Queue's get() method does not block for short
timeouts. 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 - Issue #17012: shutil.which() no longer fallbacks to the PATH environment
variable if empty path argument is specified. Patch by Serhiy Storchaka. variable if empty path argument is specified. Patch by Serhiy Storchaka.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment