• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc.  All rights reserved.
3#
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file or at
6# https://developers.google.com/open-source/licenses/bsd
7
8# TODO: We should just make these methods all "pure-virtual" and move
9# all implementation out, into reflection.py for now.
10
11
12"""Contains an abstract base class for protocol messages."""
13
14__author__ = 'robinson@google.com (Will Robinson)'
15
16class Error(Exception):
17  """Base error type for this module."""
18  pass
19
20
21class DecodeError(Error):
22  """Exception raised when deserializing messages."""
23  pass
24
25
26class EncodeError(Error):
27  """Exception raised when serializing messages."""
28  pass
29
30
31class Message(object):
32
33  """Abstract base class for protocol messages.
34
35  Protocol message classes are almost always generated by the protocol
36  compiler.  These generated types subclass Message and implement the methods
37  shown below.
38  """
39
40  # TODO: Link to an HTML document here.
41
42  # TODO: Document that instances of this class will also
43  # have an Extensions attribute with __getitem__ and __setitem__.
44  # Again, not sure how to best convey this.
45
46  # TODO: Document these fields and methods.
47
48  __slots__ = []
49
50  #: The :class:`google.protobuf.Descriptor`
51  # for this message type.
52  DESCRIPTOR = None
53
54  def __deepcopy__(self, memo=None):
55    clone = type(self)()
56    clone.MergeFrom(self)
57    return clone
58
59  def __eq__(self, other_msg):
60    """Recursively compares two messages by value and structure."""
61    raise NotImplementedError
62
63  def __ne__(self, other_msg):
64    # Can't just say self != other_msg, since that would infinitely recurse. :)
65    return not self == other_msg
66
67  def __hash__(self):
68    raise TypeError('unhashable object')
69
70  def __str__(self):
71    """Outputs a human-readable representation of the message."""
72    raise NotImplementedError
73
74  def __unicode__(self):
75    """Outputs a human-readable representation of the message."""
76    raise NotImplementedError
77
78  def __contains__(self, field_name_or_key):
79    """Checks if a certain field is set for the message.
80
81    Has presence fields return true if the field is set, false if the field is
82    not set. Fields without presence do raise `ValueError` (this includes
83    repeated fields, map fields, and implicit presence fields).
84
85    If field_name is not defined in the message descriptor, `ValueError` will
86    be raised.
87    Note: WKT Struct checks if the key is contained in fields. ListValue checks
88    if the item is contained in the list.
89
90    Args:
91      field_name_or_key: For Struct, the key (str) of the fields map. For
92        ListValue, any type that may be contained in the list. For other
93        messages, name of the field (str) to check for presence.
94
95    Returns:
96      bool: For Struct, whether the item is contained in fields. For ListValue,
97            whether the item is contained in the list. For other message,
98            whether a value has been set for the named field.
99
100    Raises:
101      ValueError: For normal messages,  if the `field_name_or_key` is not a
102                  member of this message or `field_name_or_key` is not a string.
103    """
104    raise NotImplementedError
105
106  def MergeFrom(self, other_msg):
107    """Merges the contents of the specified message into current message.
108
109    This method merges the contents of the specified message into the current
110    message. Singular fields that are set in the specified message overwrite
111    the corresponding fields in the current message. Repeated fields are
112    appended. Singular sub-messages and groups are recursively merged.
113
114    Args:
115      other_msg (Message): A message to merge into the current message.
116    """
117    raise NotImplementedError
118
119  def CopyFrom(self, other_msg):
120    """Copies the content of the specified message into the current message.
121
122    The method clears the current message and then merges the specified
123    message using MergeFrom.
124
125    Args:
126      other_msg (Message): A message to copy into the current one.
127    """
128    if self is other_msg:
129      return
130    self.Clear()
131    self.MergeFrom(other_msg)
132
133  def Clear(self):
134    """Clears all data that was set in the message."""
135    raise NotImplementedError
136
137  def SetInParent(self):
138    """Mark this as present in the parent.
139
140    This normally happens automatically when you assign a field of a
141    sub-message, but sometimes you want to make the sub-message
142    present while keeping it empty.  If you find yourself using this,
143    you may want to reconsider your design.
144    """
145    raise NotImplementedError
146
147  def IsInitialized(self):
148    """Checks if the message is initialized.
149
150    Returns:
151      bool: The method returns True if the message is initialized (i.e. all of
152      its required fields are set).
153    """
154    raise NotImplementedError
155
156  # TODO: MergeFromString() should probably return None and be
157  # implemented in terms of a helper that returns the # of bytes read.  Our
158  # deserialization routines would use the helper when recursively
159  # deserializing, but the end user would almost always just want the no-return
160  # MergeFromString().
161
162  def MergeFromString(self, serialized):
163    """Merges serialized protocol buffer data into this message.
164
165    When we find a field in `serialized` that is already present
166    in this message:
167
168    -   If it's a "repeated" field, we append to the end of our list.
169    -   Else, if it's a scalar, we overwrite our field.
170    -   Else, (it's a nonrepeated composite), we recursively merge
171        into the existing composite.
172
173    Args:
174      serialized (bytes): Any object that allows us to call
175        ``memoryview(serialized)`` to access a string of bytes using the
176        buffer interface.
177
178    Returns:
179      int: The number of bytes read from `serialized`.
180      For non-group messages, this will always be `len(serialized)`,
181      but for messages which are actually groups, this will
182      generally be less than `len(serialized)`, since we must
183      stop when we reach an ``END_GROUP`` tag.  Note that if
184      we *do* stop because of an ``END_GROUP`` tag, the number
185      of bytes returned does not include the bytes
186      for the ``END_GROUP`` tag information.
187
188    Raises:
189      DecodeError: if the input cannot be parsed.
190    """
191    # TODO: Document handling of unknown fields.
192    # TODO: When we switch to a helper, this will return None.
193    raise NotImplementedError
194
195  def ParseFromString(self, serialized):
196    """Parse serialized protocol buffer data in binary form into this message.
197
198    Like :func:`MergeFromString()`, except we clear the object first.
199
200    Raises:
201      message.DecodeError if the input cannot be parsed.
202    """
203    self.Clear()
204    return self.MergeFromString(serialized)
205
206  def SerializeToString(self, **kwargs):
207    """Serializes the protocol message to a binary string.
208
209    Keyword Args:
210      deterministic (bool): If true, requests deterministic serialization
211        of the protobuf, with predictable ordering of map keys.
212
213    Returns:
214      A binary string representation of the message if all of the required
215      fields in the message are set (i.e. the message is initialized).
216
217    Raises:
218      EncodeError: if the message isn't initialized (see :func:`IsInitialized`).
219    """
220    raise NotImplementedError
221
222  def SerializePartialToString(self, **kwargs):
223    """Serializes the protocol message to a binary string.
224
225    This method is similar to SerializeToString but doesn't check if the
226    message is initialized.
227
228    Keyword Args:
229      deterministic (bool): If true, requests deterministic serialization
230        of the protobuf, with predictable ordering of map keys.
231
232    Returns:
233      bytes: A serialized representation of the partial message.
234    """
235    raise NotImplementedError
236
237  # TODO: Decide whether we like these better
238  # than auto-generated has_foo() and clear_foo() methods
239  # on the instances themselves.  This way is less consistent
240  # with C++, but it makes reflection-type access easier and
241  # reduces the number of magically autogenerated things.
242  #
243  # TODO: Be sure to document (and test) exactly
244  # which field names are accepted here.  Are we case-sensitive?
245  # What do we do with fields that share names with Python keywords
246  # like 'lambda' and 'yield'?
247  #
248  # nnorwitz says:
249  # """
250  # Typically (in python), an underscore is appended to names that are
251  # keywords. So they would become lambda_ or yield_.
252  # """
253  def ListFields(self):
254    """Returns a list of (FieldDescriptor, value) tuples for present fields.
255
256    A message field is non-empty if HasField() would return true. A singular
257    primitive field is non-empty if HasField() would return true in proto2 or it
258    is non zero in proto3. A repeated field is non-empty if it contains at least
259    one element. The fields are ordered by field number.
260
261    Returns:
262      list[tuple(FieldDescriptor, value)]: field descriptors and values
263      for all fields in the message which are not empty. The values vary by
264      field type.
265    """
266    raise NotImplementedError
267
268  def HasField(self, field_name):
269    """Checks if a certain field is set for the message.
270
271    For a oneof group, checks if any field inside is set. Note that if the
272    field_name is not defined in the message descriptor, :exc:`ValueError` will
273    be raised.
274
275    Args:
276      field_name (str): The name of the field to check for presence.
277
278    Returns:
279      bool: Whether a value has been set for the named field.
280
281    Raises:
282      ValueError: if the `field_name` is not a member of this message.
283    """
284    raise NotImplementedError
285
286  def ClearField(self, field_name):
287    """Clears the contents of a given field.
288
289    Inside a oneof group, clears the field set. If the name neither refers to a
290    defined field or oneof group, :exc:`ValueError` is raised.
291
292    Args:
293      field_name (str): The name of the field to check for presence.
294
295    Raises:
296      ValueError: if the `field_name` is not a member of this message.
297    """
298    raise NotImplementedError
299
300  def WhichOneof(self, oneof_group):
301    """Returns the name of the field that is set inside a oneof group.
302
303    If no field is set, returns None.
304
305    Args:
306      oneof_group (str): the name of the oneof group to check.
307
308    Returns:
309      str or None: The name of the group that is set, or None.
310
311    Raises:
312      ValueError: no group with the given name exists
313    """
314    raise NotImplementedError
315
316  def HasExtension(self, field_descriptor):
317    """Checks if a certain extension is present for this message.
318
319    Extensions are retrieved using the :attr:`Extensions` mapping (if present).
320
321    Args:
322      field_descriptor: The field descriptor for the extension to check.
323
324    Returns:
325      bool: Whether the extension is present for this message.
326
327    Raises:
328      KeyError: if the extension is repeated. Similar to repeated fields,
329        there is no separate notion of presence: a "not present" repeated
330        extension is an empty list.
331    """
332    raise NotImplementedError
333
334  def ClearExtension(self, field_descriptor):
335    """Clears the contents of a given extension.
336
337    Args:
338      field_descriptor: The field descriptor for the extension to clear.
339    """
340    raise NotImplementedError
341
342  def UnknownFields(self):
343    """Returns the UnknownFieldSet.
344
345    Returns:
346      UnknownFieldSet: The unknown fields stored in this message.
347    """
348    raise NotImplementedError
349
350  def DiscardUnknownFields(self):
351    """Clears all fields in the :class:`UnknownFieldSet`.
352
353    This operation is recursive for nested message.
354    """
355    raise NotImplementedError
356
357  def ByteSize(self):
358    """Returns the serialized size of this message.
359
360    Recursively calls ByteSize() on all contained messages.
361
362    Returns:
363      int: The number of bytes required to serialize this message.
364    """
365    raise NotImplementedError
366
367  @classmethod
368  def FromString(cls, s):
369    raise NotImplementedError
370
371  def _SetListener(self, message_listener):
372    """Internal method used by the protocol message implementation.
373    Clients should not call this directly.
374
375    Sets a listener that this message will call on certain state transitions.
376
377    The purpose of this method is to register back-edges from children to
378    parents at runtime, for the purpose of setting "has" bits and
379    byte-size-dirty bits in the parent and ancestor objects whenever a child or
380    descendant object is modified.
381
382    If the client wants to disconnect this Message from the object tree, she
383    explicitly sets callback to None.
384
385    If message_listener is None, unregisters any existing listener.  Otherwise,
386    message_listener must implement the MessageListener interface in
387    internal/message_listener.py, and we discard any listener registered
388    via a previous _SetListener() call.
389    """
390    raise NotImplementedError
391
392  def __getstate__(self):
393    """Support the pickle protocol."""
394    return dict(serialized=self.SerializePartialToString())
395
396  def __setstate__(self, state):
397    """Support the pickle protocol."""
398    self.__init__()
399    serialized = state['serialized']
400    # On Python 3, using encoding='latin1' is required for unpickling
401    # protos pickled by Python 2.
402    if not isinstance(serialized, bytes):
403      serialized = serialized.encode('latin1')
404    self.ParseFromString(serialized)
405
406  def __reduce__(self):
407    message_descriptor = self.DESCRIPTOR
408    if message_descriptor.containing_type is None:
409      return type(self), (), self.__getstate__()
410    # the message type must be nested.
411    # Python does not pickle nested classes; use the symbol_database on the
412    # receiving end.
413    container = message_descriptor
414    return (_InternalConstructMessage, (container.full_name,),
415            self.__getstate__())
416
417
418def _InternalConstructMessage(full_name):
419  """Constructs a nested message."""
420  from google.protobuf import symbol_database  # pylint:disable=g-import-not-at-top
421
422  return symbol_database.Default().GetSymbol(full_name)()
423