]> granicus.if.org Git - python/commitdiff
Issue #7689: Allow pickling of dynamically created classes when their
authorAntoine Pitrou <solipsis@pitrou.net>
Tue, 4 Oct 2011 07:23:04 +0000 (09:23 +0200)
committerAntoine Pitrou <solipsis@pitrou.net>
Tue, 4 Oct 2011 07:23:04 +0000 (09:23 +0200)
metaclass is registered with copyreg.  Patch by Nicolas M. Thiéry and
Craig Citro.

Lib/pickle.py
Lib/test/pickletester.py
Misc/ACKS
Misc/NEWS
Modules/_pickle.c

index 32ae02bc7d3e42c441e0509f0eadf0c33588a7a3..c770ff8bdf4e3e5f1fd9f0497ff435c7a1c97ba7 100644 (file)
@@ -299,20 +299,20 @@ class _Pickler:
             f(self, obj) # Call unbound method with explicit self
             return
 
-        # Check for a class with a custom metaclass; treat as regular class
-        try:
-            issc = issubclass(t, type)
-        except TypeError: # t is not a class (old Boost; see SF #502085)
-            issc = 0
-        if issc:
-            self.save_global(obj)
-            return
-
         # Check copyreg.dispatch_table
         reduce = dispatch_table.get(t)
         if reduce:
             rv = reduce(obj)
         else:
+            # Check for a class with a custom metaclass; treat as regular class
+            try:
+                issc = issubclass(t, type)
+            except TypeError: # t is not a class (old Boost; see SF #502085)
+                issc = False
+            if issc:
+                self.save_global(obj)
+                return
+
             # Check for a __reduce_ex__ method, fall back to __reduce__
             reduce = getattr(obj, "__reduce_ex__", None)
             if reduce:
index c2ed0d284991aeb529ea2c647d544d9ff8d50e0e..a22acc07114259afe5f95d217bf0be5e82526a53 100644 (file)
@@ -121,6 +121,19 @@ class metaclass(type):
 class use_metaclass(object, metaclass=metaclass):
     pass
 
+class pickling_metaclass(type):
+    def __eq__(self, other):
+        return (type(self) == type(other) and
+                self.reduce_args == other.reduce_args)
+
+    def __reduce__(self):
+        return (create_dynamic_class, self.reduce_args)
+
+def create_dynamic_class(name, bases):
+    result = pickling_metaclass(name, bases, dict())
+    result.reduce_args = (name, bases)
+    return result
+
 # DATA0 .. DATA2 are the pickles we expect under the various protocols, for
 # the object returned by create_data().
 
@@ -695,6 +708,14 @@ class AbstractPickleTests(unittest.TestCase):
             b = self.loads(s)
             self.assertEqual(a.__class__, b.__class__)
 
+    def test_dynamic_class(self):
+        a = create_dynamic_class("my_dynamic_class", (object,))
+        copyreg.pickle(pickling_metaclass, pickling_metaclass.__reduce__)
+        for proto in protocols:
+            s = self.dumps(a, proto)
+            b = self.loads(s)
+            self.assertEqual(a, b)
+
     def test_structseq(self):
         import time
         import os
index 5bf395b9e7466a96ae250021903d9eb6cc1da2f2..15fb385446f6d13ca40f770b72511dbe3f6bdb0d 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -164,6 +164,7 @@ Anders Chrigström
 Tom Christiansen
 Vadim Chugunov
 David Cinege
+Craig Citro
 Mike Clarkson
 Andrew Clegg
 Brad Clements
@@ -881,6 +882,7 @@ Anatoly Techtonik
 Mikhail Terekhov
 Richard M. Tew
 Tobias Thelen
+Nicolas M. Thiéry
 James Thomas
 Robin Thomas
 Stephen Thorne
index aebd899b92cdb280053f3a09a225e9f7870a9f8e..2961776ae22089e87d66862386b02552d1495eed 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -36,6 +36,10 @@ Core and Builtins
 Library
 -------
 
+- Issue #7689: Allow pickling of dynamically created classes when their
+  metaclass is registered with copyreg.  Patch by Nicolas M. Thiéry and Craig
+  Citro.
+
 - Issue #4147: minidom's toprettyxml no longer adds whitespace to text nodes.
 
 - Issue #13034: When decoding some SSL certificates, the subjectAltName
index 164d86498099e8b092b4871fa2392c61bb4fad11..cb583491d498c06868facc14c1f4f1234f2b6c5c 100644 (file)
@@ -3141,10 +3141,6 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
         status = save_global(self, obj, NULL);
         goto done;
     }
-    else if (PyType_IsSubtype(type, &PyType_Type)) {
-        status = save_global(self, obj, NULL);
-        goto done;
-    }
 
     /* XXX: This part needs some unit tests. */
 
@@ -3163,6 +3159,10 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
         Py_INCREF(obj);
         reduce_value = _Pickler_FastCall(self, reduce_func, obj);
     }
+    else if (PyType_IsSubtype(type, &PyType_Type)) {
+        status = save_global(self, obj, NULL);
+        goto done;
+    }
     else {
         static PyObject *reduce_str = NULL;
         static PyObject *reduce_ex_str = NULL;