Refactored ExecutionContext serialization (added sheerkapickle) and added History management
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
import datetime
|
||||
import re
|
||||
import threading
|
||||
import uuid
|
||||
|
||||
from sheerkapickle import utils
|
||||
|
||||
|
||||
class ToReduce:
|
||||
def __init__(self, predicate, get_value):
|
||||
self.predicate = predicate
|
||||
self.get_value = get_value
|
||||
|
||||
|
||||
class SheerkaRegistry(object):
|
||||
|
||||
def __init__(self):
|
||||
self._handlers = {}
|
||||
self._base_handlers = {}
|
||||
|
||||
def get(self, cls_or_name, default=None):
|
||||
"""
|
||||
:param cls_or_name: the type or its fully qualified name
|
||||
:param default: default value, if a matching handler is not found
|
||||
|
||||
Looks up a handler by type reference or its fully
|
||||
qualified name. If a direct match
|
||||
is not found, the search is performed over all
|
||||
handlers registered with base=True.
|
||||
"""
|
||||
handler = self._handlers.get(cls_or_name)
|
||||
# attempt to find a base class
|
||||
if handler is None and utils.is_type(cls_or_name):
|
||||
for cls, base_handler in self._base_handlers.items():
|
||||
if issubclass(cls_or_name, cls):
|
||||
return base_handler
|
||||
return default if handler is None else handler
|
||||
|
||||
def register(self, cls, handler=None, base=False):
|
||||
"""Register the a custom handler for a class
|
||||
|
||||
:param cls: The custom object class to handle
|
||||
:param handler: The custom handler class (if
|
||||
None, a decorator wrapper is returned)
|
||||
:param base: Indicates whether the handler should
|
||||
be registered for all subclasses
|
||||
|
||||
This function can be also used as a decorator
|
||||
by omitting the `handler` argument::
|
||||
|
||||
@jsonpickle.handlers.register(Foo, base=True)
|
||||
class FooHandler(jsonpickle.handlers.BaseHandler):
|
||||
pass
|
||||
|
||||
"""
|
||||
if handler is None:
|
||||
def _register(handler_cls):
|
||||
self.register(cls, handler=handler_cls, base=base)
|
||||
return handler_cls
|
||||
|
||||
return _register
|
||||
if not utils.is_type(cls):
|
||||
raise TypeError('{!r} is not a class/type'.format(cls))
|
||||
# store both the name and the actual type for the ugly cases like
|
||||
# _sre.SRE_Pattern that cannot be loaded back directly
|
||||
self._handlers[utils.importable_name(cls)] = \
|
||||
self._handlers[cls] = handler
|
||||
if base:
|
||||
# only store the actual type for subclass checking
|
||||
self._base_handlers[cls] = handler
|
||||
|
||||
def unregister(self, cls):
|
||||
self._handlers.pop(cls, None)
|
||||
self._handlers.pop(utils.importable_name(cls), None)
|
||||
self._base_handlers.pop(cls, None)
|
||||
|
||||
|
||||
registry = SheerkaRegistry()
|
||||
register = registry.register
|
||||
unregister = registry.unregister
|
||||
get = registry.get
|
||||
|
||||
|
||||
class BaseHandler(object):
|
||||
|
||||
def __init__(self, sheerka, context):
|
||||
"""
|
||||
Initialize a new handler to handle a registered type.
|
||||
|
||||
:Parameters:
|
||||
- `context`: reference to pickler/unpickler
|
||||
|
||||
"""
|
||||
self.sheerka = sheerka
|
||||
self.context = context
|
||||
|
||||
def __call__(self, sheerka, context):
|
||||
"""This permits registering either Handler instances or classes
|
||||
|
||||
:Parameters:
|
||||
- `context`: reference to pickler/unpickler
|
||||
"""
|
||||
self.sheerka = sheerka
|
||||
self.context = context
|
||||
return self
|
||||
|
||||
def flatten(self, obj, data):
|
||||
"""
|
||||
Flatten `obj` into a json-friendly form and write result to `data`.
|
||||
|
||||
:param object obj: The object to be serialized.
|
||||
:param dict data: A partially filled dictionary which will contain the
|
||||
json-friendly representation of `obj` once this method has
|
||||
finished.
|
||||
"""
|
||||
raise NotImplementedError('You must implement flatten() in %s' %
|
||||
self.__class__)
|
||||
|
||||
def new(self, data):
|
||||
raise NotImplementedError('You must implement new() in %s' %
|
||||
self.__class__)
|
||||
|
||||
def restore(self, data, instance):
|
||||
"""
|
||||
Restore an object of the registered type from the json-friendly
|
||||
representation `obj` and return it.
|
||||
"""
|
||||
raise NotImplementedError('You must implement restore() in %s' %
|
||||
self.__class__)
|
||||
|
||||
@classmethod
|
||||
def handles(self, cls):
|
||||
"""
|
||||
Register this handler for the given class. Suitable as a decorator,
|
||||
e.g.::
|
||||
|
||||
@MyCustomHandler.handles
|
||||
class MyCustomClass:
|
||||
def __reduce__(self):
|
||||
...
|
||||
"""
|
||||
registry.register(cls, self)
|
||||
return cls
|
||||
|
||||
|
||||
# class DatetimeHandler(BaseHandler):
|
||||
# """Custom handler for datetime objects
|
||||
#
|
||||
# Datetime objects use __reduce__, and they generate binary strings encoding
|
||||
# the payload. This handler encodes that payload to reconstruct the
|
||||
# object.
|
||||
#
|
||||
# """
|
||||
#
|
||||
# def flatten(self, obj, data):
|
||||
# pickler = self.context
|
||||
# if not pickler.unpicklable:
|
||||
# return str(obj)
|
||||
# cls, args = obj.__reduce__()
|
||||
# flatten = pickler.flatten
|
||||
# payload = utils.b64encode(args[0])
|
||||
# args = [payload] + [flatten(i, reset=False) for i in args[1:]]
|
||||
# data['__reduce__'] = (flatten(cls, reset=False), args)
|
||||
# return data
|
||||
#
|
||||
# def restore(self, data):
|
||||
# cls, args = data['__reduce__']
|
||||
# unpickler = self.context
|
||||
# restore = unpickler.restore
|
||||
# cls = restore(cls, reset=False)
|
||||
# value = utils.b64decode(args[0])
|
||||
# params = (value,) + tuple([restore(i, reset=False) for i in args[1:]])
|
||||
# return cls.__new__(cls, *params)
|
||||
#
|
||||
#
|
||||
# DatetimeHandler.handles(datetime.datetime)
|
||||
# DatetimeHandler.handles(datetime.date)
|
||||
# DatetimeHandler.handles(datetime.time)
|
||||
|
||||
|
||||
class RegexHandler(BaseHandler):
|
||||
"""Flatten _sre.SRE_Pattern (compiled regex) objects"""
|
||||
|
||||
def flatten(self, obj, data):
|
||||
data['pattern'] = obj.pattern
|
||||
return data
|
||||
|
||||
def new(self, data):
|
||||
return re.compile(data['pattern'])
|
||||
|
||||
def restore(self, data, instance):
|
||||
return instance
|
||||
|
||||
|
||||
RegexHandler.handles(type(re.compile('')))
|
||||
|
||||
|
||||
class UUIDHandler(BaseHandler):
|
||||
"""Serialize uuid.UUID objects"""
|
||||
|
||||
def flatten(self, obj, data):
|
||||
data['hex'] = obj.hex
|
||||
return data
|
||||
|
||||
def new(self, data):
|
||||
return uuid.UUID(data['hex'])
|
||||
|
||||
def restore(self, data, instance):
|
||||
return instance
|
||||
|
||||
|
||||
UUIDHandler.handles(uuid.UUID)
|
||||
|
||||
|
||||
class LockHandler(BaseHandler):
|
||||
"""Serialize threading.Lock objects"""
|
||||
|
||||
def flatten(self, obj, data):
|
||||
data['locked'] = obj.locked()
|
||||
return data
|
||||
|
||||
def new(self, data):
|
||||
lock = threading.Lock()
|
||||
if data.get('locked', False):
|
||||
lock.acquire()
|
||||
return lock
|
||||
|
||||
def restore(self, data, instance):
|
||||
return instance
|
||||
|
||||
|
||||
_lock = threading.Lock()
|
||||
LockHandler.handles(_lock.__class__)
|
||||
Reference in New Issue
Block a user