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