From 19b6fa6ebb887e498437b4ae87d6e70b92b4742b Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Sat, 30 Nov 2013 16:06:39 -0800 Subject: Issue #6477: Added support for pickling the types of built-in singletons. --- Include/object.h | 3 +++ Lib/pickle.py | 11 ++++++++++- Lib/test/pickletester.py | 9 +++++++++ Misc/NEWS | 3 +++ Modules/_pickle.c | 32 +++++++++++++++++++++++++++++++- Objects/object.c | 4 ++-- 6 files changed, 58 insertions(+), 4 deletions(-) diff --git a/Include/object.h b/Include/object.h index 20c4780..5c89ca3 100644 --- a/Include/object.h +++ b/Include/object.h @@ -831,6 +831,9 @@ they can have object code that is not dependent on Python compilation flags. PyAPI_FUNC(void) Py_IncRef(PyObject *); PyAPI_FUNC(void) Py_DecRef(PyObject *); +PyAPI_DATA(PyTypeObject) PyNone_Type; +PyAPI_DATA(PyTypeObject) PyNotImplemented_Type; + /* _Py_NoneStruct is an object of undefined type which can be used in contexts where NULL (nil) is not suitable (since NULL often means 'error'). diff --git a/Lib/pickle.py b/Lib/pickle.py index d62f014..386ffba 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -728,9 +728,18 @@ class _Pickler: self.memoize(obj) + def save_type(self, obj): + if obj is type(None): + return self.save_reduce(type, (None,), obj=obj) + elif obj is type(NotImplemented): + return self.save_reduce(type, (NotImplemented,), obj=obj) + elif obj is type(...): + return self.save_reduce(type, (...,), obj=obj) + return self.save_global(obj) + dispatch[FunctionType] = save_global dispatch[BuiltinFunctionType] = save_global - dispatch[type] = save_global + dispatch[type] = save_type # Pickling helpers diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py index 86869e1..86d668e 100644 --- a/Lib/test/pickletester.py +++ b/Lib/test/pickletester.py @@ -768,6 +768,15 @@ class AbstractPickleTests(unittest.TestCase): u = self.loads(s) self.assertEqual(NotImplemented, u) + def test_singleton_types(self): + # Issue #6477: Test that types of built-in singletons can be pickled. + singletons = [None, ..., NotImplemented] + for singleton in singletons: + for proto in protocols: + s = self.dumps(type(singleton), proto) + u = self.loads(s) + self.assertIs(type(singleton), u) + # Tests for protocol 2 def test_proto(self): diff --git a/Misc/NEWS b/Misc/NEWS index 6e55d66..0acbafd 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,9 @@ Library - Fixed _pickle.Unpickler to not fail when loading empty strings as persistent IDs. +- Issue #6477: Added support for pickling the types of built-in singletons + (i.e., Ellipsis, NotImplemented, None). + - Issue #11508: Fixed uuid.getnode() and uuid.uuid1() on environment with virtual interface. Original patch by Kent Frazier. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index d862ae8..ba192fb 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -2836,6 +2836,36 @@ save_notimplemented(PicklerObject *self, PyObject *obj) } static int +save_singleton_type(PicklerObject *self, PyObject *obj, PyObject *singleton) +{ + PyObject *reduce_value; + int status; + + reduce_value = Py_BuildValue("O(O)", &PyType_Type, singleton); + if (reduce_value == NULL) { + return -1; + } + status = save_reduce(self, reduce_value, obj); + Py_DECREF(reduce_value); + return status; +} + +static int +save_type(PicklerObject *self, PyObject *obj) +{ + if (obj == (PyObject *)&PyNone_Type) { + return save_singleton_type(self, obj, Py_None); + } + else if (obj == (PyObject *)&PyEllipsis_Type) { + return save_singleton_type(self, obj, Py_Ellipsis); + } + else if (obj == (PyObject *)&PyNotImplemented_Type) { + return save_singleton_type(self, obj, Py_NotImplemented); + } + return save_global(self, obj, NULL); +} + +static int save_pers(PicklerObject *self, PyObject *obj, PyObject *func) { PyObject *pid = NULL; @@ -3189,7 +3219,7 @@ save(PicklerObject *self, PyObject *obj, int pers_save) goto done; } else if (type == &PyType_Type) { - status = save_global(self, obj, NULL); + status = save_type(self, obj); goto done; } else if (type == &PyFunction_Type) { diff --git a/Objects/object.c b/Objects/object.c index 949e7dc..83da6a4 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1459,7 +1459,7 @@ static PyNumberMethods none_as_number = { 0, /* nb_index */ }; -static PyTypeObject PyNone_Type = { +PyTypeObject PyNone_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "NoneType", 0, @@ -1524,7 +1524,7 @@ notimplemented_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_RETURN_NOTIMPLEMENTED; } -static PyTypeObject PyNotImplemented_Type = { +PyTypeObject PyNotImplemented_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "NotImplementedType", 0, -- cgit v0.12