]> granicus.if.org Git - python/commitdiff
Issue #6477: Added pickling support for singletons and their types.
authorAlexandre Vassalotti <alexandre@peadrop.com>
Sun, 1 Dec 2013 00:52:03 +0000 (16:52 -0800)
committerAlexandre Vassalotti <alexandre@peadrop.com>
Sun, 1 Dec 2013 00:52:03 +0000 (16:52 -0800)
Include/object.h
Lib/pickle.py
Lib/test/pickletester.py
Misc/NEWS
Modules/cPickle.c
Objects/object.c

index afbc68dc04e9412134dfd60bd87608656e4d1e8b..05d8d39b4099b3e0c6b4f1c2830e579ebc5f9315 100644 (file)
@@ -829,6 +829,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').
index 299de16f519b56a7cc964fa1af3fdab7b9e6b030..5d0aba740e25a6227b31e20c055909d325bc7f4c 100644 (file)
@@ -427,6 +427,14 @@ class Pickler:
         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 @@ class Pickler:
     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
 
index 1599893d8a60abb0ae5c4f20e1a07efe6279a2a7..d7b68db7b44c2c5f98c908b3173693212dfea89c 100644 (file)
@@ -661,6 +661,15 @@ class AbstractPickleTests(unittest.TestCase):
                 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):
index 55cdd7e1ad49d141c87a1e4de88e63d7e722faa9..3a7f8e125aa8bc98d2419418ee17a7ab9f1dbb90 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -15,6 +15,9 @@ Core and Builtins
 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
index 8145bbf381a098528f5d98623f08726333759399..c84a757435200ad0c315b18d6a05a9bb483f3105 100644 (file)
@@ -2558,6 +2558,60 @@ save_reduce(Picklerobject *self, PyObject *args, PyObject *fn, PyObject *ob)
     return 0;
 }
 
+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)
 {
@@ -2580,6 +2634,14 @@ save(Picklerobject *self, PyObject *args, int pers_save)
         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 @@ save(Picklerobject *self, PyObject *args, int pers_save)
             goto finally;
         }
         if (type == &PyType_Type) {
-            res = save_global(self, args, NULL);
+            res = save_type(self, args);
             goto finally;
         }
         break;
index 14f4e9f611441d0e16f47fcfab93e3d14e9b847d..26a5a2a978743e1e8be429e722319b1a0e67d816 100644 (file)
@@ -2012,7 +2012,7 @@ none_dealloc(PyObject* ignore)
 }
 
 
-static PyTypeObject PyNone_Type = {
+PyTypeObject PyNone_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     "NoneType",
     0,
@@ -2043,7 +2043,7 @@ NotImplemented_repr(PyObject *op)
     return PyString_FromString("NotImplemented");
 }
 
-static PyTypeObject PyNotImplemented_Type = {
+PyTypeObject PyNotImplemented_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     "NotImplementedType",
     0,