mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 19:34:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			196 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			196 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""Helper to provide extensibility for pickle.
 | 
						|
 | 
						|
This is only useful to add pickle support for extension types defined in
 | 
						|
C, not for instances of user-defined classes.
 | 
						|
"""
 | 
						|
 | 
						|
__all__ = ["pickle", "constructor",
 | 
						|
           "add_extension", "remove_extension", "clear_extension_cache"]
 | 
						|
 | 
						|
dispatch_table = {}
 | 
						|
 | 
						|
def pickle(ob_type, pickle_function, constructor_ob=None):
 | 
						|
    if not callable(pickle_function):
 | 
						|
        raise TypeError("reduction functions must be callable")
 | 
						|
    dispatch_table[ob_type] = pickle_function
 | 
						|
 | 
						|
    # The constructor_ob function is a vestige of safe for unpickling.
 | 
						|
    # There is no reason for the caller to pass it anymore.
 | 
						|
    if constructor_ob is not None:
 | 
						|
        constructor(constructor_ob)
 | 
						|
 | 
						|
def constructor(object):
 | 
						|
    if not callable(object):
 | 
						|
        raise TypeError("constructors must be callable")
 | 
						|
 | 
						|
# Example: provide pickling support for complex numbers.
 | 
						|
 | 
						|
try:
 | 
						|
    complex
 | 
						|
except NameError:
 | 
						|
    pass
 | 
						|
else:
 | 
						|
 | 
						|
    def pickle_complex(c):
 | 
						|
        return complex, (c.real, c.imag)
 | 
						|
 | 
						|
    pickle(complex, pickle_complex, complex)
 | 
						|
 | 
						|
# Support for pickling new-style objects
 | 
						|
 | 
						|
def _reconstructor(cls, base, state):
 | 
						|
    if base is object:
 | 
						|
        obj = object.__new__(cls)
 | 
						|
    else:
 | 
						|
        obj = base.__new__(cls, state)
 | 
						|
        if base.__init__ != object.__init__:
 | 
						|
            base.__init__(obj, state)
 | 
						|
    return obj
 | 
						|
 | 
						|
_HEAPTYPE = 1<<9
 | 
						|
 | 
						|
# Python code for object.__reduce_ex__ for protocols 0 and 1
 | 
						|
 | 
						|
def _reduce_ex(self, proto):
 | 
						|
    assert proto < 2
 | 
						|
    for base in self.__class__.__mro__:
 | 
						|
        if hasattr(base, '__flags__') and not base.__flags__ & _HEAPTYPE:
 | 
						|
            break
 | 
						|
    else:
 | 
						|
        base = object # not really reachable
 | 
						|
    if base is object:
 | 
						|
        state = None
 | 
						|
    else:
 | 
						|
        if base is self.__class__:
 | 
						|
            raise TypeError("can't pickle %s objects" % base.__name__)
 | 
						|
        state = base(self)
 | 
						|
    args = (self.__class__, base, state)
 | 
						|
    try:
 | 
						|
        getstate = self.__getstate__
 | 
						|
    except AttributeError:
 | 
						|
        if getattr(self, "__slots__", None):
 | 
						|
            raise TypeError("a class that defines __slots__ without "
 | 
						|
                            "defining __getstate__ cannot be pickled")
 | 
						|
        try:
 | 
						|
            dict = self.__dict__
 | 
						|
        except AttributeError:
 | 
						|
            dict = None
 | 
						|
    else:
 | 
						|
        dict = getstate()
 | 
						|
    if dict:
 | 
						|
        return _reconstructor, args, dict
 | 
						|
    else:
 | 
						|
        return _reconstructor, args
 | 
						|
 | 
						|
# Helper for __reduce_ex__ protocol 2
 | 
						|
 | 
						|
def __newobj__(cls, *args):
 | 
						|
    return cls.__new__(cls, *args)
 | 
						|
 | 
						|
def _slotnames(cls):
 | 
						|
    """Return a list of slot names for a given class.
 | 
						|
 | 
						|
    This needs to find slots defined by the class and its bases, so we
 | 
						|
    can't simply return the __slots__ attribute.  We must walk down
 | 
						|
    the Method Resolution Order and concatenate the __slots__ of each
 | 
						|
    class found there.  (This assumes classes don't modify their
 | 
						|
    __slots__ attribute to misrepresent their slots after the class is
 | 
						|
    defined.)
 | 
						|
    """
 | 
						|
 | 
						|
    # Get the value from a cache in the class if possible
 | 
						|
    names = cls.__dict__.get("__slotnames__")
 | 
						|
    if names is not None:
 | 
						|
        return names
 | 
						|
 | 
						|
    # Not cached -- calculate the value
 | 
						|
    names = []
 | 
						|
    if not hasattr(cls, "__slots__"):
 | 
						|
        # This class has no slots
 | 
						|
        pass
 | 
						|
    else:
 | 
						|
        # Slots found -- gather slot names from all base classes
 | 
						|
        for c in cls.__mro__:
 | 
						|
            if "__slots__" in c.__dict__:
 | 
						|
                slots = c.__dict__['__slots__']
 | 
						|
                # if class has a single slot, it can be given as a string
 | 
						|
                if isinstance(slots, str):
 | 
						|
                    slots = (slots,)
 | 
						|
                for name in slots:
 | 
						|
                    # special descriptors
 | 
						|
                    if name in ("__dict__", "__weakref__"):
 | 
						|
                        continue
 | 
						|
                    # mangled names
 | 
						|
                    elif name.startswith('__') and not name.endswith('__'):
 | 
						|
                        names.append('_%s%s' % (c.__name__, name))
 | 
						|
                    else:
 | 
						|
                        names.append(name)
 | 
						|
 | 
						|
    # Cache the outcome in the class if at all possible
 | 
						|
    try:
 | 
						|
        cls.__slotnames__ = names
 | 
						|
    except:
 | 
						|
        pass # But don't die if we can't
 | 
						|
 | 
						|
    return names
 | 
						|
 | 
						|
# A registry of extension codes.  This is an ad-hoc compression
 | 
						|
# mechanism.  Whenever a global reference to <module>, <name> is about
 | 
						|
# to be pickled, the (<module>, <name>) tuple is looked up here to see
 | 
						|
# if it is a registered extension code for it.  Extension codes are
 | 
						|
# universal, so that the meaning of a pickle does not depend on
 | 
						|
# context.  (There are also some codes reserved for local use that
 | 
						|
# don't have this restriction.)  Codes are positive ints; 0 is
 | 
						|
# reserved.
 | 
						|
 | 
						|
_extension_registry = {}                # key -> code
 | 
						|
_inverted_registry = {}                 # code -> key
 | 
						|
_extension_cache = {}                   # code -> object
 | 
						|
# Don't ever rebind those names:  pickling grabs a reference to them when
 | 
						|
# it's initialized, and won't see a rebinding.
 | 
						|
 | 
						|
def add_extension(module, name, code):
 | 
						|
    """Register an extension code."""
 | 
						|
    code = int(code)
 | 
						|
    if not 1 <= code <= 0x7fffffff:
 | 
						|
        raise ValueError("code out of range")
 | 
						|
    key = (module, name)
 | 
						|
    if (_extension_registry.get(key) == code and
 | 
						|
        _inverted_registry.get(code) == key):
 | 
						|
        return # Redundant registrations are benign
 | 
						|
    if key in _extension_registry:
 | 
						|
        raise ValueError("key %s is already registered with code %s" %
 | 
						|
                         (key, _extension_registry[key]))
 | 
						|
    if code in _inverted_registry:
 | 
						|
        raise ValueError("code %s is already in use for key %s" %
 | 
						|
                         (code, _inverted_registry[code]))
 | 
						|
    _extension_registry[key] = code
 | 
						|
    _inverted_registry[code] = key
 | 
						|
 | 
						|
def remove_extension(module, name, code):
 | 
						|
    """Unregister an extension code.  For testing only."""
 | 
						|
    key = (module, name)
 | 
						|
    if (_extension_registry.get(key) != code or
 | 
						|
        _inverted_registry.get(code) != key):
 | 
						|
        raise ValueError("key %s is not registered with code %s" %
 | 
						|
                         (key, code))
 | 
						|
    del _extension_registry[key]
 | 
						|
    del _inverted_registry[code]
 | 
						|
    if code in _extension_cache:
 | 
						|
        del _extension_cache[code]
 | 
						|
 | 
						|
def clear_extension_cache():
 | 
						|
    _extension_cache.clear()
 | 
						|
 | 
						|
# Standard extension code assignments
 | 
						|
 | 
						|
# Reserved ranges
 | 
						|
 | 
						|
# First  Last Count  Purpose
 | 
						|
#     1   127   127  Reserved for Python standard library
 | 
						|
#   128   191    64  Reserved for Zope
 | 
						|
#   192   239    48  Reserved for 3rd parties
 | 
						|
#   240   255    16  Reserved for private use (will never be assigned)
 | 
						|
#   256   Inf   Inf  Reserved for future assignment
 | 
						|
 | 
						|
# Extension codes are assigned by the Python Software Foundation.
 |