You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
6.1 KiB
189 lines
6.1 KiB
"""Helper to provide extensibility for pickle/cPickle. |
|
|
|
This is only useful to add pickle support for extension types defined in |
|
C, not for instances of user-defined classes. |
|
""" |
|
|
|
from types import ClassType as _ClassType |
|
|
|
__all__ = ["pickle", "constructor", |
|
"add_extension", "remove_extension", "clear_extension_cache"] |
|
|
|
dispatch_table = {} |
|
|
|
def pickle(ob_type, pickle_function, constructor_ob=None): |
|
if type(ob_type) is _ClassType: |
|
raise TypeError("copy_reg is not intended for use with classes") |
|
|
|
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) |
|
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__: |
|
names += [name for name in c.__dict__["__slots__"] |
|
if name not in ("__dict__", "__weakref__")] |
|
|
|
# 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: cPickle 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.
|
|
|