copyreg.py 6.97 KB
Newer Older
1
"""Helper to provide extensibility for pickle.
2 3 4 5 6

This is only useful to add pickle support for extension types defined in
C, not for instances of user-defined classes.
"""

7 8
__all__ = ["pickle", "constructor",
           "add_extension", "remove_extension", "clear_extension_cache"]
9

10 11
dispatch_table = {}

12
def pickle(ob_type, pickle_function, constructor_ob=None):
13
    if not callable(pickle_function):
14
        raise TypeError("reduction functions must be callable")
15 16
    dispatch_table[ob_type] = pickle_function

17 18 19 20 21
    # 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)

22
def constructor(object):
23
    if not callable(object):
24
        raise TypeError("constructors must be callable")
25

26
# Example: provide pickling support for complex numbers.
27

28 29 30 31 32
try:
    complex
except NameError:
    pass
else:
33

34 35 36 37
    def pickle_complex(c):
        return complex, (c.real, c.imag)

    pickle(complex, pickle_complex, complex)
38

39
# Support for pickling new-style objects
40 41

def _reconstructor(cls, base, state):
42 43 44 45
    if base is object:
        obj = object.__new__(cls)
    else:
        obj = base.__new__(cls, state)
46 47
        if base.__init__ != object.__init__:
            base.__init__(obj, state)
48 49 50 51
    return obj

_HEAPTYPE = 1<<9

52 53 54 55
# Python code for object.__reduce_ex__ for protocols 0 and 1

def _reduce_ex(self, proto):
    assert proto < 2
56 57
    cls = self.__class__
    for base in cls.__mro__:
Guido van Rossum's avatar
Guido van Rossum committed
58
        if hasattr(base, '__flags__') and not base.__flags__ & _HEAPTYPE:
59 60 61 62 63 64
            break
    else:
        base = object # not really reachable
    if base is object:
        state = None
    else:
65 66
        if base is cls:
            raise TypeError(f"cannot pickle {cls.__name__!r} object")
67
        state = base(self)
68
    args = (cls, base, state)
69
    try:
Guido van Rossum's avatar
Guido van Rossum committed
70
        getstate = self.__getstate__
71
    except AttributeError:
72
        if getattr(self, "__slots__", None):
73 74 75 76
            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
Guido van Rossum's avatar
Guido van Rossum committed
77 78 79 80 81 82
        try:
            dict = self.__dict__
        except AttributeError:
            dict = None
    else:
        dict = getstate()
83 84 85 86
    if dict:
        return _reconstructor, args, dict
    else:
        return _reconstructor, args
87

88
# Helper for __reduce_ex__ protocol 2
89 90 91 92

def __newobj__(cls, *args):
    return cls.__new__(cls, *args)

93 94 95 96 97 98
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)

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
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__:
124 125
                slots = c.__dict__['__slots__']
                # if class has a single slot, it can be given as a string
126
                if isinstance(slots, str):
127 128 129 130 131 132 133
                    slots = (slots,)
                for name in slots:
                    # special descriptors
                    if name in ("__dict__", "__weakref__"):
                        continue
                    # mangled names
                    elif name.startswith('__') and not name.endswith('__'):
134 135 136 137 138
                        stripped = c.__name__.lstrip('_')
                        if stripped:
                            names.append('_%s%s' % (stripped, name))
                        else:
                            names.append(name)
139 140
                    else:
                        names.append(name)
141 142 143 144 145 146 147 148 149

    # 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

150 151 152 153 154 155 156 157 158
# 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.

159 160 161
_extension_registry = {}                # key -> code
_inverted_registry = {}                 # code -> key
_extension_cache = {}                   # code -> object
162
# Don't ever rebind those names:  pickling grabs a reference to them when
163
# it's initialized, and won't see a rebinding.
164 165 166 167

def add_extension(module, name, code):
    """Register an extension code."""
    code = int(code)
168
    if not 1 <= code <= 0x7fffffff:
169
        raise ValueError("code out of range")
170
    key = (module, name)
171 172
    if (_extension_registry.get(key) == code and
        _inverted_registry.get(code) == key):
173
        return # Redundant registrations are benign
174
    if key in _extension_registry:
175
        raise ValueError("key %s is already registered with code %s" %
176 177
                         (key, _extension_registry[key]))
    if code in _inverted_registry:
178
        raise ValueError("code %s is already in use for key %s" %
179 180 181
                         (code, _inverted_registry[code]))
    _extension_registry[key] = code
    _inverted_registry[code] = key
182 183 184 185

def remove_extension(module, name, code):
    """Unregister an extension code.  For testing only."""
    key = (module, name)
186 187
    if (_extension_registry.get(key) != code or
        _inverted_registry.get(code) != key):
188 189
        raise ValueError("key %s is not registered with code %s" %
                         (key, code))
190 191 192 193
    del _extension_registry[key]
    del _inverted_registry[code]
    if code in _extension_cache:
        del _extension_cache[code]
194 195

def clear_extension_cache():
196
    _extension_cache.clear()
197 198 199 200 201 202 203

# Standard extension code assignments

# Reserved ranges

# First  Last Count  Purpose
#     1   127   127  Reserved for Python standard library
204
#   128   191    64  Reserved for Zope
205 206 207 208 209
#   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.