serializers.py 3.3 KB

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