1"""Helper to provide extensibility for pickle. 2 3This is only useful to add pickle support for extension types defined in 4C, not for instances of user-defined classes. 5""" 6 7__all__ = ["pickle", "constructor", 8 "add_extension", "remove_extension", "clear_extension_cache"] 9 10dispatch_table = {} 11 12def pickle(ob_type, pickle_function, constructor_ob=None): 13 if not callable(pickle_function): 14 raise TypeError("reduction functions must be callable") 15 dispatch_table[ob_type] = pickle_function 16 17 # The constructor_ob function is a vestige of safe for unpickling. 18 # There is no reason for the caller to pass it anymore. 19 if constructor_ob is not None: 20 constructor(constructor_ob) 21 22def constructor(object): 23 if not callable(object): 24 raise TypeError("constructors must be callable") 25 26# Example: provide pickling support for complex numbers. 27 28try: 29 complex 30except NameError: 31 pass 32else: 33 34 def pickle_complex(c): 35 return complex, (c.real, c.imag) 36 37 pickle(complex, pickle_complex, complex) 38 39def pickle_union(obj): 40 import functools, operator 41 return functools.reduce, (operator.or_, obj.__args__) 42 43pickle(type(int | str), pickle_union) 44 45# Support for pickling new-style objects 46 47def _reconstructor(cls, base, state): 48 if base is object: 49 obj = object.__new__(cls) 50 else: 51 obj = base.__new__(cls, state) 52 if base.__init__ != object.__init__: 53 base.__init__(obj, state) 54 return obj 55 56_HEAPTYPE = 1<<9 57_new_type = type(int.__new__) 58 59# Python code for object.__reduce_ex__ for protocols 0 and 1 60 61def _reduce_ex(self, proto): 62 assert proto < 2 63 cls = self.__class__ 64 for base in cls.__mro__: 65 if hasattr(base, '__flags__') and not base.__flags__ & _HEAPTYPE: 66 break 67 new = base.__new__ 68 if isinstance(new, _new_type) and new.__self__ is base: 69 break 70 else: 71 base = object # not really reachable 72 if base is object: 73 state = None 74 else: 75 if base is cls: 76 raise TypeError(f"cannot pickle {cls.__name__!r} object") 77 state = base(self) 78 args = (cls, base, state) 79 try: 80 getstate = self.__getstate__ 81 except AttributeError: 82 if getattr(self, "__slots__", None): 83 raise TypeError(f"cannot pickle {cls.__name__!r} object: " 84 f"a class that defines __slots__ without " 85 f"defining __getstate__ cannot be pickled " 86 f"with protocol {proto}") from None 87 try: 88 dict = self.__dict__ 89 except AttributeError: 90 dict = None 91 else: 92 dict = getstate() 93 if dict: 94 return _reconstructor, args, dict 95 else: 96 return _reconstructor, args 97 98# Helper for __reduce_ex__ protocol 2 99 100def __newobj__(cls, *args): 101 return cls.__new__(cls, *args) 102 103def __newobj_ex__(cls, args, kwargs): 104 """Used by pickle protocol 4, instead of __newobj__ to allow classes with 105 keyword-only arguments to be pickled correctly. 106 """ 107 return cls.__new__(cls, *args, **kwargs) 108 109def _slotnames(cls): 110 """Return a list of slot names for a given class. 111 112 This needs to find slots defined by the class and its bases, so we 113 can't simply return the __slots__ attribute. We must walk down 114 the Method Resolution Order and concatenate the __slots__ of each 115 class found there. (This assumes classes don't modify their 116 __slots__ attribute to misrepresent their slots after the class is 117 defined.) 118 """ 119 120 # Get the value from a cache in the class if possible 121 names = cls.__dict__.get("__slotnames__") 122 if names is not None: 123 return names 124 125 # Not cached -- calculate the value 126 names = [] 127 if not hasattr(cls, "__slots__"): 128 # This class has no slots 129 pass 130 else: 131 # Slots found -- gather slot names from all base classes 132 for c in cls.__mro__: 133 if "__slots__" in c.__dict__: 134 slots = c.__dict__['__slots__'] 135 # if class has a single slot, it can be given as a string 136 if isinstance(slots, str): 137 slots = (slots,) 138 for name in slots: 139 # special descriptors 140 if name in ("__dict__", "__weakref__"): 141 continue 142 # mangled names 143 elif name.startswith('__') and not name.endswith('__'): 144 stripped = c.__name__.lstrip('_') 145 if stripped: 146 names.append('_%s%s' % (stripped, name)) 147 else: 148 names.append(name) 149 else: 150 names.append(name) 151 152 # Cache the outcome in the class if at all possible 153 try: 154 cls.__slotnames__ = names 155 except: 156 pass # But don't die if we can't 157 158 return names 159 160# A registry of extension codes. This is an ad-hoc compression 161# mechanism. Whenever a global reference to <module>, <name> is about 162# to be pickled, the (<module>, <name>) tuple is looked up here to see 163# if it is a registered extension code for it. Extension codes are 164# universal, so that the meaning of a pickle does not depend on 165# context. (There are also some codes reserved for local use that 166# don't have this restriction.) Codes are positive ints; 0 is 167# reserved. 168 169_extension_registry = {} # key -> code 170_inverted_registry = {} # code -> key 171_extension_cache = {} # code -> object 172# Don't ever rebind those names: pickling grabs a reference to them when 173# it's initialized, and won't see a rebinding. 174 175def add_extension(module, name, code): 176 """Register an extension code.""" 177 code = int(code) 178 if not 1 <= code <= 0x7fffffff: 179 raise ValueError("code out of range") 180 key = (module, name) 181 if (_extension_registry.get(key) == code and 182 _inverted_registry.get(code) == key): 183 return # Redundant registrations are benign 184 if key in _extension_registry: 185 raise ValueError("key %s is already registered with code %s" % 186 (key, _extension_registry[key])) 187 if code in _inverted_registry: 188 raise ValueError("code %s is already in use for key %s" % 189 (code, _inverted_registry[code])) 190 _extension_registry[key] = code 191 _inverted_registry[code] = key 192 193def remove_extension(module, name, code): 194 """Unregister an extension code. For testing only.""" 195 key = (module, name) 196 if (_extension_registry.get(key) != code or 197 _inverted_registry.get(code) != key): 198 raise ValueError("key %s is not registered with code %s" % 199 (key, code)) 200 del _extension_registry[key] 201 del _inverted_registry[code] 202 if code in _extension_cache: 203 del _extension_cache[code] 204 205def clear_extension_cache(): 206 _extension_cache.clear() 207 208# Standard extension code assignments 209 210# Reserved ranges 211 212# First Last Count Purpose 213# 1 127 127 Reserved for Python standard library 214# 128 191 64 Reserved for Zope 215# 192 239 48 Reserved for 3rd parties 216# 240 255 16 Reserved for private use (will never be assigned) 217# 256 Inf Inf Reserved for future assignment 218 219# Extension codes are assigned by the Python Software Foundation. 220