diff -r d964d7023aa4 Include/object.h --- a/Include/object.h Sat Nov 30 17:43:42 2013 -0800 +++ b/Include/object.h Sat Nov 30 18:33:57 2013 -0800 @@ -829,6 +829,9 @@ 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 -r d964d7023aa4 Lib/pickle.py --- a/Lib/pickle.py Sat Nov 30 17:43:42 2013 -0800 +++ b/Lib/pickle.py Sat Nov 30 18:33:57 2013 -0800 @@ -427,6 +427,14 @@ self.write(NONE) dispatch[NoneType] = save_none + def save_ellipsis(self, obj): + self.save_global(Ellipsis, 'Ellipsis') + dispatch[type(Ellipsis)] = save_ellipsis + + def save_notimplemented(self, obj): + self.save_global(NotImplemented, 'NotImplemented') + dispatch[type(NotImplemented)] = save_notimplemented + def save_bool(self, obj): if self.proto >= 2: self.write(obj and NEWTRUE or NEWFALSE) @@ -770,7 +778,18 @@ dispatch[ClassType] = save_global dispatch[FunctionType] = save_global dispatch[BuiltinFunctionType] = save_global - dispatch[TypeType] = save_global + + 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(Ellipsis): + return self.save_reduce(type, (Ellipsis,), obj=obj) + return self.save_global(obj) + + dispatch[TypeType] = save_type + # Pickling helpers diff -r d964d7023aa4 Lib/test/pickletester.py --- a/Lib/test/pickletester.py Sat Nov 30 17:43:42 2013 -0800 +++ b/Lib/test/pickletester.py Sat Nov 30 18:33:57 2013 -0800 @@ -661,6 +661,15 @@ u = self.loads(s) self.assertEqual(t, u) + def test_singleton_types(self): + # Issue #6477: Test that types of built-in singletons can be pickled. + singletons = [None, Ellipsis, 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 -r d964d7023aa4 Lib/test/test_xpickle.py --- a/Lib/test/test_xpickle.py Sat Nov 30 17:43:42 2013 -0800 +++ b/Lib/test/test_xpickle.py Sat Nov 30 18:33:57 2013 -0800 @@ -158,6 +158,10 @@ def test_dynamic_class(self): pass + # This tests a fix that's in 2.7 only + def test_singleton_types(self): + pass + if test_support.have_unicode: # This is a cut-down version of pickletester's test_unicode. Backwards # compatibility was explicitly broken in r67934 to fix a bug. diff -r d964d7023aa4 Misc/NEWS --- a/Misc/NEWS Sat Nov 30 17:43:42 2013 -0800 +++ b/Misc/NEWS Sat Nov 30 18:33:57 2013 -0800 @@ -15,6 +15,9 @@ Library ------- +- Issue #6477: Added pickling support for built-in singletons (i.e., None, + Ellipsis and NotImplemented) and their respective types. + - Issue #16231: Fixed pickle.Pickler to only fallback to its default pickling behaviour when Pickler.persistent_id returns None, but not for any other false values. This allows false values other than None to be used as diff -r d964d7023aa4 Modules/cPickle.c --- a/Modules/cPickle.c Sat Nov 30 17:43:42 2013 -0800 +++ b/Modules/cPickle.c Sat Nov 30 18:33:57 2013 -0800 @@ -2559,6 +2559,60 @@ } static int +save_ellipsis(Picklerobject *self, PyObject *obj) +{ + PyObject *str = PyString_FromString("Ellipsis"); + int res; + if (str == NULL) + return -1; + res = save_global(self, Py_Ellipsis, str); + Py_DECREF(str); + return res; +} + +static int +save_notimplemented(Picklerobject *self, PyObject *obj) +{ + PyObject *str = PyString_FromString("NotImplemented"); + int res; + if (str == NULL) + return -1; + res = save_global(self, Py_NotImplemented, str); + Py_DECREF(str); + return res; +} + +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, (PyObject *)self, 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(Picklerobject *self, PyObject *args, int pers_save) { PyTypeObject *type; @@ -2580,6 +2634,14 @@ res = save_none(self, args); goto finally; } + else if (args == Py_Ellipsis) { + res = save_ellipsis(self, args); + goto finally; + } + else if (args == Py_NotImplemented) { + res = save_notimplemented(self, args); + goto finally; + } type = Py_TYPE(args); @@ -2671,7 +2733,7 @@ goto finally; } if (type == &PyType_Type) { - res = save_global(self, args, NULL); + res = save_type(self, args); goto finally; } break; diff -r d964d7023aa4 Objects/object.c --- a/Objects/object.c Sat Nov 30 17:43:42 2013 -0800 +++ b/Objects/object.c Sat Nov 30 18:33:57 2013 -0800 @@ -2012,7 +2012,7 @@ } -static PyTypeObject PyNone_Type = { +PyTypeObject _PyNone_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "NoneType", 0, @@ -2031,7 +2031,7 @@ PyObject _Py_NoneStruct = { _PyObject_EXTRA_INIT - 1, &PyNone_Type + 1, &_PyNone_Type }; /* NotImplemented is an object that can be used to signal that an @@ -2043,7 +2043,7 @@ return PyString_FromString("NotImplemented"); } -static PyTypeObject PyNotImplemented_Type = { +PyTypeObject _PyNotImplemented_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "NotImplementedType", 0, @@ -2062,7 +2062,7 @@ PyObject _Py_NotImplementedStruct = { _PyObject_EXTRA_INIT - 1, &PyNotImplemented_Type + 1, &_PyNotImplemented_Type }; void @@ -2092,10 +2092,10 @@ if (PyType_Ready(&PyList_Type) < 0) Py_FatalError("Can't initialize list type"); - if (PyType_Ready(&PyNone_Type) < 0) + if (PyType_Ready(&_PyNone_Type) < 0) Py_FatalError("Can't initialize None type"); - if (PyType_Ready(&PyNotImplemented_Type) < 0) + if (PyType_Ready(&_PyNotImplemented_Type) < 0) Py_FatalError("Can't initialize NotImplemented type"); if (PyType_Ready(&PyTraceBack_Type) < 0)