summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2011-10-04 07:23:04 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2011-10-04 07:23:04 (GMT)
commitffd41d9f101e31973b8713e884c95118fceb6f59 (patch)
tree3b549e20b5cf09244789f6e6f67a7dce2fad75b4
parent1efb33a6823b94739e59d6e2fe92e8e703a7fc7d (diff)
downloadcpython-ffd41d9f101e31973b8713e884c95118fceb6f59.zip
cpython-ffd41d9f101e31973b8713e884c95118fceb6f59.tar.gz
cpython-ffd41d9f101e31973b8713e884c95118fceb6f59.tar.bz2
Issue #7689: Allow pickling of dynamically created classes when their
metaclass is registered with copyreg. Patch by Nicolas M. Thiéry and Craig Citro.
-rw-r--r--Lib/pickle.py18
-rw-r--r--Lib/test/pickletester.py21
-rw-r--r--Misc/ACKS2
-rw-r--r--Misc/NEWS4
-rw-r--r--Modules/_pickle.c8
5 files changed, 40 insertions, 13 deletions
diff --git a/Lib/pickle.py b/Lib/pickle.py
index 32ae02b..c770ff8 100644
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -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:
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index c2ed0d2..a22acc0 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -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
diff --git a/Misc/ACKS b/Misc/ACKS
index 5bf395b..15fb385 100644
--- 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
diff --git a/Misc/NEWS b/Misc/NEWS
index aebd899..2961776 100644
--- 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
diff --git a/Modules/_pickle.c b/Modules/_pickle.c
index 164d864..cb58349 100644
--- a/Modules/_pickle.c
+++ b/Modules/_pickle.c
@@ -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;