codekingpro/portable-devtools
114k
1# Protocol Buffers - Google's data interchange format2# Copyright 2008 Google Inc. All rights reserved.3#4# Use of this source code is governed by a BSD-style5# license that can be found in the LICENSE file or at6# https://developers.google.com/open-source/licenses/bsd7 8# TODO: We should just make these methods all "pure-virtual" and move9# 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 pass22 23 24class DecodeError(Error):25 """Exception raised when deserializing messages."""26 pass27 28 29class EncodeError(Error):30 """Exception raised when serializing messages."""31 pass32 33 34class Message(object):35 36 """Abstract base class for protocol messages.37 38 Protocol message classes are almost always generated by the protocol39 compiler. These generated types subclass Message and implement the methods40 shown below.41 """42 43 # TODO: Link to an HTML document here.44 45 # TODO: Document that instances of this class will also46 # 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 = None56 57 def __deepcopy__(self, memo=None):58 clone = type(self)()59 clone.MergeFrom(self)60 return clone61 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 as68 # 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 that74 # have to be removed.75 for attribute in _INCONSISTENT_MESSAGE_ATTRIBUTES:76 if attribute not in message_attributes:77 continue78 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 NotImplementedError88 89 def __ne__(self, other_msg):90 # Can't just say self != other_msg, since that would infinitely recurse. :)91 return not self == other_msg92 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 NotImplementedError99 100 def __unicode__(self):101 """Outputs a human-readable representation of the message."""102 raise NotImplementedError103 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 is108 not set. Fields without presence do raise `ValueError` (this includes109 repeated fields, map fields, and implicit presence fields).110 111 If field_name is not defined in the message descriptor, `ValueError` will112 be raised.113 Note: WKT Struct checks if the key is contained in fields. ListValue checks114 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. For118 ListValue, any type that may be contained in the list. For other119 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 a128 member of this message or `field_name_or_key` is not a string.129 """130 raise NotImplementedError131 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 current136 message. Singular fields that are set in the specified message overwrite137 the corresponding fields in the current message. Repeated fields are138 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 NotImplementedError144 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 specified149 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 return156 self.Clear()157 self.MergeFrom(other_msg)158 159 def Clear(self):160 """Clears all data that was set in the message."""161 raise NotImplementedError162 163 def SetInParent(self):164 """Mark this as present in the parent.165 166 This normally happens automatically when you assign a field of a167 sub-message, but sometimes you want to make the sub-message168 present while keeping it empty. If you find yourself using this,169 you may want to reconsider your design.170 """171 raise NotImplementedError172 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 of178 its required fields are set).179 """180 raise NotImplementedError181 182 # TODO: MergeFromString() should probably return None and be183 # implemented in terms of a helper that returns the # of bytes read. Our184 # deserialization routines would use the helper when recursively185 # deserializing, but the end user would almost always just want the no-return186 # 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 present192 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 merge197 into the existing composite.198 199 Args:200 serialized (bytes): Any object that allows us to call201 ``memoryview(serialized)`` to access a string of bytes using the202 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 will208 generally be less than `len(serialized)`, since we must209 stop when we reach an ``END_GROUP`` tag. Note that if210 we *do* stop because of an ``END_GROUP`` tag, the number211 of bytes returned does not include the bytes212 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 NotImplementedError220 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 serialization237 of the protobuf, with predictable ordering of map keys.238 239 Returns:240 A binary string representation of the message if all of the required241 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 NotImplementedError247 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 the252 message is initialized.253 254 Keyword Args:255 deterministic (bool): If true, requests deterministic serialization256 of the protobuf, with predictable ordering of map keys.257 258 Returns:259 bytes: A serialized representation of the partial message.260 """261 raise NotImplementedError262 263 # TODO: Decide whether we like these better264 # than auto-generated has_foo() and clear_foo() methods265 # on the instances themselves. This way is less consistent266 # with C++, but it makes reflection-type access easier and267 # reduces the number of magically autogenerated things.268 #269 # TODO: Be sure to document (and test) exactly270 # which field names are accepted here. Are we case-sensitive?271 # What do we do with fields that share names with Python keywords272 # like 'lambda' and 'yield'?273 #274 # nnorwitz says:275 # """276 # Typically (in python), an underscore is appended to names that are277 # 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 singular283 primitive field is non-empty if HasField() would return true in proto2 or it284 is non zero in proto3. A repeated field is non-empty if it contains at least285 one element. The fields are ordered by field number.286 287 Returns:288 list[tuple(FieldDescriptor, value)]: field descriptors and values289 for all fields in the message which are not empty. The values vary by290 field type.291 """292 raise NotImplementedError293 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 the298 field_name is not defined in the message descriptor, :exc:`ValueError` will299 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 NotImplementedError311 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 a316 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 NotImplementedError325 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 exists339 """340 raise NotImplementedError341 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" repeated356 extension is an empty list.357 """358 raise NotImplementedError359 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 NotImplementedError367 368 def UnknownFields(self):369 """Returns the UnknownFieldSet.370 371 Returns:372 UnknownFieldSet: The unknown fields stored in this message.373 """374 raise NotImplementedError375 376 def DiscardUnknownFields(self):377 """Clears all fields in the :class:`UnknownFieldSet`.378 379 This operation is recursive for nested message.380 """381 raise NotImplementedError382 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 NotImplementedError392 393 @classmethod394 def FromString(cls, s):395 raise NotImplementedError396 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 to404 parents at runtime, for the purpose of setting "has" bits and405 byte-size-dirty bits in the parent and ancestor objects whenever a child or406 descendant object is modified.407 408 If the client wants to disconnect this Message from the object tree, she409 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 in413 internal/message_listener.py, and we discard any listener registered414 via a previous _SetListener() call.415 """416 raise NotImplementedError417 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 unpickling427 # 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.DESCRIPTOR434 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 the438 # receiving end.439 container = message_descriptor440 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-top447 448 return symbol_database.Default().GetSymbol(full_name)()449 