Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1f7492c

Browse files
committed
Isuse #17720: Fix APPENDS handling in the Python implementation of Unpickler
to correctly process the opcode when it is used on non-list objects.
1 parent bdf940d commit 1f7492c

3 files changed

Lines changed: 65 additions & 6 deletions

File tree

Lib/pickle.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,8 +1208,14 @@ def load_append(self):
12081208
def load_appends(self):
12091209
stack = self.stack
12101210
mark = self.marker()
1211-
list = stack[mark - 1]
1212-
list.extend(stack[mark + 1:])
1211+
list_obj = stack[mark - 1]
1212+
items = stack[mark + 1:]
1213+
if isinstance(list_obj, list):
1214+
list_obj.extend(items)
1215+
else:
1216+
append = list_obj.append
1217+
for item in items:
1218+
append(item)
12131219
del stack[mark:]
12141220
dispatch[APPENDS[0]] = load_appends
12151221

Lib/test/pickletester.py

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,29 @@ def test_negative_32b_binput(self):
12141214
dumped = b'\x80\x03X\x01\x00\x00\x00ar\xff\xff\xff\xff.'
12151215
self.assertRaises(ValueError, self.loads, dumped)
12161216

1217+
def _check_pickling_with_opcode(self, obj, opcode, proto):
1218+
pickled = self.dumps(obj, proto)
1219+
self.assertTrue(opcode_in_pickle(opcode, pickled))
1220+
unpickled = self.loads(pickled)
1221+
self.assertEqual(obj, unpickled)
1222+
1223+
def test_appends_on_non_lists(self):
1224+
# Issue #17720
1225+
obj = REX_six([1, 2, 3])
1226+
for proto in protocols:
1227+
if proto == 0:
1228+
self._check_pickling_with_opcode(obj, pickle.APPEND, proto)
1229+
else:
1230+
self._check_pickling_with_opcode(obj, pickle.APPENDS, proto)
1231+
1232+
def test_setitems_on_non_dicts(self):
1233+
obj = REX_seven({1: -1, 2: -2, 3: -3})
1234+
for proto in protocols:
1235+
if proto == 0:
1236+
self._check_pickling_with_opcode(obj, pickle.SETITEM, proto)
1237+
else:
1238+
self._check_pickling_with_opcode(obj, pickle.SETITEMS, proto)
1239+
12171240

12181241
class BigmemPickleTests(unittest.TestCase):
12191242

@@ -1299,18 +1322,18 @@ def test_huge_str_64b(self, size):
12991322
# Test classes for reduce_ex
13001323

13011324
class REX_one(object):
1325+
"""No __reduce_ex__ here, but inheriting it from object"""
13021326
_reduce_called = 0
13031327
def __reduce__(self):
13041328
self._reduce_called = 1
13051329
return REX_one, ()
1306-
# No __reduce_ex__ here, but inheriting it from object
13071330

13081331
class REX_two(object):
1332+
"""No __reduce__ here, but inheriting it from object"""
13091333
_proto = None
13101334
def __reduce_ex__(self, proto):
13111335
self._proto = proto
13121336
return REX_two, ()
1313-
# No __reduce__ here, but inheriting it from object
13141337

13151338
class REX_three(object):
13161339
_proto = None
@@ -1321,18 +1344,45 @@ def __reduce__(self):
13211344
raise TestFailed("This __reduce__ shouldn't be called")
13221345

13231346
class REX_four(object):
1347+
"""Calling base class method should succeed"""
13241348
_proto = None
13251349
def __reduce_ex__(self, proto):
13261350
self._proto = proto
13271351
return object.__reduce_ex__(self, proto)
1328-
# Calling base class method should succeed
13291352

13301353
class REX_five(object):
1354+
"""This one used to fail with infinite recursion"""
13311355
_reduce_called = 0
13321356
def __reduce__(self):
13331357
self._reduce_called = 1
13341358
return object.__reduce__(self)
1335-
# This one used to fail with infinite recursion
1359+
1360+
class REX_six(object):
1361+
"""This class is used to check the 4th argument (list iterator) of the reduce
1362+
protocol.
1363+
"""
1364+
def __init__(self, items=None):
1365+
self.items = items if items is not None else []
1366+
def __eq__(self, other):
1367+
return type(self) is type(other) and self.items == self.items
1368+
def append(self, item):
1369+
self.items.append(item)
1370+
def __reduce__(self):
1371+
return type(self), (), None, iter(self.items), None
1372+
1373+
class REX_seven(object):
1374+
"""This class is used to check the 5th argument (dict iterator) of the reduce
1375+
protocol.
1376+
"""
1377+
def __init__(self, table=None):
1378+
self.table = table if table is not None else {}
1379+
def __eq__(self, other):
1380+
return type(self) is type(other) and self.table == self.table
1381+
def __setitem__(self, key, value):
1382+
self.table[key] = value
1383+
def __reduce__(self):
1384+
return type(self), (), None, None, iter(self.table.items())
1385+
13361386

13371387
# Test classes for newobj
13381388

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ Library
4242
- Issue #17707: multiprocessing.Queue's get() method does not block for short
4343
timeouts.
4444

45+
- Isuse #17720: Fix the Python implementation of pickle.Unpickler to correctly
46+
process the APPENDS opcode when it is used on non-list objects.
47+
4548
- Issue #17012: shutil.which() no longer fallbacks to the PATH environment
4649
variable if empty path argument is specified. Patch by Serhiy Storchaka.
4750

0 commit comments

Comments
 (0)