Issue #7689: Allow pickling of dynamically created classes when their
metaclass is registered with copyreg. Patch by Nicolas M. Thiéry and Craig Citro.
This commit is contained in:
parent
1efb33a682
commit
ffd41d9f10
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue