serde.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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_COMPRESSED = 1 << 3 # unused, to main compatibility with python-memcached
  24. FLAG_PICKLE = 1 << 0
  25. FLAG_UNICODE = 1 << 6
  26. # to main compatibility with java memcache
  27. FLAG_INTEGER = 1 << 2
  28. FLAG_INTEGER_OLD = 1 << 1
  29. FLAG_STRING = 1 << 5
  30. FLAG_LONG = 1 << 14
  31. # Pickle protocol version (highest available to runtime)
  32. # Warning with `0`: If somewhere in your value lies a slotted object,
  33. # ie defines `__slots__`, even if you do not include it in your pickleable
  34. # state via `__getstate__`, python will complain with something like:
  35. # TypeError: a class that defines __slots__ without defining __getstate__
  36. # cannot be pickled
  37. DEFAULT_PICKLE_VERSION = pickle.HIGHEST_PROTOCOL
  38. def _python_memcache_serializer(key, value, pickle_version=None):
  39. flags = 0
  40. value_type = type(value)
  41. # Check against exact types so that subclasses of native types will be
  42. # restored as their native type
  43. if value_type is bytes:
  44. flags |= FLAG_STRING
  45. elif value_type is six.text_type:
  46. flags |= FLAG_UNICODE
  47. value = value.encode('utf8')
  48. elif value_type is int:
  49. flags |= FLAG_INTEGER
  50. value = "%d" % value
  51. elif six.PY2 and value_type is long_type:
  52. flags |= FLAG_LONG
  53. value = "%d" % value
  54. else:
  55. flags |= FLAG_PICKLE
  56. output = BytesIO()
  57. pickler = pickle.Pickler(output, pickle_version)
  58. pickler.dump(value)
  59. value = output.getvalue()
  60. return value, flags
  61. def get_python_memcache_serializer(pickle_version=DEFAULT_PICKLE_VERSION):
  62. """Return a serializer using a specific pickle version"""
  63. return partial(_python_memcache_serializer, pickle_version=pickle_version)
  64. python_memcache_serializer = get_python_memcache_serializer()
  65. def python_memcache_deserializer(key, value, flags):
  66. if flags == 0 or flags == FLAG_STRING:
  67. return value
  68. elif flags & FLAG_UNICODE:
  69. return value.decode('utf8')
  70. elif flags & FLAG_INTEGER:
  71. return int(value)
  72. elif flags & FLAG_INTEGER_OLD:
  73. return int(value)
  74. elif flags & FLAG_LONG:
  75. if six.PY3:
  76. return int(value)
  77. else:
  78. return long_type(value)
  79. elif flags & FLAG_PICKLE:
  80. try:
  81. buf = BytesIO(value)
  82. unpickler = pickle.Unpickler(buf)
  83. return unpickler.load()
  84. except Exception:
  85. logging.info('Pickle error', exc_info=True)
  86. return None
  87. return value
  88. class PickleSerde(object):
  89. """
  90. An object which implements the serialization/deserialization protocol for
  91. :py:class:`pymemcache.client.base.Client` and its descendants using the
  92. :mod:`pickle` module.
  93. Serialization and deserialization are implemented as methods of this class.
  94. To implement a custom serialization/deserialization method for pymemcache,
  95. you should implement the same interface as the one provided by this object
  96. -- :py:meth:`pymemcache.serde.PickleSerde.serialize` and
  97. :py:meth:`pymemcache.serde.PickleSerde.deserialize`. Then,
  98. pass your custom object to the pymemcache client object in place of
  99. `PickleSerde`.
  100. For more details on the serialization protocol, see the class documentation
  101. for :py:class:`pymemcache.client.base.Client`
  102. """
  103. def __init__(self, pickle_version=DEFAULT_PICKLE_VERSION):
  104. self._serialize_func = get_python_memcache_serializer(pickle_version)
  105. def serialize(self, key, value):
  106. return self._serialize_func(key, value)
  107. def deserialize(self, key, value, flags):
  108. return python_memcache_deserializer(key, value, flags)
  109. class LegacyWrappingSerde(object):
  110. """
  111. This class defines how to wrap legacy de/serialization functions into a
  112. 'serde' object which implements '.serialize' and '.deserialize' methods.
  113. It is used automatically by pymemcache.client.base.Client when the
  114. 'serializer' or 'deserializer' arguments are given.
  115. The serializer_func and deserializer_func are expected to be None in the
  116. case that they are missing.
  117. """
  118. def __init__(self, serializer_func, deserializer_func):
  119. self.serialize = serializer_func or self._default_serialize
  120. self.deserialize = deserializer_func or self._default_deserialize
  121. def _default_serialize(self, key, value):
  122. return value, 0
  123. def _default_deserialize(self, key, value, flags):
  124. return value
  125. pickle_serde = PickleSerde()