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:
Antoine Pitrou 2011-10-04 09:34:48 +02:00
parent ad349a190e
commit 561a821e93
5 changed files with 41 additions and 14 deletions

View file

@ -286,20 +286,20 @@ def save(self, obj):
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, TypeType)
except TypeError: # t is not a class (old Boost; see SF #502085)
issc = 0
if issc:
self.save_global(obj)
return
# Check copy_reg.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, TypeType)
except TypeError: # t is not a class (old Boost; see SF #502085)
issc = 0
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:

View file

@ -124,6 +124,19 @@ class metaclass(type):
class use_metaclass(object):
__metaclass__ = metaclass
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().
@ -609,6 +622,14 @@ def test_metaclass(self):
b = self.loads(s)
self.assertEqual(a.__class__, b.__class__)
def test_dynamic_class(self):
a = create_dynamic_class("my_dynamic_class", (object,))
copy_reg.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

View file

@ -147,6 +147,7 @@ Anders Chrigström
Tom Christiansen
Vadim Chugunov
David Cinege
Craig Citro
Mike Clarkson
Andrew Clegg
Brad Clements
@ -817,6 +818,7 @@ Anatoly Techtonik
Mikhail Terekhov
Richard M. Tew
Tobias Thelen
Nicolas M. Thiéry
James Thomas
Robin Thomas
Stephen Thorne

View file

@ -50,6 +50,10 @@ Core and Builtins
Library
-------
- Issue #7689: Allow pickling of dynamically created classes when their
metaclass is registered with copy_reg. Patch by Nicolas M. Thiéry and
Craig Citro.
- Issue #13058: ossaudiodev: fix a file descriptor leak on error. Patch by
Thomas Jarosch.

View file

@ -2697,11 +2697,6 @@ save(Picklerobject *self, PyObject *args, int pers_save)
}
}
if (PyType_IsSubtype(type, &PyType_Type)) {
res = save_global(self, args, NULL);
goto finally;
}
/* Get a reduction callable, and call it. This may come from
* copy_reg.dispatch_table, the object's __reduce_ex__ method,
* or the object's __reduce__ method.
@ -2717,6 +2712,11 @@ save(Picklerobject *self, PyObject *args, int pers_save)
}
}
else {
if (PyType_IsSubtype(type, &PyType_Type)) {
res = save_global(self, args, NULL);
goto finally;
}
/* Check for a __reduce_ex__ method. */
__reduce__ = PyObject_GetAttr(args, __reduce_ex___str);
if (__reduce__ != NULL) {