serde.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. # Copyright 2012 Pinterest.com
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from functools import partial
  15. import logging
  16. from io import BytesIO
  17. import six
  18. from six.moves import cPickle as pickle
  19. try:
  20. long_type = long # noqa
  21. except NameError:
  22. long_type = None
  23. FLAG_BYTES = 0
  24. FLAG_PICKLE = 1 << 0
  25. FLAG_INTEGER = 1 << 1
  26. FLAG_LONG = 1 << 2
  27. FLAG_COMPRESSED = 1 << 3 # unused, to main compatibility with python-memcached
  28. FLAG_TEXT = 1 << 4
  29. # Pickle protocol version (highest available to runtime)
  30. # Warning with `0`: If somewhere in your value lies a slotted object,
  31. # ie defines `__slots__`, even if you do not include it in your pickleable
  32. # state via `__getstate__`, python will complain with something like:
  33. # TypeError: a class that defines __slots__ without defining __getstate__
  34. # cannot be pickled
  35. DEFAULT_PICKLE_VERSION = pickle.HIGHEST_PROTOCOL
  36. def _python_memcache_serializer(key, value, pickle_version=None):
  37. flags = 0
  38. value_type = type(value)
  39. # Check against exact types so that subclasses of native types will be
  40. # restored as their native type
  41. if value_type is bytes:
  42. pass
  43. elif value_type is six.text_type:
  44. flags |= FLAG_TEXT
  45. value = value.encode('utf8')
  46. elif value_type is int:
  47. flags |= FLAG_INTEGER
  48. value = "%d" % value
  49. elif six.PY2 and value_type is long_type:
  50. flags |= FLAG_LONG
  51. value = "%d" % value
  52. else:
  53. flags |= FLAG_PICKLE
  54. output = BytesIO()
  55. pickler = pickle.Pickler(output, pickle_version)
  56. pickler.dump(value)
  57. value = output.getvalue()
  58. return value, flags
  59. def get_python_memcache_serializer(pickle_version=DEFAULT_PICKLE_VERSION):
  60. """Return a serializer using a specific pickle version"""
  61. return partial(_python_memcache_serializer, pickle_version=pickle_version)
  62. python_memcache_serializer = get_python_memcache_serializer()
  63. def python_memcache_deserializer(key, value, flags):
  64. if flags == 0:
  65. return value
  66. elif flags & FLAG_TEXT:
  67. return value.decode('utf8')
  68. elif flags & FLAG_INTEGER:
  69. return int(value)
  70. elif flags & FLAG_LONG:
  71. if six.PY3:
  72. return int(value)
  73. else:
  74. return long_type(value)
  75. elif flags & FLAG_PICKLE:
  76. try:
  77. buf = BytesIO(value)
  78. unpickler = pickle.Unpickler(buf)
  79. return unpickler.load()
  80. except Exception:
  81. logging.info('Pickle error', exc_info=True)
  82. return None
  83. return value
  84. class PickleSerde(object):
  85. """
  86. An object which implements the serialization/deserialization protocol for
  87. :py:class:`pymemcache.client.base.Client` and its descendants using the
  88. :mod:`pickle` module.
  89. Serialization and deserialization are implemented as methods of this class.
  90. To implement a custom serialization/deserialization method for pymemcache,
  91. you should implement the same interface as the one provided by this object
  92. -- :py:meth:`pymemcache.serde.PickleSerde.serialize` and
  93. :py:meth:`pymemcache.serde.PickleSerde.deserialize`. Then,
  94. pass your custom object to the pymemcache client object in place of
  95. `PickleSerde`.
  96. For more details on the serialization protocol, see the class documentation
  97. for :py:class:`pymemcache.client.base.Client`
  98. """
  99. def __init__(self, pickle_version=DEFAULT_PICKLE_VERSION):
  100. self._serialize_func = get_python_memcache_serializer(pickle_version)
  101. def serialize(self, key, value):
  102. return self._serialize_func(key, value)
  103. def deserialize(self, key, value, flags):
  104. return python_memcache_deserializer(key, value, flags)
  105. class LegacyWrappingSerde(object):
  106. """
  107. This class defines how to wrap legacy de/serialization functions into a
  108. 'serde' object which implements '.serialize' and '.deserialize' methods.
  109. It is used automatically by pymemcache.client.base.Client when the
  110. 'serializer' or 'deserializer' arguments are given.
  111. The serializer_func and deserializer_func are expected to be None in the
  112. case that they are missing.
  113. """
  114. def __init__(self, serializer_func, deserializer_func):
  115. self.serialize = serializer_func or self._default_serialize
  116. self.deserialize = deserializer_func or self._default_deserialize
  117. def _default_serialize(self, key, value):
  118. return value, 0
  119. def _default_deserialize(self, key, value, flags):
  120. return value
  121. pickle_serde = PickleSerde()