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