| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import collections |
| import collections.abc |
| import copy |
| import re |
| from typing import List, Optional, Type |
| import warnings |
|
|
| import google.protobuf |
| from google.protobuf import descriptor_pb2 |
| from google.protobuf import message |
| from google.protobuf.json_format import MessageToDict, MessageToJson, Parse |
|
|
| from proto import _file_info |
| from proto import _package_info |
| from proto.fields import Field |
| from proto.fields import MapField |
| from proto.fields import RepeatedField |
| from proto.marshal import Marshal |
| from proto.primitives import ProtoType |
| from proto.utils import has_upb |
|
|
|
|
| PROTOBUF_VERSION = google.protobuf.__version__ |
|
|
| _upb = has_upb() |
|
|
|
|
| class MessageMeta(type): |
| """A metaclass for building and registering Message subclasses.""" |
|
|
| def __new__(mcls, name, bases, attrs): |
| |
| if not bases: |
| return super().__new__(mcls, name, bases, attrs) |
|
|
| |
| |
| package, marshal = _package_info.compile(name, attrs) |
|
|
| |
| local_path = tuple(attrs.get("__qualname__", name).split(".")) |
|
|
| |
| |
| if "<locals>" in local_path: |
| ix = local_path.index("<locals>") |
| local_path = local_path[: ix - 1] + local_path[ix + 1 :] |
|
|
| |
| full_name = ".".join((package,) + local_path).lstrip(".") |
|
|
| |
| |
| |
| |
| map_fields = {} |
| for key, field in attrs.items(): |
| if not isinstance(field, MapField): |
| continue |
|
|
| |
| msg_name = "{pascal_key}Entry".format( |
| pascal_key=re.sub( |
| r"_\w", |
| lambda m: m.group()[1:].upper(), |
| key, |
| ).replace(key[0], key[0].upper(), 1), |
| ) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| entry_attrs = collections.OrderedDict( |
| { |
| "__module__": attrs.get("__module__", None), |
| "__qualname__": "{prefix}.{name}".format( |
| prefix=attrs.get("__qualname__", name), |
| name=msg_name, |
| ), |
| "_pb_options": {"map_entry": True}, |
| } |
| ) |
| entry_attrs["key"] = Field(field.map_key_type, number=1) |
| entry_attrs["value"] = Field( |
| field.proto_type, |
| number=2, |
| enum=field.enum, |
| message=field.message, |
| ) |
| map_fields[msg_name] = MessageMeta(msg_name, (Message,), entry_attrs) |
|
|
| |
| map_fields[key] = RepeatedField( |
| ProtoType.MESSAGE, |
| number=field.number, |
| message=map_fields[msg_name], |
| ) |
|
|
| |
| attrs.update(map_fields) |
|
|
| |
| |
| |
| fields = [] |
| new_attrs = {} |
| oneofs = collections.OrderedDict() |
| proto_imports = set() |
| index = 0 |
| for key, field in attrs.items(): |
| |
| if not isinstance(field, Field): |
| |
| new_attrs[key] = field |
| continue |
|
|
| |
| |
| |
| field.mcls_data = { |
| "name": key, |
| "parent_name": full_name, |
| "index": index, |
| "package": package, |
| } |
|
|
| |
| fields.append(field) |
| |
| |
| if field.oneof: |
| |
| |
| oneofs.setdefault(field.oneof, len(oneofs)) |
| field.descriptor.oneof_index = oneofs[field.oneof] |
|
|
| |
| |
| |
| if field.message and not isinstance(field.message, str): |
| field_msg = field.message |
| if hasattr(field_msg, "pb") and callable(field_msg.pb): |
| field_msg = field_msg.pb() |
| |
| |
| |
| |
| |
| |
| if field_msg: |
| proto_imports.add(field_msg.DESCRIPTOR.file.name) |
|
|
| |
| elif field.enum and not isinstance(field.enum, str): |
| field_enum = ( |
| field.enum._meta.pb |
| if hasattr(field.enum, "_meta") |
| else field.enum.DESCRIPTOR |
| ) |
|
|
| if field_enum: |
| proto_imports.add(field_enum.file.name) |
|
|
| |
| index += 1 |
|
|
| |
| |
| opt_attrs = {} |
| for field in fields: |
| if field.optional: |
| field.oneof = "_{}".format(field.name) |
| field.descriptor.oneof_index = oneofs[field.oneof] = len(oneofs) |
| opt_attrs[field.name] = field.name |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if opt_attrs: |
| mcls = type("AttrsMeta", (mcls,), opt_attrs) |
|
|
| |
| |
| |
| filename = _file_info._FileInfo.proto_file_name( |
| new_attrs.get("__module__", name.lower()) |
| ) |
|
|
| |
| |
| file_info = _file_info._FileInfo.maybe_add_descriptor(filename, package) |
|
|
| |
| |
| for proto_import in proto_imports: |
| if proto_import not in file_info.descriptor.dependency: |
| file_info.descriptor.dependency.append(proto_import) |
|
|
| |
| opts = descriptor_pb2.MessageOptions(**new_attrs.pop("_pb_options", {})) |
|
|
| |
| desc = descriptor_pb2.DescriptorProto( |
| name=name, |
| field=[i.descriptor for i in fields], |
| oneof_decl=[ |
| descriptor_pb2.OneofDescriptorProto(name=i) for i in oneofs.keys() |
| ], |
| options=opts, |
| ) |
|
|
| |
| |
| child_paths = [p for p in file_info.nested.keys() if local_path == p[:-1]] |
| for child_path in child_paths: |
| desc.nested_type.add().MergeFrom(file_info.nested.pop(child_path)) |
|
|
| |
| child_paths = [p for p in file_info.nested_enum.keys() if local_path == p[:-1]] |
| for child_path in child_paths: |
| desc.enum_type.add().MergeFrom(file_info.nested_enum.pop(child_path)) |
|
|
| |
| |
| if len(local_path) == 1: |
| file_info.descriptor.message_type.add().MergeFrom(desc) |
| else: |
| file_info.nested[local_path] = desc |
|
|
| |
| new_attrs["_meta"] = _MessageInfo( |
| fields=fields, |
| full_name=full_name, |
| marshal=marshal, |
| options=opts, |
| package=package, |
| ) |
|
|
| |
| cls = super().__new__(mcls, name, bases, new_attrs) |
|
|
| |
| cls._meta.parent = cls |
| for field in cls._meta.fields.values(): |
| field.parent = cls |
|
|
| |
| |
| |
| file_info.messages[full_name] = cls |
|
|
| |
| if file_info.ready(new_class=cls): |
| file_info.generate_file_pb(new_class=cls, fallback_salt=full_name) |
|
|
| |
| return cls |
|
|
| @classmethod |
| def __prepare__(mcls, name, bases, **kwargs): |
| return collections.OrderedDict() |
|
|
| @property |
| def meta(cls): |
| return cls._meta |
|
|
| def __dir__(self): |
| try: |
| names = set(dir(type)) |
| names.update( |
| ( |
| "meta", |
| "pb", |
| "wrap", |
| "serialize", |
| "deserialize", |
| "to_json", |
| "from_json", |
| "to_dict", |
| "copy_from", |
| ) |
| ) |
| desc = self.pb().DESCRIPTOR |
| names.update(t.name for t in desc.nested_types) |
| names.update(e.name for e in desc.enum_types) |
|
|
| return names |
| except AttributeError: |
| return dir(type) |
|
|
| def pb(cls, obj=None, *, coerce: bool = False): |
| """Return the underlying protobuf Message class or instance. |
| |
| Args: |
| obj: If provided, and an instance of ``cls``, return the |
| underlying protobuf instance. |
| coerce (bool): If provided, will attempt to coerce ``obj`` to |
| ``cls`` if it is not already an instance. |
| """ |
| if obj is None: |
| return cls.meta.pb |
| if not isinstance(obj, cls): |
| if coerce: |
| obj = cls(obj) |
| else: |
| raise TypeError( |
| "%r is not an instance of %s" |
| % ( |
| obj, |
| cls.__name__, |
| ) |
| ) |
| return obj._pb |
|
|
| def wrap(cls, pb): |
| """Return a Message object that shallowly wraps the descriptor. |
| |
| Args: |
| pb: A protocol buffer object, such as would be returned by |
| :meth:`pb`. |
| """ |
| |
| instance = cls.__new__(cls) |
| super(cls, instance).__setattr__("_pb", pb) |
| return instance |
|
|
| def serialize(cls, instance) -> bytes: |
| """Return the serialized proto. |
| |
| Args: |
| instance: An instance of this message type, or something |
| compatible (accepted by the type's constructor). |
| |
| Returns: |
| bytes: The serialized representation of the protocol buffer. |
| """ |
| return cls.pb(instance, coerce=True).SerializeToString() |
|
|
| def deserialize(cls, payload: bytes) -> "Message": |
| """Given a serialized proto, deserialize it into a Message instance. |
| |
| Args: |
| payload (bytes): The serialized proto. |
| |
| Returns: |
| ~.Message: An instance of the message class against which this |
| method was called. |
| """ |
| return cls.wrap(cls.pb().FromString(payload)) |
|
|
| def _warn_if_including_default_value_fields_is_used_protobuf_5( |
| cls, including_default_value_fields: Optional[bool] |
| ) -> None: |
| """ |
| Warn Protobuf 5.x+ users that `including_default_value_fields` is deprecated if it is set. |
| |
| Args: |
| including_default_value_fields (Optional(bool)): The value of `including_default_value_fields` set by the user. |
| """ |
| if ( |
| PROTOBUF_VERSION[0] not in ("3", "4") |
| and including_default_value_fields is not None |
| ): |
| warnings.warn( |
| """The argument `including_default_value_fields` has been removed from |
| Protobuf 5.x. Please use `always_print_fields_with_no_presence` instead. |
| """, |
| DeprecationWarning, |
| ) |
|
|
| def _raise_if_print_fields_values_are_set_and_differ( |
| cls, |
| always_print_fields_with_no_presence: Optional[bool], |
| including_default_value_fields: Optional[bool], |
| ) -> None: |
| """ |
| Raise Exception if both `always_print_fields_with_no_presence` and `including_default_value_fields` are set |
| and the values differ. |
| |
| Args: |
| always_print_fields_with_no_presence (Optional(bool)): The value of `always_print_fields_with_no_presence` set by the user. |
| including_default_value_fields (Optional(bool)): The value of `including_default_value_fields` set by the user. |
| Returns: |
| None |
| Raises: |
| ValueError: if both `always_print_fields_with_no_presence` and `including_default_value_fields` are set and |
| the values differ. |
| """ |
| if ( |
| always_print_fields_with_no_presence is not None |
| and including_default_value_fields is not None |
| and always_print_fields_with_no_presence != including_default_value_fields |
| ): |
| raise ValueError( |
| "Arguments `always_print_fields_with_no_presence` and `including_default_value_fields` must match" |
| ) |
|
|
| def _normalize_print_fields_without_presence( |
| cls, |
| always_print_fields_with_no_presence: Optional[bool], |
| including_default_value_fields: Optional[bool], |
| ) -> bool: |
| """ |
| Return true if fields with no presence should be included in the results. |
| By default, fields with no presence will be included in the results |
| when both `always_print_fields_with_no_presence` and |
| `including_default_value_fields` are not set |
| |
| Args: |
| always_print_fields_with_no_presence (Optional(bool)): The value of `always_print_fields_with_no_presence` set by the user. |
| including_default_value_fields (Optional(bool)): The value of `including_default_value_fields` set by the user. |
| Returns: |
| None |
| Raises: |
| ValueError: if both `always_print_fields_with_no_presence` and `including_default_value_fields` are set and |
| the values differ. |
| """ |
|
|
| cls._warn_if_including_default_value_fields_is_used_protobuf_5( |
| including_default_value_fields |
| ) |
| cls._raise_if_print_fields_values_are_set_and_differ( |
| always_print_fields_with_no_presence, including_default_value_fields |
| ) |
| |
| return ( |
| ( |
| always_print_fields_with_no_presence is None |
| and including_default_value_fields is None |
| ) |
| or always_print_fields_with_no_presence |
| or including_default_value_fields |
| ) |
|
|
| def to_json( |
| cls, |
| instance, |
| *, |
| use_integers_for_enums=True, |
| including_default_value_fields=None, |
| preserving_proto_field_name=False, |
| sort_keys=False, |
| indent=2, |
| float_precision=None, |
| always_print_fields_with_no_presence=None, |
| ) -> str: |
| """Given a message instance, serialize it to json |
| |
| Args: |
| instance: An instance of this message type, or something |
| compatible (accepted by the type's constructor). |
| use_integers_for_enums (Optional(bool)): An option that determines whether enum |
| values should be represented by strings (False) or integers (True). |
| Default is True. |
| including_default_value_fields (Optional(bool)): Deprecated. Use argument |
| `always_print_fields_with_no_presence` instead. An option that |
| determines whether the default field values should be included in the results. |
| This value must match `always_print_fields_with_no_presence`, |
| if both arguments are explicitly set. |
| preserving_proto_field_name (Optional(bool)): An option that |
| determines whether field name representations preserve |
| proto case (snake_case) or use lowerCamelCase. Default is False. |
| sort_keys (Optional(bool)): If True, then the output will be sorted by field names. |
| Default is False. |
| indent (Optional(int)): The JSON object will be pretty-printed with this indent level. |
| An indent level of 0 or negative will only insert newlines. |
| Pass None for the most compact representation without newlines. |
| float_precision (Optional(int)): If set, use this to specify float field valid digits. |
| Default is None. |
| always_print_fields_with_no_presence (Optional(bool)): If True, fields without |
| presence (implicit presence scalars, repeated fields, and map fields) will |
| always be serialized. Any field that supports presence is not affected by |
| this option (including singular message fields and oneof fields). |
| This value must match `including_default_value_fields`, |
| if both arguments are explicitly set. |
| Returns: |
| str: The json string representation of the protocol buffer. |
| """ |
|
|
| print_fields = cls._normalize_print_fields_without_presence( |
| always_print_fields_with_no_presence, including_default_value_fields |
| ) |
|
|
| if PROTOBUF_VERSION[0] in ("3", "4"): |
| return MessageToJson( |
| cls.pb(instance), |
| use_integers_for_enums=use_integers_for_enums, |
| including_default_value_fields=print_fields, |
| preserving_proto_field_name=preserving_proto_field_name, |
| sort_keys=sort_keys, |
| indent=indent, |
| float_precision=float_precision, |
| ) |
| else: |
| |
| |
| |
| |
| |
| return MessageToJson( |
| cls.pb(instance), |
| use_integers_for_enums=use_integers_for_enums, |
| always_print_fields_with_no_presence=print_fields, |
| preserving_proto_field_name=preserving_proto_field_name, |
| sort_keys=sort_keys, |
| indent=indent, |
| float_precision=float_precision, |
| ) |
|
|
| def from_json(cls, payload, *, ignore_unknown_fields=False) -> "Message": |
| """Given a json string representing an instance, |
| parse it into a message. |
| |
| Args: |
| payload: A json string representing a message. |
| ignore_unknown_fields (Optional(bool)): If True, do not raise errors |
| for unknown fields. |
| |
| Returns: |
| ~.Message: An instance of the message class against which this |
| method was called. |
| """ |
| instance = cls() |
| Parse(payload, instance._pb, ignore_unknown_fields=ignore_unknown_fields) |
| return instance |
|
|
| def to_dict( |
| cls, |
| instance, |
| *, |
| use_integers_for_enums=True, |
| preserving_proto_field_name=True, |
| including_default_value_fields=None, |
| float_precision=None, |
| always_print_fields_with_no_presence=None, |
| ) -> "Message": |
| """Given a message instance, return its representation as a python dict. |
| |
| Args: |
| instance: An instance of this message type, or something |
| compatible (accepted by the type's constructor). |
| use_integers_for_enums (Optional(bool)): An option that determines whether enum |
| values should be represented by strings (False) or integers (True). |
| Default is True. |
| preserving_proto_field_name (Optional(bool)): An option that |
| determines whether field name representations preserve |
| proto case (snake_case) or use lowerCamelCase. Default is True. |
| including_default_value_fields (Optional(bool)): Deprecated. Use argument |
| `always_print_fields_with_no_presence` instead. An option that |
| determines whether the default field values should be included in the results. |
| This value must match `always_print_fields_with_no_presence`, |
| if both arguments are explicitly set. |
| float_precision (Optional(int)): If set, use this to specify float field valid digits. |
| Default is None. |
| always_print_fields_with_no_presence (Optional(bool)): If True, fields without |
| presence (implicit presence scalars, repeated fields, and map fields) will |
| always be serialized. Any field that supports presence is not affected by |
| this option (including singular message fields and oneof fields). This value |
| must match `including_default_value_fields`, if both arguments are explicitly set. |
| |
| Returns: |
| dict: A representation of the protocol buffer using pythonic data structures. |
| Messages and map fields are represented as dicts, |
| repeated fields are represented as lists. |
| """ |
|
|
| print_fields = cls._normalize_print_fields_without_presence( |
| always_print_fields_with_no_presence, including_default_value_fields |
| ) |
|
|
| if PROTOBUF_VERSION[0] in ("3", "4"): |
| return MessageToDict( |
| cls.pb(instance), |
| including_default_value_fields=print_fields, |
| preserving_proto_field_name=preserving_proto_field_name, |
| use_integers_for_enums=use_integers_for_enums, |
| float_precision=float_precision, |
| ) |
| else: |
| |
| |
| |
| |
| |
| return MessageToDict( |
| cls.pb(instance), |
| always_print_fields_with_no_presence=print_fields, |
| preserving_proto_field_name=preserving_proto_field_name, |
| use_integers_for_enums=use_integers_for_enums, |
| float_precision=float_precision, |
| ) |
|
|
| def copy_from(cls, instance, other): |
| """Equivalent for protobuf.Message.CopyFrom |
| |
| Args: |
| instance: An instance of this message type |
| other: (Union[dict, ~.Message): |
| A dictionary or message to reinitialize the values for this message. |
| """ |
| if isinstance(other, cls): |
| |
| other = Message.pb(other) |
| elif isinstance(other, cls.pb()): |
| |
| pass |
| elif isinstance(other, collections.abc.Mapping): |
| |
| other = cls._meta.pb(**other) |
| else: |
| raise TypeError( |
| "invalid argument type to copy to {}: {}".format( |
| cls.__name__, other.__class__.__name__ |
| ) |
| ) |
|
|
| |
| |
| |
| |
| cls.pb(instance).CopyFrom(other) |
|
|
|
|
| class Message(metaclass=MessageMeta): |
| """The abstract base class for a message. |
| |
| Args: |
| mapping (Union[dict, ~.Message]): A dictionary or message to be |
| used to determine the values for this message. |
| ignore_unknown_fields (Optional(bool)): If True, do not raise errors for |
| unknown fields. Only applied if `mapping` is a mapping type or there |
| are keyword parameters. |
| kwargs (dict): Keys and values corresponding to the fields of the |
| message. |
| """ |
|
|
| def __init__( |
| self, |
| mapping=None, |
| *, |
| ignore_unknown_fields=False, |
| **kwargs, |
| ): |
| |
| |
| |
| |
| |
| if mapping is None: |
| if not kwargs: |
| |
| super().__setattr__("_pb", self._meta.pb()) |
| return |
|
|
| mapping = kwargs |
| elif isinstance(mapping, self._meta.pb): |
| |
| |
| |
| |
| |
| |
| |
| mapping = copy.deepcopy(mapping) |
| if kwargs: |
| mapping.MergeFrom(self._meta.pb(**kwargs)) |
|
|
| super().__setattr__("_pb", mapping) |
| return |
| elif isinstance(mapping, type(self)): |
| |
| self.__init__(mapping=mapping._pb, **kwargs) |
| return |
| elif isinstance(mapping, collections.abc.Mapping): |
| |
| mapping = copy.copy(mapping) |
| |
| mapping.update(kwargs) |
| else: |
| |
| raise TypeError( |
| "Invalid constructor input for %s: %r" |
| % ( |
| self.__class__.__name__, |
| mapping, |
| ) |
| ) |
|
|
| params = {} |
| |
| |
| marshal = self._meta.marshal |
| for key, value in mapping.items(): |
| (key, pb_type) = self._get_pb_type_from_key(key) |
| if pb_type is None: |
| if ignore_unknown_fields: |
| continue |
|
|
| raise ValueError( |
| "Unknown field for {}: {}".format(self.__class__.__name__, key) |
| ) |
|
|
| pb_value = marshal.to_proto(pb_type, value) |
|
|
| if pb_value is not None: |
| params[key] = pb_value |
|
|
| |
| super().__setattr__("_pb", self._meta.pb(**params)) |
|
|
| def _get_pb_type_from_key(self, key): |
| """Given a key, return the corresponding pb_type. |
| |
| Args: |
| key(str): The name of the field. |
| |
| Returns: |
| A tuple containing a key and pb_type. The pb_type will be |
| the composite type of the field, or the primitive type if a primitive. |
| If no corresponding field exists, return None. |
| """ |
|
|
| pb_type = None |
|
|
| try: |
| pb_type = self._meta.fields[key].pb_type |
| except KeyError: |
| |
| |
| |
| |
| |
| |
| |
| if f"{key}_" in self._meta.fields: |
| key = f"{key}_" |
| pb_type = self._meta.fields[key].pb_type |
|
|
| return (key, pb_type) |
|
|
| def __dir__(self): |
| desc = type(self).pb().DESCRIPTOR |
| names = {f_name for f_name in self._meta.fields.keys()} |
| names.update(m.name for m in desc.nested_types) |
| names.update(e.name for e in desc.enum_types) |
| names.update(dir(object())) |
| |
| |
| names.update( |
| ( |
| "__bool__", |
| "__contains__", |
| "__dict__", |
| "__getattr__", |
| "__getstate__", |
| "__module__", |
| "__setstate__", |
| "__weakref__", |
| ) |
| ) |
|
|
| return names |
|
|
| def __bool__(self): |
| """Return True if any field is truthy, False otherwise.""" |
| return any(k in self and getattr(self, k) for k in self._meta.fields.keys()) |
|
|
| def __contains__(self, key): |
| """Return True if this field was set to something non-zero on the wire. |
| |
| In most cases, this method will return True when ``__getattr__`` |
| would return a truthy value and False when it would return a falsy |
| value, so explicitly calling this is not useful. |
| |
| The exception case is empty messages explicitly set on the wire, |
| which are falsy from ``__getattr__``. This method allows to |
| distinguish between an explicitly provided empty message and the |
| absence of that message, which is useful in some edge cases. |
| |
| The most common edge case is the use of ``google.protobuf.BoolValue`` |
| to get a boolean that distinguishes between ``False`` and ``None`` |
| (or the same for a string, int, etc.). This library transparently |
| handles that case for you, but this method remains available to |
| accommodate cases not automatically covered. |
| |
| Args: |
| key (str): The name of the field. |
| |
| Returns: |
| bool: Whether the field's value corresponds to a non-empty |
| wire serialization. |
| """ |
| pb_value = getattr(self._pb, key) |
| try: |
| |
| |
| |
| |
| |
| |
| |
| return self._pb.HasField(key) |
| except ValueError: |
| return bool(pb_value) |
|
|
| def __delattr__(self, key): |
| """Delete the value on the given field. |
| |
| This is generally equivalent to setting a falsy value. |
| """ |
| self._pb.ClearField(key) |
|
|
| def __eq__(self, other): |
| """Return True if the messages are equal, False otherwise.""" |
| |
| if isinstance(other, type(self)): |
| return self._pb == other._pb |
|
|
| |
| if isinstance(other, self._meta.pb): |
| return self._pb == other |
|
|
| |
| return NotImplemented |
|
|
| def __getattr__(self, key): |
| """Retrieve the given field's value. |
| |
| In protocol buffers, the presence of a field on a message is |
| sufficient for it to always be "present". |
| |
| For primitives, a value of the correct type will always be returned |
| (the "falsy" values in protocol buffers consistently match those |
| in Python). For repeated fields, the falsy value is always an empty |
| sequence. |
| |
| For messages, protocol buffers does distinguish between an empty |
| message and absence, but this distinction is subtle and rarely |
| relevant. Therefore, this method always returns an empty message |
| (following the official implementation). To check for message |
| presence, use ``key in self`` (in other words, ``__contains__``). |
| |
| .. note:: |
| |
| Some well-known protocol buffer types |
| (e.g. ``google.protobuf.Timestamp``) will be converted to |
| their Python equivalents. See the ``marshal`` module for |
| more details. |
| """ |
| (key, pb_type) = self._get_pb_type_from_key(key) |
| if pb_type is None: |
| raise AttributeError( |
| "Unknown field for {}: {}".format(self.__class__.__name__, key) |
| ) |
| pb_value = getattr(self._pb, key) |
| marshal = self._meta.marshal |
| return marshal.to_python(pb_type, pb_value, absent=key not in self) |
|
|
| def __ne__(self, other): |
| """Return True if the messages are unequal, False otherwise.""" |
| return not self == other |
|
|
| def __repr__(self): |
| return repr(self._pb) |
|
|
| def __setattr__(self, key, value): |
| """Set the value on the given field. |
| |
| For well-known protocol buffer types which are marshalled, either |
| the protocol buffer object or the Python equivalent is accepted. |
| """ |
| if key[0] == "_": |
| return super().__setattr__(key, value) |
| marshal = self._meta.marshal |
| (key, pb_type) = self._get_pb_type_from_key(key) |
| if pb_type is None: |
| raise AttributeError( |
| "Unknown field for {}: {}".format(self.__class__.__name__, key) |
| ) |
|
|
| pb_value = marshal.to_proto(pb_type, value) |
|
|
| |
| |
| |
| self._pb.ClearField(key) |
|
|
| |
| if pb_value is not None: |
| self._pb.MergeFrom(self._meta.pb(**{key: pb_value})) |
|
|
| def __getstate__(self): |
| """Serialize for pickling.""" |
| return self._pb.SerializeToString() |
|
|
| def __setstate__(self, value): |
| """Deserialization for pickling.""" |
| new_pb = self._meta.pb().FromString(value) |
| super().__setattr__("_pb", new_pb) |
|
|
|
|
| class _MessageInfo: |
| """Metadata about a message. |
| |
| Args: |
| fields (Tuple[~.fields.Field]): The fields declared on the message. |
| package (str): The proto package. |
| full_name (str): The full name of the message. |
| file_info (~._FileInfo): The file descriptor and messages for the |
| file containing this message. |
| marshal (~.Marshal): The marshal instance to which this message was |
| automatically registered. |
| options (~.descriptor_pb2.MessageOptions): Any options that were |
| set on the message. |
| """ |
|
|
| def __init__( |
| self, |
| *, |
| fields: List[Field], |
| package: str, |
| full_name: str, |
| marshal: Marshal, |
| options: descriptor_pb2.MessageOptions, |
| ) -> None: |
| self.package = package |
| self.full_name = full_name |
| self.options = options |
| self.fields = collections.OrderedDict((i.name, i) for i in fields) |
| self.fields_by_number = collections.OrderedDict((i.number, i) for i in fields) |
| self.marshal = marshal |
| self._pb = None |
|
|
| @property |
| def pb(self) -> Type[message.Message]: |
| """Return the protobuf message type for this descriptor. |
| |
| If a field on the message references another message which has not |
| loaded, then this method returns None. |
| """ |
| return self._pb |
|
|
|
|
| __all__ = ("Message",) |
|
|