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