mirror of
https://github.com/python/cpython.git
synced 2025-10-17 12:18:23 +00:00
Issue #25945: Fixed bugs in functools.partial.
Fixed a crash when unpickle the functools.partial object with wrong state. Fixed a leak in failed functools.partial constructor. "args" and "keywords" attributes of functools.partial have now always types tuple and dict correspondingly.
This commit is contained in:
parent
567d513b9b
commit
3874128519
3 changed files with 160 additions and 66 deletions
|
@ -30,6 +30,16 @@ def signature(part):
|
|||
""" return the signature of a partial object """
|
||||
return (part.func, part.args, part.keywords, part.__dict__)
|
||||
|
||||
class MyTuple(tuple):
|
||||
pass
|
||||
|
||||
class BadTuple(tuple):
|
||||
def __add__(self, other):
|
||||
return list(self) + list(other)
|
||||
|
||||
class MyDict(dict):
|
||||
pass
|
||||
|
||||
|
||||
class TestPartial:
|
||||
|
||||
|
@ -208,11 +218,84 @@ class TestPartialC(TestPartial, unittest.TestCase):
|
|||
for kwargs_repr in kwargs_reprs])
|
||||
|
||||
def test_pickle(self):
|
||||
f = self.partial(signature, 'asdf', bar=True)
|
||||
f.add_something_to__dict__ = True
|
||||
f = self.partial(signature, ['asdf'], bar=[True])
|
||||
f.attr = []
|
||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||
f_copy = pickle.loads(pickle.dumps(f, proto))
|
||||
self.assertEqual(signature(f), signature(f_copy))
|
||||
self.assertEqual(signature(f_copy), signature(f))
|
||||
|
||||
def test_copy(self):
|
||||
f = self.partial(signature, ['asdf'], bar=[True])
|
||||
f.attr = []
|
||||
f_copy = copy.copy(f)
|
||||
self.assertEqual(signature(f_copy), signature(f))
|
||||
self.assertIs(f_copy.attr, f.attr)
|
||||
self.assertIs(f_copy.args, f.args)
|
||||
self.assertIs(f_copy.keywords, f.keywords)
|
||||
|
||||
def test_deepcopy(self):
|
||||
f = self.partial(signature, ['asdf'], bar=[True])
|
||||
f.attr = []
|
||||
f_copy = copy.deepcopy(f)
|
||||
self.assertEqual(signature(f_copy), signature(f))
|
||||
self.assertIsNot(f_copy.attr, f.attr)
|
||||
self.assertIsNot(f_copy.args, f.args)
|
||||
self.assertIsNot(f_copy.args[0], f.args[0])
|
||||
self.assertIsNot(f_copy.keywords, f.keywords)
|
||||
self.assertIsNot(f_copy.keywords['bar'], f.keywords['bar'])
|
||||
|
||||
def test_setstate(self):
|
||||
f = self.partial(signature)
|
||||
f.__setstate__((capture, (1,), dict(a=10), dict(attr=[])))
|
||||
self.assertEqual(signature(f),
|
||||
(capture, (1,), dict(a=10), dict(attr=[])))
|
||||
self.assertEqual(f(2, b=20), ((1, 2), {'a': 10, 'b': 20}))
|
||||
|
||||
f.__setstate__((capture, (1,), dict(a=10), None))
|
||||
self.assertEqual(signature(f), (capture, (1,), dict(a=10), {}))
|
||||
self.assertEqual(f(2, b=20), ((1, 2), {'a': 10, 'b': 20}))
|
||||
|
||||
f.__setstate__((capture, (1,), None, None))
|
||||
#self.assertEqual(signature(f), (capture, (1,), {}, {}))
|
||||
self.assertEqual(f(2, b=20), ((1, 2), {'b': 20}))
|
||||
self.assertEqual(f(2), ((1, 2), {}))
|
||||
self.assertEqual(f(), ((1,), {}))
|
||||
|
||||
f.__setstate__((capture, (), {}, None))
|
||||
self.assertEqual(signature(f), (capture, (), {}, {}))
|
||||
self.assertEqual(f(2, b=20), ((2,), {'b': 20}))
|
||||
self.assertEqual(f(2), ((2,), {}))
|
||||
self.assertEqual(f(), ((), {}))
|
||||
|
||||
def test_setstate_errors(self):
|
||||
f = self.partial(signature)
|
||||
self.assertRaises(TypeError, f.__setstate__, (capture, (), {}))
|
||||
self.assertRaises(TypeError, f.__setstate__, (capture, (), {}, {}, None))
|
||||
self.assertRaises(TypeError, f.__setstate__, [capture, (), {}, None])
|
||||
self.assertRaises(TypeError, f.__setstate__, (None, (), {}, None))
|
||||
self.assertRaises(TypeError, f.__setstate__, (capture, None, {}, None))
|
||||
self.assertRaises(TypeError, f.__setstate__, (capture, [], {}, None))
|
||||
self.assertRaises(TypeError, f.__setstate__, (capture, (), [], None))
|
||||
|
||||
def test_setstate_subclasses(self):
|
||||
f = self.partial(signature)
|
||||
f.__setstate__((capture, MyTuple((1,)), MyDict(a=10), None))
|
||||
s = signature(f)
|
||||
self.assertEqual(s, (capture, (1,), dict(a=10), {}))
|
||||
self.assertIs(type(s[1]), tuple)
|
||||
self.assertIs(type(s[2]), dict)
|
||||
r = f()
|
||||
self.assertEqual(r, ((1,), {'a': 10}))
|
||||
self.assertIs(type(r[0]), tuple)
|
||||
self.assertIs(type(r[1]), dict)
|
||||
|
||||
f.__setstate__((capture, BadTuple((1,)), {}, None))
|
||||
s = signature(f)
|
||||
self.assertEqual(s, (capture, (1,), {}, {}))
|
||||
self.assertIs(type(s[1]), tuple)
|
||||
r = f(2)
|
||||
self.assertEqual(r, ((1, 2), {}))
|
||||
self.assertIs(type(r[0]), tuple)
|
||||
|
||||
# Issue 6083: Reference counting bug
|
||||
def test_setstate_refcount(self):
|
||||
|
@ -229,9 +312,7 @@ class TestPartialC(TestPartial, unittest.TestCase):
|
|||
raise IndexError
|
||||
|
||||
f = self.partial(object)
|
||||
self.assertRaisesRegex(SystemError,
|
||||
"new style getargs format but argument is not a tuple",
|
||||
f.__setstate__, BadSequence())
|
||||
self.assertRaises(TypeError, f.__setstate__, BadSequence())
|
||||
|
||||
|
||||
class TestPartialPy(TestPartial, unittest.TestCase):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue