mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +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,6 +286,11 @@ class Pickler:
 | 
				
			||||||
            f(self, obj) # Call unbound method with explicit self
 | 
					            f(self, obj) # Call unbound method with explicit self
 | 
				
			||||||
            return
 | 
					            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
 | 
					            # Check for a class with a custom metaclass; treat as regular class
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                issc = issubclass(t, TypeType)
 | 
					                issc = issubclass(t, TypeType)
 | 
				
			||||||
| 
						 | 
					@ -295,11 +300,6 @@ class Pickler:
 | 
				
			||||||
                self.save_global(obj)
 | 
					                self.save_global(obj)
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Check copy_reg.dispatch_table
 | 
					 | 
				
			||||||
        reduce = dispatch_table.get(t)
 | 
					 | 
				
			||||||
        if reduce:
 | 
					 | 
				
			||||||
            rv = reduce(obj)
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            # 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