diff options
-rw-r--r-- | Lib/copy_reg.py | 15 | ||||
-rw-r--r-- | Lib/test/test_descr.py | 35 | ||||
-rw-r--r-- | Objects/typeobject.c | 32 |
3 files changed, 22 insertions, 60 deletions
diff --git a/Lib/copy_reg.py b/Lib/copy_reg.py index f96703e..11ae960 100644 --- a/Lib/copy_reg.py +++ b/Lib/copy_reg.py @@ -58,6 +58,9 @@ def _reduce(self): try: getstate = self.__getstate__ except AttributeError: + if getattr(self, "__slots__", None): + raise TypeError("a class that defines __slots__ without " + "defining __getstate__ cannot be pickled") try: dict = self.__dict__ except AttributeError: @@ -83,16 +86,8 @@ def _better_reduce(obj): args = () getstate = getattr(obj, "__getstate__", None) if getstate: - try: - state = getstate() - except TypeError, err: - # XXX Catch generic exception caused by __slots__ - if str(err) != ("a class that defines __slots__ " - "without defining __getstate__ " - "cannot be pickled"): - raise # Not that specific exception - getstate = None - if not getstate: + state = getstate() + else: state = getattr(obj, "__dict__", None) names = _slotnames(cls) if names: diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 8ef7979..d7368d3 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -2835,7 +2835,7 @@ def pickleslots(): pass else: raise TestFailed, "should fail: cPickle D instance - %s" % base - # Give C a __getstate__ and __setstate__ + # Give C a nice generic __getstate__ and __setstate__ class C(base): __slots__ = ['a'] def __getstate__(self): @@ -2843,10 +2843,12 @@ def pickleslots(): d = self.__dict__.copy() except AttributeError: d = {} - try: - d['a'] = self.a - except AttributeError: - pass + for cls in self.__class__.__mro__: + for sn in cls.__dict__.get('__slots__', ()): + try: + d[sn] = getattr(self, sn) + except AttributeError: + pass return d def __setstate__(self, d): for k, v in d.items(): @@ -2871,21 +2873,18 @@ def pickleslots(): vereq(y.a + y.b, 142) y = cPickle.loads(cPickle.dumps(x)) vereq(y.a + y.b, 142) - # But a subclass that adds a slot should not work + # A subclass that adds a slot should also work class E(C): __slots__ = ['b'] - try: - pickle.dumps(E()) - except TypeError: - pass - else: - raise TestFailed, "should fail: pickle E instance - %s" % base - try: - cPickle.dumps(E()) - except TypeError: - pass - else: - raise TestFailed, "should fail: cPickle E instance - %s" % base + x = E() + x.a = 42 + x.b = "foo" + y = pickle.loads(pickle.dumps(x)) + vereq(y.a, x.a) + vereq(y.b, x.b) + y = cPickle.loads(cPickle.dumps(x)) + vereq(y.a, x.a) + vereq(y.b, x.b) def copies(): if verbose: print "Testing copy.copy() and copy.deepcopy()..." diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 01eb706..31ac441 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1468,21 +1468,6 @@ static PyGetSetDef subtype_getsets_weakref_only[] = { {0} }; -/* bozo: __getstate__ that raises TypeError */ - -static PyObject * -bozo_func(PyObject *self, PyObject *args) -{ - PyErr_SetString(PyExc_TypeError, - "a class that defines __slots__ without " - "defining __getstate__ cannot be pickled"); - return NULL; -} - -static PyMethodDef bozo_ml = {"__getstate__", bozo_func, METH_VARARGS}; - -static PyObject *bozo_obj = NULL; - static int valid_identifier(PyObject *s) { @@ -1740,23 +1725,6 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) Py_DECREF(slots); slots = newslots; - /* See if *this* class defines __getstate__ */ - if (PyDict_GetItemString(dict, "__getstate__") == NULL) { - /* If not, provide a bozo that raises TypeError */ - if (bozo_obj == NULL) { - bozo_obj = PyCFunction_New(&bozo_ml, NULL); - if (bozo_obj == NULL) - goto bad_slots; - } - if (PyDict_SetItemString(dict, - "__getstate__", - bozo_obj) < 0) - { - Py_DECREF(bozo_obj); - goto bad_slots; - } - } - /* Secondary bases may provide weakrefs or dict */ if (nbases > 1 && ((may_add_dict && !add_dict) || |