diff -r 628bd1ebfa22 Include/object.h --- a/Include/object.h Fri Mar 18 03:03:10 2016 +0000 +++ b/Include/object.h Thu Mar 17 09:38:56 2016 +0200 @@ -578,6 +578,11 @@ PyAPI_FUNC(PyObject *) */ PyAPI_FUNC(PyObject *) PyObject_Dir(PyObject *); +/* Pickle support. */ +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _PyObject_GetState(PyObject *); +#endif + /* Helpers for printing recursive container types */ PyAPI_FUNC(int) Py_ReprEnter(PyObject *); diff -r 628bd1ebfa22 Lib/_weakrefset.py --- a/Lib/_weakrefset.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/_weakrefset.py Thu Mar 17 09:38:56 2016 +0200 @@ -3,6 +3,7 @@ # by abc.py to load everything else at startup. from _weakref import ref +from copyreg import _getstate __all__ = ['WeakSet'] @@ -75,8 +76,7 @@ class WeakSet: return wr in self.data def __reduce__(self): - return (self.__class__, (list(self),), - getattr(self, '__dict__', None)) + return self.__class__, (list(self),), _getstate(self) def add(self, item): if self._pending_removals: diff -r 628bd1ebfa22 Lib/collections/__init__.py --- a/Lib/collections/__init__.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/collections/__init__.py Thu Mar 17 09:38:56 2016 +0200 @@ -23,6 +23,7 @@ from _collections_abc import * import _collections_abc __all__ += _collections_abc.__all__ +from copyreg import _getstate from operator import itemgetter as _itemgetter, eq as _eq from keyword import iskeyword as _iskeyword import sys as _sys @@ -260,10 +261,22 @@ class OrderedDict(dict): def __reduce__(self): 'Return state information for pickling' - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - return self.__class__, (), inst_dict or None, None, iter(self.items()) + state = _getstate(self) + if state: + if isinstance(state, tuple): + state, slots = state + else: + slots = {} + state = state.copy() + slots = slots.copy() + for k in vars(OrderedDict()): + state.pop(k, None) + slots.pop(k, None) + if slots: + state = state, slots + else: + state = state or None + return self.__class__, (), state, None, iter(self.items()) def copy(self): 'od.copy() -> a shallow copy of od' diff -r 628bd1ebfa22 Lib/copyreg.py --- a/Lib/copyreg.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/copyreg.py Thu Mar 17 09:38:56 2016 +0200 @@ -140,6 +140,27 @@ def _slotnames(cls): return names +def _getstate(obj): + try: + getstate = obj.__getstate__ + except AttributeError: + pass + else: + return getstate() + + state = getattr(obj, '__dict__', None) or None + slotnames = _slotnames(type(obj)) + if slotnames: + slots = {} + for name in slotnames: + try: + slots[name] = getattr(obj, name) + except AttributeError: + pass # the attribute is not present + if slots: + state = state, slots + return state + # A registry of extension codes. This is an ad-hoc compression # mechanism. Whenever a global reference to , is about # to be pickled, the (, ) tuple is looked up here to see diff -r 628bd1ebfa22 Lib/datetime.py --- a/Lib/datetime.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/datetime.py Thu Mar 17 09:38:56 2016 +0200 @@ -6,6 +6,7 @@ time zone and DST data sources. import time as _time import math as _math +from copyreg import _getstate def _cmp(x, y): return 0 if x == y else 1 if x > y else -1 @@ -1005,15 +1006,7 @@ class tzinfo: args = getinitargs() else: args = () - getstate = getattr(self, "__getstate__", None) - if getstate: - state = getstate() - else: - state = getattr(self, "__dict__", None) or None - if state is None: - return (self.__class__, args) - else: - return (self.__class__, args, state) + return (self.__class__, args, _getstate(self)) _tzinfo_class = tzinfo diff -r 628bd1ebfa22 Lib/email/headerregistry.py --- a/Lib/email/headerregistry.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/email/headerregistry.py Thu Mar 17 09:38:56 2016 +0200 @@ -12,6 +12,7 @@ from types import MappingProxyType from email import utils from email import errors from email import _header_value_parser as parser +from copyreg import _getstate class Address: @@ -223,7 +224,7 @@ class BaseHeader(str): self.__class__.__bases__, str(self), ), - self.__dict__) + _getstate(self)) @classmethod def _reconstruct(cls, value): diff -r 628bd1ebfa22 Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/test/datetimetester.py Thu Mar 17 09:38:56 2016 +0200 @@ -117,6 +117,9 @@ class PicklableFixedOffset(FixedOffset): def __init__(self, offset=None, name=None, dstoffset=None): FixedOffset.__init__(self, offset, name, dstoffset) +class PicklableFixedOffsetWithSlots(PicklableFixedOffset): + __slots__ = '_FixedOffset__offset', '_FixedOffset__name', 'spam' + class _TZInfo(tzinfo): def utcoffset(self, datetime_module): return random.random() @@ -177,6 +180,7 @@ class TestTZInfo(unittest.TestCase): offset = timedelta(minutes=-300) for otype, args in [ (PicklableFixedOffset, (offset, 'cookie')), + (PicklableFixedOffsetWithSlots, (offset, 'cookie')), (timezone, (offset,)), (timezone, (offset, "EST"))]: orig = otype(*args) @@ -192,6 +196,7 @@ class TestTZInfo(unittest.TestCase): self.assertIs(type(derived), otype) self.assertEqual(derived.utcoffset(None), offset) self.assertEqual(derived.tzname(None), oname) + self.assertFalse(hasattr(derived, 'spam')) def test_issue23600(self): DSTDIFF = DSTOFFSET = timedelta(hours=1) diff -r 628bd1ebfa22 Lib/test/test_bytes.py --- a/Lib/test/test_bytes.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/test/test_bytes.py Thu Mar 17 09:38:56 2016 +0200 @@ -1654,33 +1654,38 @@ class SubclassTest: def test_pickle(self): a = self.subclass2test(b"abcd") a.x = 10 - a.y = self.subclass2test(b"efgh") + a.z = self.subclass2test(b"efgh") for proto in range(pickle.HIGHEST_PROTOCOL + 1): b = pickle.loads(pickle.dumps(a, proto)) self.assertNotEqual(id(a), id(b)) self.assertEqual(a, b) self.assertEqual(a.x, b.x) - self.assertEqual(a.y, b.y) + self.assertEqual(a.z, b.z) self.assertEqual(type(a), type(b)) - self.assertEqual(type(a.y), type(b.y)) + self.assertEqual(type(a.z), type(b.z)) + self.assertFalse(hasattr(b, 'y')) def test_copy(self): a = self.subclass2test(b"abcd") a.x = 10 - a.y = self.subclass2test(b"efgh") + a.z = self.subclass2test(b"efgh") for copy_method in (copy.copy, copy.deepcopy): b = copy_method(a) self.assertNotEqual(id(a), id(b)) self.assertEqual(a, b) self.assertEqual(a.x, b.x) - self.assertEqual(a.y, b.y) + self.assertEqual(a.z, b.z) self.assertEqual(type(a), type(b)) - self.assertEqual(type(a.y), type(b.y)) + self.assertEqual(type(a.z), type(b.z)) + self.assertFalse(hasattr(b, 'y')) class ByteArraySubclass(bytearray): pass +class ByteArraySubclassWithSlots(bytearray): + __slots__ = ('x', 'z', '__dict__') + class BytesSubclass(bytes): pass @@ -1701,6 +1706,9 @@ class ByteArraySubclassTest(SubclassTest x = subclass(newarg=4, source=b"abcd") self.assertEqual(x, b"abcd") +class ByteArraySubclassWithSlotsTest(SubclassTest, unittest.TestCase): + type2test = bytearray + subclass2test = ByteArraySubclassWithSlots class BytesSubclassTest(SubclassTest, unittest.TestCase): type2test = bytes diff -r 628bd1ebfa22 Lib/test/test_deque.py --- a/Lib/test/test_deque.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/test/test_deque.py Thu Mar 17 09:38:56 2016 +0200 @@ -792,6 +792,9 @@ class TestVariousIteratorArgs(unittest.T class Deque(deque): pass +class DequeWithSlots(deque): + __slots__ = ('x', 'y', '__dict__') + class DequeWithBadIter(deque): def __iter__(self): raise TypeError @@ -821,40 +824,28 @@ class TestSubclass(unittest.TestCase): self.assertEqual(len(d), 0) def test_copy_pickle(self): + for cls in Deque, DequeWithSlots: + for d in cls('abc'), cls('abcde', maxlen=4): + d.x = ['x'] + d.z = ['z'] - d = Deque('abc') + e = d.__copy__() + self.assertEqual(type(d), type(e)) + self.assertEqual(list(d), list(e)) - e = d.__copy__() - self.assertEqual(type(d), type(e)) - self.assertEqual(list(d), list(e)) + e = cls(d) + self.assertEqual(type(d), type(e)) + self.assertEqual(list(d), list(e)) - e = Deque(d) - self.assertEqual(type(d), type(e)) - self.assertEqual(list(d), list(e)) - - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - s = pickle.dumps(d, proto) - e = pickle.loads(s) - self.assertNotEqual(id(d), id(e)) - self.assertEqual(type(d), type(e)) - self.assertEqual(list(d), list(e)) - - d = Deque('abcde', maxlen=4) - - e = d.__copy__() - self.assertEqual(type(d), type(e)) - self.assertEqual(list(d), list(e)) - - e = Deque(d) - self.assertEqual(type(d), type(e)) - self.assertEqual(list(d), list(e)) - - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - s = pickle.dumps(d, proto) - e = pickle.loads(s) - self.assertNotEqual(id(d), id(e)) - self.assertEqual(type(d), type(e)) - self.assertEqual(list(d), list(e)) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + s = pickle.dumps(d, proto) + e = pickle.loads(s) + self.assertNotEqual(id(d), id(e)) + self.assertEqual(type(d), type(e)) + self.assertEqual(list(d), list(e)) + self.assertEqual(e.x, d.x) + self.assertEqual(e.z, d.z) + self.assertFalse(hasattr(e, 'y')) def test_pickle_recursive(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): diff -r 628bd1ebfa22 Lib/test/test_ordered_dict.py --- a/Lib/test/test_ordered_dict.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/test/test_ordered_dict.py Thu Mar 17 09:38:56 2016 +0200 @@ -262,6 +262,8 @@ class OrderedDictTests: # and have a repr/eval round-trip pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)] od = OrderedDict(pairs) + od.x = ['x'] + od.z = ['z'] def check(dup): msg = "\ncopy: %s\nod: %s" % (dup, od) self.assertIsNot(dup, od, msg) @@ -270,13 +272,27 @@ class OrderedDictTests: self.assertEqual(len(dup), len(od)) self.assertEqual(type(dup), type(od)) check(od.copy()) - check(copy.copy(od)) - check(copy.deepcopy(od)) + dup = copy.copy(od) + check(dup) + self.assertIs(dup.x, od.x) + self.assertIs(dup.z, od.z) + self.assertFalse(hasattr(dup, 'y')) + dup = copy.deepcopy(od) + check(dup) + self.assertEqual(dup.x, od.x) + self.assertIsNot(dup.x, od.x) + self.assertEqual(dup.z, od.z) + self.assertIsNot(dup.z, od.z) + self.assertFalse(hasattr(dup, 'y')) # pickle directly pulls the module, so we have to fake it with replaced_module('collections', self.module): for proto in range(pickle.HIGHEST_PROTOCOL + 1): with self.subTest(proto=proto): - check(pickle.loads(pickle.dumps(od, proto))) + dup = pickle.loads(pickle.dumps(od, proto)) + check(dup) + self.assertEqual(dup.x, od.x) + self.assertEqual(dup.z, od.z) + self.assertFalse(hasattr(dup, 'y')) check(eval(repr(od))) update_test = OrderedDict() update_test.update(od) @@ -684,6 +700,23 @@ class CPythonOrderedDictSubclassTests(CP pass +class PurePythonOrderedDictWithSlotsCopyingTests(unittest.TestCase): + + module = py_coll + class OrderedDict(py_coll.OrderedDict): + __slots__ = ('x', 'y') + test_copying = OrderedDictTests.test_copying + + +@unittest.skipUnless(c_coll, 'requires the C version of the collections module') +class CPythonOrderedDictWithSlotsCopyingTests(unittest.TestCase): + + module = c_coll + class OrderedDict(c_coll.OrderedDict): + __slots__ = ('x', 'y') + test_copying = OrderedDictTests.test_copying + + class PurePythonGeneralMappingTests(mapping_tests.BasicTestMappingProtocol): @classmethod diff -r 628bd1ebfa22 Lib/test/test_set.py --- a/Lib/test/test_set.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/test/test_set.py Thu Mar 17 09:38:56 2016 +0200 @@ -228,14 +228,17 @@ class TestJointOps: def test_pickling(self): for i in range(pickle.HIGHEST_PROTOCOL + 1): + if type(self.s) not in (set, frozenset): + self.s.x = ['x'] + self.s.z = ['z'] p = pickle.dumps(self.s, i) dup = pickle.loads(p) self.assertEqual(self.s, dup, "%s != %s" % (self.s, dup)) if type(self.s) not in (set, frozenset): - self.s.x = 10 - p = pickle.dumps(self.s, i) - dup = pickle.loads(p) self.assertEqual(self.s.x, dup.x) + self.assertEqual(self.s.z, dup.z) + self.assertFalse(hasattr(self.s, 'y')) + del self.s.x, self.s.z def test_iterator_pickling(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): @@ -767,6 +770,21 @@ class TestFrozenSetSubclass(TestFrozenSe # All empty frozenset subclass instances should have different ids self.assertEqual(len(set(map(id, efs))), len(efs)) + +class SetSubclassWithSlots(set): + __slots__ = ('x', 'y', '__dict__') + +class TestSetSubclassWithSlots(unittest.TestCase): + thetype = SetSubclassWithSlots + setUp = TestJointOps.setUp + test_pickling = TestJointOps.test_pickling + +class FrozenSetSubclassWithSlots(frozenset): + __slots__ = ('x', 'y', '__dict__') + +class TestFrozenSetSubclassWithSlots(TestSetSubclassWithSlots): + thetype = FrozenSetSubclassWithSlots + # Tests taken from test_sets.py ============================================= empty_set = set() diff -r 628bd1ebfa22 Lib/test/test_weakset.py --- a/Lib/test/test_weakset.py Fri Mar 18 03:03:10 2016 +0000 +++ b/Lib/test/test_weakset.py Thu Mar 17 09:38:56 2016 +0200 @@ -20,6 +20,12 @@ class RefCycle: def __init__(self): self.cycle = self +class WeakSetSubclass(WeakSet): + pass + +class WeakSetWithSlots(WeakSet): + __slots__ = ('x', 'y') + class TestWeakSet(unittest.TestCase): @@ -441,6 +447,30 @@ class TestWeakSet(unittest.TestCase): self.assertGreaterEqual(n2, 0) self.assertLessEqual(n2, n1) + def test_copying(self): + for cls in WeakSet, WeakSetWithSlots: + s = cls(self.items) + s.x = ['x'] + s.z = ['z'] + + dup = copy.copy(s) + self.assertIsInstance(dup, cls) + self.assertEqual(dup, s) + self.assertIsNot(dup, s) + self.assertIs(dup.x, s.x) + self.assertIs(dup.z, s.z) + self.assertFalse(hasattr(dup, 'y')) + + dup = copy.deepcopy(s) + self.assertIsInstance(dup, cls) + self.assertEqual(dup, s) + self.assertIsNot(dup, s) + self.assertEqual(dup.x, s.x) + self.assertIsNot(dup.x, s.x) + self.assertEqual(dup.z, s.z) + self.assertIsNot(dup.z, s.z) + self.assertFalse(hasattr(dup, 'y')) + if __name__ == "__main__": unittest.main() diff -r 628bd1ebfa22 Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c Fri Mar 18 03:03:10 2016 +0000 +++ b/Modules/_collectionsmodule.c Thu Mar 17 09:38:56 2016 +0200 @@ -1296,30 +1296,24 @@ deque_traverse(dequeobject *deque, visit static PyObject * deque_reduce(dequeobject *deque) { - PyObject *dict, *it; - _Py_IDENTIFIER(__dict__); + PyObject *state, *it; - dict = _PyObject_GetAttrId((PyObject *)deque, &PyId___dict__); - if (dict == NULL) { - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { - return NULL; - } - PyErr_Clear(); - dict = Py_None; - Py_INCREF(dict); + state = _PyObject_GetState((PyObject *)deque); + if (state == NULL) { + return NULL; } it = PyObject_GetIter((PyObject *)deque); if (it == NULL) { - Py_DECREF(dict); + Py_DECREF(state); return NULL; } if (deque->maxlen < 0) { - return Py_BuildValue("O()NN", Py_TYPE(deque), dict, it); + return Py_BuildValue("O()NN", Py_TYPE(deque), state, it); } else { - return Py_BuildValue("O(()n)NN", Py_TYPE(deque), deque->maxlen, dict, it); + return Py_BuildValue("O(()n)NN", Py_TYPE(deque), deque->maxlen, state, it); } } diff -r 628bd1ebfa22 Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Fri Mar 18 03:03:10 2016 +0000 +++ b/Modules/_datetimemodule.c Thu Mar 17 09:38:56 2016 +0200 @@ -3077,9 +3077,8 @@ static PyObject * tzinfo_reduce(PyObject *self) { PyObject *args, *state, *tmp; - PyObject *getinitargs, *getstate; + PyObject *getinitargs; _Py_IDENTIFIER(__getinitargs__); - _Py_IDENTIFIER(__getstate__); tmp = PyTuple_New(0); if (tmp == NULL) @@ -3100,34 +3099,13 @@ tzinfo_reduce(PyObject *self) Py_INCREF(args); } - getstate = _PyObject_GetAttrId(self, &PyId___getstate__); - if (getstate != NULL) { - state = PyObject_CallObject(getstate, tmp); - Py_DECREF(getstate); - if (state == NULL) { - Py_DECREF(args); - Py_DECREF(tmp); - return NULL; - } + state = _PyObject_GetState(self); + if (state == NULL) { + Py_DECREF(args); + return NULL; } - else { - PyObject **dictptr; - PyErr_Clear(); - state = Py_None; - dictptr = _PyObject_GetDictPtr(self); - if (dictptr && *dictptr && PyDict_Size(*dictptr)) - state = *dictptr; - Py_INCREF(state); - } - - Py_DECREF(tmp); - - if (state == Py_None) { - Py_DECREF(state); - return Py_BuildValue("(ON)", Py_TYPE(self), args); - } - else - return Py_BuildValue("(ONN)", Py_TYPE(self), args, state); + + return Py_BuildValue("(ONN)", Py_TYPE(self), args, state); } static PyMethodDef tzinfo_methods[] = { diff -r 628bd1ebfa22 Objects/bytearrayobject.c --- a/Objects/bytearrayobject.c Fri Mar 18 03:03:10 2016 +0000 +++ b/Objects/bytearrayobject.c Thu Mar 17 09:38:56 2016 +0200 @@ -2826,35 +2826,26 @@ bytearray_hex(PyBytesObject *self) static PyObject * _common_reduce(PyByteArrayObject *self, int proto) { - PyObject *dict; - _Py_IDENTIFIER(__dict__); - char *buf; - - dict = _PyObject_GetAttrId((PyObject *)self, &PyId___dict__); - if (dict == NULL) { - PyErr_Clear(); - dict = Py_None; - Py_INCREF(dict); + PyObject *state; + const char *buf; + + state = _PyObject_GetState((PyObject *)self); + if (state == NULL) { + return NULL; } + if (!Py_SIZE(self)) { + return Py_BuildValue("(O()N)", Py_TYPE(self), state); + } buf = PyByteArray_AS_STRING(self); if (proto < 3) { /* use str based reduction for backwards compatibility with Python 2.x */ - PyObject *latin1; - if (Py_SIZE(self)) - latin1 = PyUnicode_DecodeLatin1(buf, Py_SIZE(self), NULL); - else - latin1 = PyUnicode_FromString(""); - return Py_BuildValue("(O(Ns)N)", Py_TYPE(self), latin1, "latin-1", dict); + PyObject *latin1 = PyUnicode_DecodeLatin1(buf, Py_SIZE(self), NULL); + return Py_BuildValue("(O(Ns)N)", Py_TYPE(self), latin1, "latin-1", state); } else { /* use more efficient byte based reduction */ - if (Py_SIZE(self)) { - return Py_BuildValue("(O(y#)N)", Py_TYPE(self), buf, Py_SIZE(self), dict); - } - else { - return Py_BuildValue("(O()N)", Py_TYPE(self), dict); - } + return Py_BuildValue("(O(y#)N)", Py_TYPE(self), buf, Py_SIZE(self), state); } } diff -r 628bd1ebfa22 Objects/odictobject.c --- a/Objects/odictobject.c Fri Mar 18 03:03:10 2016 +0000 +++ b/Objects/odictobject.c Thu Mar 17 09:38:56 2016 +0200 @@ -953,25 +953,14 @@ PyDoc_STRVAR(odict_reduce__doc__, "Retur static PyObject * odict_reduce(register PyODictObject *od) { - _Py_IDENTIFIER(__dict__); _Py_IDENTIFIER(items); - PyObject *dict = NULL, *result = NULL; + PyObject *state, *result = NULL; PyObject *items_iter, *items, *args = NULL; /* capture any instance state */ - dict = _PyObject_GetAttrId((PyObject *)od, &PyId___dict__); - if (dict == NULL) + state = _PyObject_GetState((PyObject *)od); + if (state == NULL) goto Done; - else { - /* od.__dict__ isn't necessarily a dict... */ - Py_ssize_t dict_len = PyObject_Length(dict); - if (dict_len == -1) - goto Done; - if (!dict_len) { - /* nothing to pickle in od.__dict__ */ - Py_CLEAR(dict); - } - } /* build the result */ args = PyTuple_New(0); @@ -987,11 +976,11 @@ odict_reduce(register PyODictObject *od) if (items_iter == NULL) goto Done; - result = PyTuple_Pack(5, Py_TYPE(od), args, dict ? dict : Py_None, Py_None, items_iter); + result = PyTuple_Pack(5, Py_TYPE(od), args, state, Py_None, items_iter); Py_DECREF(items_iter); Done: - Py_XDECREF(dict); + Py_DECREF(state); Py_XDECREF(args); return result; diff -r 628bd1ebfa22 Objects/setobject.c --- a/Objects/setobject.c Fri Mar 18 03:03:10 2016 +0000 +++ b/Objects/setobject.c Thu Mar 17 09:38:56 2016 +0200 @@ -1973,8 +1973,7 @@ If the element is not a member, do nothi static PyObject * set_reduce(PySetObject *so) { - PyObject *keys=NULL, *args=NULL, *result=NULL, *dict=NULL; - _Py_IDENTIFIER(__dict__); + PyObject *keys=NULL, *args=NULL, *result=NULL, *state=NULL; keys = PySequence_List((PyObject *)so); if (keys == NULL) @@ -1982,17 +1981,14 @@ set_reduce(PySetObject *so) args = PyTuple_Pack(1, keys); if (args == NULL) goto done; - dict = _PyObject_GetAttrId((PyObject *)so, &PyId___dict__); - if (dict == NULL) { - PyErr_Clear(); - dict = Py_None; - Py_INCREF(dict); - } - result = PyTuple_Pack(3, Py_TYPE(so), args, dict); + state = _PyObject_GetState((PyObject *)so); + if (state == NULL) + goto done; + result = PyTuple_Pack(3, Py_TYPE(so), args, state); done: Py_XDECREF(args); Py_XDECREF(keys); - Py_XDECREF(dict); + Py_XDECREF(state); return result; } diff -r 628bd1ebfa22 Objects/typeobject.c --- a/Objects/typeobject.c Fri Mar 18 03:03:10 2016 +0000 +++ b/Objects/typeobject.c Thu Mar 17 09:38:56 2016 +0200 @@ -3826,7 +3826,7 @@ Py_LOCAL(PyObject *) } Py_LOCAL(PyObject *) -_PyObject_GetState(PyObject *obj, int required) +getstate(PyObject *obj, int required) { PyObject *state; PyObject *getstate; @@ -3971,6 +3971,12 @@ Py_LOCAL(PyObject *) return state; } +PyObject * +_PyObject_GetState(PyObject *obj) +{ + return getstate(obj, 0); +} + Py_LOCAL(int) _PyObject_GetNewArguments(PyObject *obj, PyObject **args, PyObject **kwargs) { @@ -4185,8 +4191,7 @@ reduce_newobj(PyObject *obj) } } - state = _PyObject_GetState(obj, - !hasargs && !PyList_Check(obj) && !PyDict_Check(obj)); + state = getstate(obj, !hasargs && !PyList_Check(obj) && !PyDict_Check(obj)); if (state == NULL) { Py_DECREF(newobj); Py_DECREF(newargs);