mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			209 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
	
		
			7 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
 | |
|     cls = self.__class__
 | |
|     for base in cls.__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 cls:
 | |
|             raise TypeError(f"cannot pickle {cls.__name__!r} object")
 | |
|         state = base(self)
 | |
|     args = (cls, base, state)
 | |
|     try:
 | |
|         getstate = self.__getstate__
 | |
|     except AttributeError:
 | |
|         if getattr(self, "__slots__", None):
 | |
|             raise TypeError(f"cannot pickle {cls.__name__!r} object: "
 | |
|                             f"a class that defines __slots__ without "
 | |
|                             f"defining __getstate__ cannot be pickled "
 | |
|                             f"with protocol {proto}") from None
 | |
|         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 __newobj_ex__(cls, args, kwargs):
 | |
|     """Used by pickle protocol 4, instead of __newobj__ to allow classes with
 | |
|     keyword-only arguments to be pickled correctly.
 | |
|     """
 | |
|     return cls.__new__(cls, *args, **kwargs)
 | |
| 
 | |
| 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('__'):
 | |
|                         stripped = c.__name__.lstrip('_')
 | |
|                         if stripped:
 | |
|                             names.append('_%s%s' % (stripped, name))
 | |
|                         else:
 | |
|                             names.append(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.
 | 
