forked from bton/matekasse
115 lines
3.3 KiB
Python
115 lines
3.3 KiB
Python
|
import logging
|
||
|
import pickle
|
||
|
import typing as _t
|
||
|
|
||
|
|
||
|
class BaseSerializer:
|
||
|
"""This is the base interface for all default serializers.
|
||
|
|
||
|
BaseSerializer.load and BaseSerializer.dump will
|
||
|
default to pickle.load and pickle.dump. This is currently
|
||
|
used only by FileSystemCache which dumps/loads to/from a file stream.
|
||
|
"""
|
||
|
|
||
|
def _warn(self, e: pickle.PickleError) -> None:
|
||
|
logging.warning(
|
||
|
f"An exception has been raised during a pickling operation: {e}"
|
||
|
)
|
||
|
|
||
|
def dump(
|
||
|
self, value: int, f: _t.IO, protocol: int = pickle.HIGHEST_PROTOCOL
|
||
|
) -> None:
|
||
|
try:
|
||
|
pickle.dump(value, f, protocol)
|
||
|
except (pickle.PickleError, pickle.PicklingError) as e:
|
||
|
self._warn(e)
|
||
|
|
||
|
def load(self, f: _t.BinaryIO) -> _t.Any:
|
||
|
try:
|
||
|
data = pickle.load(f)
|
||
|
except pickle.PickleError as e:
|
||
|
self._warn(e)
|
||
|
return None
|
||
|
else:
|
||
|
return data
|
||
|
|
||
|
"""BaseSerializer.loads and BaseSerializer.dumps
|
||
|
work on top of pickle.loads and pickle.dumps. Dumping/loading
|
||
|
strings and byte strings is the default for most cache types.
|
||
|
"""
|
||
|
|
||
|
def dumps(self, value: _t.Any, protocol: int = pickle.HIGHEST_PROTOCOL) -> bytes:
|
||
|
try:
|
||
|
serialized = pickle.dumps(value, protocol)
|
||
|
except (pickle.PickleError, pickle.PicklingError) as e:
|
||
|
self._warn(e)
|
||
|
return serialized
|
||
|
|
||
|
def loads(self, bvalue: bytes) -> _t.Any:
|
||
|
try:
|
||
|
data = pickle.loads(bvalue)
|
||
|
except pickle.PickleError as e:
|
||
|
self._warn(e)
|
||
|
return None
|
||
|
else:
|
||
|
return data
|
||
|
|
||
|
|
||
|
"""Default serializers for each cache type.
|
||
|
|
||
|
The following classes can be used to further customize
|
||
|
serialiation behaviour. Alternatively, any serializer can be
|
||
|
overriden in order to use a custom serializer with a different
|
||
|
strategy altogether.
|
||
|
"""
|
||
|
|
||
|
|
||
|
class UWSGISerializer(BaseSerializer):
|
||
|
"""Default serializer for UWSGICache."""
|
||
|
|
||
|
|
||
|
class SimpleSerializer(BaseSerializer):
|
||
|
"""Default serializer for SimpleCache."""
|
||
|
|
||
|
|
||
|
class FileSystemSerializer(BaseSerializer):
|
||
|
"""Default serializer for FileSystemCache."""
|
||
|
|
||
|
|
||
|
class RedisSerializer(BaseSerializer):
|
||
|
"""Default serializer for RedisCache."""
|
||
|
|
||
|
def dumps(self, value: _t.Any, protocol: int = pickle.HIGHEST_PROTOCOL) -> bytes:
|
||
|
"""Dumps an object into a string for redis. By default it serializes
|
||
|
integers as regular string and pickle dumps everything else.
|
||
|
"""
|
||
|
return b"!" + pickle.dumps(value, protocol)
|
||
|
|
||
|
def loads(self, value: _t.Optional[bytes]) -> _t.Any:
|
||
|
"""The reversal of :meth:`dump_object`. This might be called with
|
||
|
None.
|
||
|
"""
|
||
|
if value is None:
|
||
|
return None
|
||
|
if value.startswith(b"!"):
|
||
|
try:
|
||
|
return pickle.loads(value[1:])
|
||
|
except pickle.PickleError:
|
||
|
return None
|
||
|
try:
|
||
|
return int(value)
|
||
|
except ValueError:
|
||
|
# before 0.8 we did not have serialization. Still support that.
|
||
|
return value
|
||
|
|
||
|
|
||
|
class DynamoDbSerializer(RedisSerializer):
|
||
|
"""Default serializer for DynamoDbCache."""
|
||
|
|
||
|
def loads(self, value: _t.Any) -> _t.Any:
|
||
|
"""The reversal of :meth:`dump_object`. This might be called with
|
||
|
None.
|
||
|
"""
|
||
|
value = value.value
|
||
|
return super().loads(value)
|