1 import logging 2 import pickle 3 import typing as _t 4 5 6 class BaseSerializer: 7 """This is the base interface for all default serializers. 8 9 BaseSerializer.load and BaseSerializer.dump will 10 default to pickle.load and pickle.dump. This is currently 11 used only by FileSystemCache which dumps/loads to/from a file stream. 12 """ 13 14 def _warn(self, e: pickle.PickleError) -> None: 15 logging.warning( 16 f"An exception has been raised during a pickling operation: {e}" 17 ) 18 19 def dump( 20 self, value: int, f: _t.IO, protocol: int = pickle.HIGHEST_PROTOCOL 21 ) -> None: 22 try: 23 pickle.dump(value, f, protocol) 24 except (pickle.PickleError, pickle.PicklingError) as e: 25 self._warn(e) 26 27 def load(self, f: _t.BinaryIO) -> _t.Any: 28 try: 29 data = pickle.load(f) 30 except pickle.PickleError as e: 31 self._warn(e) 32 return None 33 else: 34 return data 35 36 """BaseSerializer.loads and BaseSerializer.dumps 37 work on top of pickle.loads and pickle.dumps. Dumping/loading 38 strings and byte strings is the default for most cache types. 39 """ 40 41 def dumps(self, value: _t.Any, protocol: int = pickle.HIGHEST_PROTOCOL) -> bytes: 42 try: 43 serialized = pickle.dumps(value, protocol) 44 except (pickle.PickleError, pickle.PicklingError) as e: 45 self._warn(e) 46 return serialized 47 48 def loads(self, bvalue: bytes) -> _t.Any: 49 try: 50 data = pickle.loads(bvalue) 51 except pickle.PickleError as e: 52 self._warn(e) 53 return None 54 else: 55 return data 56 57 58 """Default serializers for each cache type. 59 60 The following classes can be used to further customize 61 serialiation behaviour. Alternatively, any serializer can be 62 overriden in order to use a custom serializer with a different 63 strategy altogether. 64 """ 65 66 67 class UWSGISerializer(BaseSerializer): 68 """Default serializer for UWSGICache.""" 69 70 71 class SimpleSerializer(BaseSerializer): 72 """Default serializer for SimpleCache.""" 73 74 75 class FileSystemSerializer(BaseSerializer): 76 """Default serializer for FileSystemCache.""" 77 78 79 class RedisSerializer(BaseSerializer): 80 """Default serializer for RedisCache.""" 81 82 def dumps(self, value: _t.Any, protocol: int = pickle.HIGHEST_PROTOCOL) -> bytes: 83 """Dumps an object into a string for redis, using pickle by default.""" 84 return b"!" + pickle.dumps(value, protocol) 85 86 def loads(self, value: _t.Optional[bytes]) -> _t.Any: 87 """The reversal of :meth:`dump_object`. This might be called with 88 None. 89 """ 90 if value is None: 91 return None 92 if value.startswith(b"!"): 93 try: 94 return pickle.loads(value[1:]) 95 except pickle.PickleError: 96 return None 97 try: 98 return int(value) 99 except ValueError: 100 # before 0.8 we did not have serialization. Still support that. 101 return value 102 103 104 class DynamoDbSerializer(RedisSerializer): 105 """Default serializer for DynamoDbCache.""" 106 107 def loads(self, value: _t.Any) -> _t.Any: 108 """The reversal of :meth:`dump_object`. This might be called with 109 None. 110 """ 111 value = value.value 112 return super().loads(value) 113