mirror of
https://github.com/python/cpython.git
synced 2025-11-03 03:22:27 +00:00
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
ad349a190e
commit
561a821e93
5 changed files with 41 additions and 14 deletions
|
|
@ -286,20 +286,20 @@ class Pickler:
|
||||||
f(self, obj) # Call unbound method with explicit self
|
f(self, obj) # Call unbound method with explicit self
|
||||||
return
|
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
|
# Check copy_reg.dispatch_table
|
||||||
reduce = dispatch_table.get(t)
|
reduce = dispatch_table.get(t)
|
||||||
if reduce:
|
if reduce:
|
||||||
rv = reduce(obj)
|
rv = reduce(obj)
|
||||||
else:
|
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__
|
# Check for a __reduce_ex__ method, fall back to __reduce__
|
||||||
reduce = getattr(obj, "__reduce_ex__", None)
|
reduce = getattr(obj, "__reduce_ex__", None)
|
||||||
if reduce:
|
if reduce:
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,19 @@ class metaclass(type):
|
||||||
class use_metaclass(object):
|
class use_metaclass(object):
|
||||||
__metaclass__ = metaclass
|
__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
|
# DATA0 .. DATA2 are the pickles we expect under the various protocols, for
|
||||||
# the object returned by create_data().
|
# the object returned by create_data().
|
||||||
|
|
||||||
|
|
@ -609,6 +622,14 @@ class AbstractPickleTests(unittest.TestCase):
|
||||||
b = self.loads(s)
|
b = self.loads(s)
|
||||||
self.assertEqual(a.__class__, b.__class__)
|
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):
|
def test_structseq(self):
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ Anders Chrigström
|
||||||
Tom Christiansen
|
Tom Christiansen
|
||||||
Vadim Chugunov
|
Vadim Chugunov
|
||||||
David Cinege
|
David Cinege
|
||||||
|
Craig Citro
|
||||||
Mike Clarkson
|
Mike Clarkson
|
||||||
Andrew Clegg
|
Andrew Clegg
|
||||||
Brad Clements
|
Brad Clements
|
||||||
|
|
@ -817,6 +818,7 @@ Anatoly Techtonik
|
||||||
Mikhail Terekhov
|
Mikhail Terekhov
|
||||||
Richard M. Tew
|
Richard M. Tew
|
||||||
Tobias Thelen
|
Tobias Thelen
|
||||||
|
Nicolas M. Thiéry
|
||||||
James Thomas
|
James Thomas
|
||||||
Robin Thomas
|
Robin Thomas
|
||||||
Stephen Thorne
|
Stephen Thorne
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,10 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #13058: ossaudiodev: fix a file descriptor leak on error. Patch by
|
||||||
Thomas Jarosch.
|
Thomas Jarosch.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
/* Get a reduction callable, and call it. This may come from
|
||||||
* copy_reg.dispatch_table, the object's __reduce_ex__ method,
|
* copy_reg.dispatch_table, the object's __reduce_ex__ method,
|
||||||
* or the object's __reduce__ method.
|
* or the object's __reduce__ method.
|
||||||
|
|
@ -2717,6 +2712,11 @@ save(Picklerobject *self, PyObject *args, int pers_save)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (PyType_IsSubtype(type, &PyType_Type)) {
|
||||||
|
res = save_global(self, args, NULL);
|
||||||
|
goto finally;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for a __reduce_ex__ method. */
|
/* Check for a __reduce_ex__ method. */
|
||||||
__reduce__ = PyObject_GetAttr(args, __reduce_ex___str);
|
__reduce__ = PyObject_GetAttr(args, __reduce_ex___str);
|
||||||
if (__reduce__ != NULL) {
|
if (__reduce__ != NULL) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue