PageRenderTime 52ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/python/protorpc/protobuf.py

https://code.google.com/p/google-protorpc/
Python | 358 lines | 182 code | 52 blank | 124 comment | 32 complexity | 7c98c47b364413471d47ad7c14d59d5f MD5 | raw file
Possible License(s): Apache-2.0
  1. #!/usr/bin/env python
  2. #
  3. # Copyright 2010 Google Inc.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. """Protocol buffer support for message types.
  18. For more details about protocol buffer encoding and decoding please see:
  19. http://code.google.com/apis/protocolbuffers/docs/encoding.html
  20. Public Exceptions:
  21. DecodeError: Raised when a decode error occurs from incorrect protobuf format.
  22. Public Functions:
  23. encode_message: Encodes a message in to a protocol buffer string.
  24. decode_message: Decode from a protocol buffer string to a message.
  25. """
  26. __author__ = 'rafek@google.com (Rafe Kaplan)'
  27. import array
  28. from . import message_types
  29. from . import messages
  30. from . import util
  31. from .google_imports import ProtocolBuffer
  32. __all__ = ['ALTERNATIVE_CONTENT_TYPES',
  33. 'CONTENT_TYPE',
  34. 'encode_message',
  35. 'decode_message',
  36. ]
  37. CONTENT_TYPE = 'application/octet-stream'
  38. ALTERNATIVE_CONTENT_TYPES = ['application/x-google-protobuf']
  39. class _Encoder(ProtocolBuffer.Encoder):
  40. """Extension of protocol buffer encoder.
  41. Original protocol buffer encoder does not have complete set of methods
  42. for handling required encoding. This class adds them.
  43. """
  44. # TODO(rafek): Implement the missing encoding types.
  45. def no_encoding(self, value):
  46. """No encoding available for type.
  47. Args:
  48. value: Value to encode.
  49. Raises:
  50. NotImplementedError at all times.
  51. """
  52. raise NotImplementedError()
  53. def encode_enum(self, value):
  54. """Encode an enum value.
  55. Args:
  56. value: Enum to encode.
  57. """
  58. self.putVarInt32(value.number)
  59. def encode_message(self, value):
  60. """Encode a Message in to an embedded message.
  61. Args:
  62. value: Message instance to encode.
  63. """
  64. self.putPrefixedString(encode_message(value))
  65. def encode_unicode_string(self, value):
  66. """Helper to properly pb encode unicode strings to UTF-8.
  67. Args:
  68. value: String value to encode.
  69. """
  70. if isinstance(value, unicode):
  71. value = value.encode('utf-8')
  72. self.putPrefixedString(value)
  73. class _Decoder(ProtocolBuffer.Decoder):
  74. """Extension of protocol buffer decoder.
  75. Original protocol buffer decoder does not have complete set of methods
  76. for handling required decoding. This class adds them.
  77. """
  78. # TODO(rafek): Implement the missing encoding types.
  79. def no_decoding(self):
  80. """No decoding available for type.
  81. Raises:
  82. NotImplementedError at all times.
  83. """
  84. raise NotImplementedError()
  85. def decode_string(self):
  86. """Decode a unicode string.
  87. Returns:
  88. Next value in stream as a unicode string.
  89. """
  90. return self.getPrefixedString().decode('UTF-8')
  91. def decode_boolean(self):
  92. """Decode a boolean value.
  93. Returns:
  94. Next value in stream as a boolean.
  95. """
  96. return bool(self.getBoolean())
  97. # Number of bits used to describe a protocol buffer bits used for the variant.
  98. _WIRE_TYPE_BITS = 3
  99. _WIRE_TYPE_MASK = 7
  100. # Maps variant to underlying wire type. Many variants map to same type.
  101. _VARIANT_TO_WIRE_TYPE = {
  102. messages.Variant.DOUBLE: _Encoder.DOUBLE,
  103. messages.Variant.FLOAT: _Encoder.FLOAT,
  104. messages.Variant.INT64: _Encoder.NUMERIC,
  105. messages.Variant.UINT64: _Encoder.NUMERIC,
  106. messages.Variant.INT32: _Encoder.NUMERIC,
  107. messages.Variant.BOOL: _Encoder.NUMERIC,
  108. messages.Variant.STRING: _Encoder.STRING,
  109. messages.Variant.MESSAGE: _Encoder.STRING,
  110. messages.Variant.BYTES: _Encoder.STRING,
  111. messages.Variant.UINT32: _Encoder.NUMERIC,
  112. messages.Variant.ENUM: _Encoder.NUMERIC,
  113. messages.Variant.SINT32: _Encoder.NUMERIC,
  114. messages.Variant.SINT64: _Encoder.NUMERIC,
  115. }
  116. # Maps variant to encoder method.
  117. _VARIANT_TO_ENCODER_MAP = {
  118. messages.Variant.DOUBLE: _Encoder.putDouble,
  119. messages.Variant.FLOAT: _Encoder.putFloat,
  120. messages.Variant.INT64: _Encoder.putVarInt64,
  121. messages.Variant.UINT64: _Encoder.putVarUint64,
  122. messages.Variant.INT32: _Encoder.putVarInt32,
  123. messages.Variant.BOOL: _Encoder.putBoolean,
  124. messages.Variant.STRING: _Encoder.encode_unicode_string,
  125. messages.Variant.MESSAGE: _Encoder.encode_message,
  126. messages.Variant.BYTES: _Encoder.encode_unicode_string,
  127. messages.Variant.UINT32: _Encoder.no_encoding,
  128. messages.Variant.ENUM: _Encoder.encode_enum,
  129. messages.Variant.SINT32: _Encoder.no_encoding,
  130. messages.Variant.SINT64: _Encoder.no_encoding,
  131. }
  132. # Basic wire format decoders. Used for reading unknown values.
  133. _WIRE_TYPE_TO_DECODER_MAP = {
  134. _Encoder.NUMERIC: _Decoder.getVarInt64,
  135. _Encoder.DOUBLE: _Decoder.getDouble,
  136. _Encoder.STRING: _Decoder.getPrefixedString,
  137. _Encoder.FLOAT: _Decoder.getFloat,
  138. }
  139. # Map wire type to variant. Used to find a variant for unknown values.
  140. _WIRE_TYPE_TO_VARIANT_MAP = {
  141. _Encoder.NUMERIC: messages.Variant.INT64,
  142. _Encoder.DOUBLE: messages.Variant.DOUBLE,
  143. _Encoder.STRING: messages.Variant.STRING,
  144. _Encoder.FLOAT: messages.Variant.FLOAT,
  145. }
  146. # Wire type to name mapping for error messages.
  147. _WIRE_TYPE_NAME = {
  148. _Encoder.NUMERIC: 'NUMERIC',
  149. _Encoder.DOUBLE: 'DOUBLE',
  150. _Encoder.STRING: 'STRING',
  151. _Encoder.FLOAT: 'FLOAT',
  152. }
  153. # Maps variant to decoder method.
  154. _VARIANT_TO_DECODER_MAP = {
  155. messages.Variant.DOUBLE: _Decoder.getDouble,
  156. messages.Variant.FLOAT: _Decoder.getFloat,
  157. messages.Variant.INT64: _Decoder.getVarInt64,
  158. messages.Variant.UINT64: _Decoder.getVarUint64,
  159. messages.Variant.INT32: _Decoder.getVarInt32,
  160. messages.Variant.BOOL: _Decoder.decode_boolean,
  161. messages.Variant.STRING: _Decoder.decode_string,
  162. messages.Variant.MESSAGE: _Decoder.getPrefixedString,
  163. messages.Variant.BYTES: _Decoder.getPrefixedString,
  164. messages.Variant.UINT32: _Decoder.no_decoding,
  165. messages.Variant.ENUM: _Decoder.getVarInt32,
  166. messages.Variant.SINT32: _Decoder.no_decoding,
  167. messages.Variant.SINT64: _Decoder.no_decoding,
  168. }
  169. def encode_message(message):
  170. """Encode Message instance to protocol buffer.
  171. Args:
  172. Message instance to encode in to protocol buffer.
  173. Returns:
  174. String encoding of Message instance in protocol buffer format.
  175. Raises:
  176. messages.ValidationError if message is not initialized.
  177. """
  178. message.check_initialized()
  179. encoder = _Encoder()
  180. # Get all fields, from the known fields we parsed and the unknown fields
  181. # we saved. Note which ones were known, so we can process them differently.
  182. all_fields = [(field.number, field) for field in message.all_fields()]
  183. all_fields.extend((key, None)
  184. for key in message.all_unrecognized_fields()
  185. if isinstance(key, (int, long)))
  186. all_fields.sort()
  187. for field_num, field in all_fields:
  188. if field:
  189. # Known field.
  190. value = message.get_assigned_value(field.name)
  191. if value is None:
  192. continue
  193. variant = field.variant
  194. repeated = field.repeated
  195. else:
  196. # Unrecognized field.
  197. value, variant = message.get_unrecognized_field_info(field_num)
  198. if not isinstance(variant, messages.Variant):
  199. continue
  200. repeated = isinstance(value, (list, tuple))
  201. tag = ((field_num << _WIRE_TYPE_BITS) | _VARIANT_TO_WIRE_TYPE[variant])
  202. # Write value to wire.
  203. if repeated:
  204. values = value
  205. else:
  206. values = [value]
  207. for next in values:
  208. encoder.putVarInt32(tag)
  209. if isinstance(field, messages.MessageField):
  210. next = field.value_to_message(next)
  211. field_encoder = _VARIANT_TO_ENCODER_MAP[variant]
  212. field_encoder(encoder, next)
  213. return encoder.buffer().tostring()
  214. def decode_message(message_type, encoded_message):
  215. """Decode protocol buffer to Message instance.
  216. Args:
  217. message_type: Message type to decode data to.
  218. encoded_message: Encoded version of message as string.
  219. Returns:
  220. Decoded instance of message_type.
  221. Raises:
  222. DecodeError if an error occurs during decoding, such as incompatible
  223. wire format for a field.
  224. messages.ValidationError if merged message is not initialized.
  225. """
  226. message = message_type()
  227. message_array = array.array('B')
  228. message_array.fromstring(encoded_message)
  229. try:
  230. decoder = _Decoder(message_array, 0, len(message_array))
  231. while decoder.avail() > 0:
  232. # Decode tag and variant information.
  233. encoded_tag = decoder.getVarInt32()
  234. tag = encoded_tag >> _WIRE_TYPE_BITS
  235. wire_type = encoded_tag & _WIRE_TYPE_MASK
  236. try:
  237. found_wire_type_decoder = _WIRE_TYPE_TO_DECODER_MAP[wire_type]
  238. except:
  239. raise messages.DecodeError('No such wire type %d' % wire_type)
  240. if tag < 1:
  241. raise messages.DecodeError('Invalid tag value %d' % tag)
  242. try:
  243. field = message.field_by_number(tag)
  244. except KeyError:
  245. # Unexpected tags are ok.
  246. field = None
  247. wire_type_decoder = found_wire_type_decoder
  248. else:
  249. expected_wire_type = _VARIANT_TO_WIRE_TYPE[field.variant]
  250. if expected_wire_type != wire_type:
  251. raise messages.DecodeError('Expected wire type %s but found %s' % (
  252. _WIRE_TYPE_NAME[expected_wire_type],
  253. _WIRE_TYPE_NAME[wire_type]))
  254. wire_type_decoder = _VARIANT_TO_DECODER_MAP[field.variant]
  255. value = wire_type_decoder(decoder)
  256. # Save unknown fields and skip additional processing.
  257. if not field:
  258. # When saving this, save it under the tag number (which should
  259. # be unique), and set the variant and value so we know how to
  260. # interpret the value later.
  261. variant = _WIRE_TYPE_TO_VARIANT_MAP.get(wire_type)
  262. if variant:
  263. message.set_unrecognized_field(tag, value, variant)
  264. continue
  265. # Special case Enum and Message types.
  266. if isinstance(field, messages.EnumField):
  267. try:
  268. value = field.type(value)
  269. except TypeError:
  270. raise messages.DecodeError('Invalid enum value %s' % value)
  271. elif isinstance(field, messages.MessageField):
  272. value = decode_message(field.message_type, value)
  273. value = field.value_from_message(value)
  274. # Merge value in to message.
  275. if field.repeated:
  276. values = getattr(message, field.name)
  277. if values is None:
  278. setattr(message, field.name, [value])
  279. else:
  280. values.append(value)
  281. else:
  282. setattr(message, field.name, value)
  283. except ProtocolBuffer.ProtocolBufferDecodeError, err:
  284. raise messages.DecodeError('Decoding error: %s' % str(err))
  285. message.check_initialized()
  286. return message