message.py revision d0332953cda33fb4f8e24ebff9c49159b69c43d6
1# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc.  All rights reserved.
3# http://code.google.com/p/protobuf/
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9#     * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#     * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15#     * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31# TODO(robinson): We should just make these methods all "pure-virtual" and move
32# all implementation out, into reflection.py for now.
33
34
35"""Contains an abstract base class for protocol messages."""
36
37__author__ = 'robinson@google.com (Will Robinson)'
38
39
40class Error(Exception): pass
41class DecodeError(Error): pass
42class EncodeError(Error): pass
43
44
45class Message(object):
46
47  """Abstract base class for protocol messages.
48
49  Protocol message classes are almost always generated by the protocol
50  compiler.  These generated types subclass Message and implement the methods
51  shown below.
52
53  TODO(robinson): Link to an HTML document here.
54
55  TODO(robinson): Document that instances of this class will also
56  have an Extensions attribute with __getitem__ and __setitem__.
57  Again, not sure how to best convey this.
58
59  TODO(robinson): Document that the class must also have a static
60    RegisterExtension(extension_field) method.
61    Not sure how to best express at this point.
62  """
63
64  # TODO(robinson): Document these fields and methods.
65
66  __slots__ = []
67
68  DESCRIPTOR = None
69
70  def __eq__(self, other_msg):
71    raise NotImplementedError
72
73  def __ne__(self, other_msg):
74    # Can't just say self != other_msg, since that would infinitely recurse. :)
75    return not self == other_msg
76
77  def __str__(self):
78    raise NotImplementedError
79
80  def MergeFrom(self, other_msg):
81    """Merges the contents of the specified message into current message.
82
83    This method merges the contents of the specified message into the current
84    message. Singular fields that are set in the specified message overwrite
85    the corresponding fields in the current message. Repeated fields are
86    appended. Singular sub-messages and groups are recursively merged.
87
88    Args:
89      other_msg: Message to merge into the current message.
90    """
91    raise NotImplementedError
92
93  def CopyFrom(self, other_msg):
94    """Copies the content of the specified message into the current message.
95
96    The method clears the current message and then merges the specified
97    message using MergeFrom.
98
99    Args:
100      other_msg: Message to copy into the current one.
101    """
102    if self is other_msg:
103      return
104    self.Clear()
105    self.MergeFrom(other_msg)
106
107  def Clear(self):
108    """Clears all data that was set in the message."""
109    raise NotImplementedError
110
111  def SetInParent(self):
112    """Mark this as present in the parent.
113
114    This normally happens automatically when you assign a field of a
115    sub-message, but sometimes you want to make the sub-message
116    present while keeping it empty.  If you find yourself using this,
117    you may want to reconsider your design."""
118    raise NotImplementedError
119
120  def IsInitialized(self):
121    """Checks if the message is initialized.
122
123    Returns:
124      The method returns True if the message is initialized (i.e. all of its
125      required fields are set).
126    """
127    raise NotImplementedError
128
129  # TODO(robinson): MergeFromString() should probably return None and be
130  # implemented in terms of a helper that returns the # of bytes read.  Our
131  # deserialization routines would use the helper when recursively
132  # deserializing, but the end user would almost always just want the no-return
133  # MergeFromString().
134
135  def MergeFromString(self, serialized):
136    """Merges serialized protocol buffer data into this message.
137
138    When we find a field in |serialized| that is already present
139    in this message:
140      - If it's a "repeated" field, we append to the end of our list.
141      - Else, if it's a scalar, we overwrite our field.
142      - Else, (it's a nonrepeated composite), we recursively merge
143        into the existing composite.
144
145    TODO(robinson): Document handling of unknown fields.
146
147    Args:
148      serialized: Any object that allows us to call buffer(serialized)
149        to access a string of bytes using the buffer interface.
150
151    TODO(robinson): When we switch to a helper, this will return None.
152
153    Returns:
154      The number of bytes read from |serialized|.
155      For non-group messages, this will always be len(serialized),
156      but for messages which are actually groups, this will
157      generally be less than len(serialized), since we must
158      stop when we reach an END_GROUP tag.  Note that if
159      we *do* stop because of an END_GROUP tag, the number
160      of bytes returned does not include the bytes
161      for the END_GROUP tag information.
162    """
163    raise NotImplementedError
164
165  def ParseFromString(self, serialized):
166    """Like MergeFromString(), except we clear the object first."""
167    self.Clear()
168    self.MergeFromString(serialized)
169
170  def SerializeToString(self):
171    """Serializes the protocol message to a binary string.
172
173    Returns:
174      A binary string representation of the message if all of the required
175      fields in the message are set (i.e. the message is initialized).
176
177    Raises:
178      message.EncodeError if the message isn't initialized.
179    """
180    raise NotImplementedError
181
182  def SerializePartialToString(self):
183    """Serializes the protocol message to a binary string.
184
185    This method is similar to SerializeToString but doesn't check if the
186    message is initialized.
187
188    Returns:
189      A string representation of the partial message.
190    """
191    raise NotImplementedError
192
193  # TODO(robinson): Decide whether we like these better
194  # than auto-generated has_foo() and clear_foo() methods
195  # on the instances themselves.  This way is less consistent
196  # with C++, but it makes reflection-type access easier and
197  # reduces the number of magically autogenerated things.
198  #
199  # TODO(robinson): Be sure to document (and test) exactly
200  # which field names are accepted here.  Are we case-sensitive?
201  # What do we do with fields that share names with Python keywords
202  # like 'lambda' and 'yield'?
203  #
204  # nnorwitz says:
205  # """
206  # Typically (in python), an underscore is appended to names that are
207  # keywords. So they would become lambda_ or yield_.
208  # """
209  def ListFields(self):
210    """Returns a list of (FieldDescriptor, value) tuples for all
211    fields in the message which are not empty.  A singular field is non-empty
212    if HasField() would return true, and a repeated field is non-empty if
213    it contains at least one element.  The fields are ordered by field
214    number"""
215    raise NotImplementedError
216
217  def HasField(self, field_name):
218    raise NotImplementedError
219
220  def ClearField(self, field_name):
221    raise NotImplementedError
222
223  def HasExtension(self, extension_handle):
224    raise NotImplementedError
225
226  def ClearExtension(self, extension_handle):
227    raise NotImplementedError
228
229  def ByteSize(self):
230    """Returns the serialized size of this message.
231    Recursively calls ByteSize() on all contained messages.
232    """
233    raise NotImplementedError
234
235  def _SetListener(self, message_listener):
236    """Internal method used by the protocol message implementation.
237    Clients should not call this directly.
238
239    Sets a listener that this message will call on certain state transitions.
240
241    The purpose of this method is to register back-edges from children to
242    parents at runtime, for the purpose of setting "has" bits and
243    byte-size-dirty bits in the parent and ancestor objects whenever a child or
244    descendant object is modified.
245
246    If the client wants to disconnect this Message from the object tree, she
247    explicitly sets callback to None.
248
249    If message_listener is None, unregisters any existing listener.  Otherwise,
250    message_listener must implement the MessageListener interface in
251    internal/message_listener.py, and we discard any listener registered
252    via a previous _SetListener() call.
253    """
254    raise NotImplementedError
255