Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
message.py851 linesDownload Raw Back to proto
1# Copyright 2018 Google LLC2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7#     https://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14 15import collections16import collections.abc17import copy18import re19from typing import List, Type20 21from google.protobuf import descriptor_pb222from google.protobuf import message23from google.protobuf.json_format import MessageToDict, MessageToJson, Parse24 25from proto import _file_info26from proto import _package_info27from proto.fields import Field28from proto.fields import MapField29from proto.fields import RepeatedField30from proto.marshal import Marshal31from proto.primitives import ProtoType32from proto.utils import has_upb33 34 35_upb = has_upb()  # Important to cache result here.36 37 38class MessageMeta(type):39    """A metaclass for building and registering Message subclasses."""40 41    def __new__(mcls, name, bases, attrs):42        # Do not do any special behavior for Message itself.43        if not bases:44            return super().__new__(mcls, name, bases, attrs)45 46        # Get the essential information about the proto package, and where47        # this component belongs within the file.48        package, marshal = _package_info.compile(name, attrs)49 50        # Determine the local path of this proto component within the file.51        local_path = tuple(attrs.get("__qualname__", name).split("."))52 53        # Sanity check: We get the wrong full name if a class is declared54        # inside a function local scope; correct this.55        if "<locals>" in local_path:56            ix = local_path.index("<locals>")57            local_path = local_path[: ix - 1] + local_path[ix + 1 :]58 59        # Determine the full name in protocol buffers.60        full_name = ".".join((package,) + local_path).lstrip(".")61 62        # Special case: Maps. Map fields are special; they are essentially63        # shorthand for a nested message and a repeated field of that message.64        # Decompose each map into its constituent form.65        # https://developers.google.com/protocol-buffers/docs/proto3#maps66        map_fields = {}67        for key, field in attrs.items():68            if not isinstance(field, MapField):69                continue70 71            # Determine the name of the entry message.72            msg_name = "{pascal_key}Entry".format(73                pascal_key=re.sub(74                    r"_\w",75                    lambda m: m.group()[1:].upper(),76                    key,77                ).replace(key[0], key[0].upper(), 1),78            )79 80            # Create the "entry" message (with the key and value fields).81            #82            # Note: We instantiate an ordered dictionary here and then83            # attach key and value in order to ensure that the fields are84            # iterated in the correct order when the class is created.85            # This is only an issue in Python 3.5, where the order is86            # random (and the wrong order causes the pool to refuse to add87            # the descriptor because reasons).88            entry_attrs = collections.OrderedDict(89                {90                    "__module__": attrs.get("__module__", None),91                    "__qualname__": "{prefix}.{name}".format(92                        prefix=attrs.get("__qualname__", name),93                        name=msg_name,94                    ),95                    "_pb_options": {"map_entry": True},96                }97            )98            entry_attrs["key"] = Field(field.map_key_type, number=1)99            entry_attrs["value"] = Field(100                field.proto_type,101                number=2,102                enum=field.enum,103                message=field.message,104            )105            map_fields[msg_name] = MessageMeta(msg_name, (Message,), entry_attrs)106 107            # Create the repeated field for the entry message.108            map_fields[key] = RepeatedField(109                ProtoType.MESSAGE,110                number=field.number,111                message=map_fields[msg_name],112            )113 114        # Add the new entries to the attrs115        attrs.update(map_fields)116 117        # Okay, now we deal with all the rest of the fields.118        # Iterate over all the attributes and separate the fields into119        # their own sequence.120        fields = []121        new_attrs = {}122        oneofs = collections.OrderedDict()123        proto_imports = set()124        index = 0125        for key, field in attrs.items():126            # Sanity check: If this is not a field, do nothing.127            if not isinstance(field, Field):128                # The field objects themselves should not be direct attributes.129                new_attrs[key] = field130                continue131 132            # Add data that the field requires that we do not take in the133            # constructor because we can derive it from the metaclass.134            # (The goal is to make the declaration syntax as nice as possible.)135            field.mcls_data = {136                "name": key,137                "parent_name": full_name,138                "index": index,139                "package": package,140            }141 142            # Add the field to the list of fields.143            fields.append(field)144            # If this field is part of a "oneof", ensure the oneof itself145            # is represented.146            if field.oneof:147                # Keep a running tally of the index of each oneof, and assign148                # that index to the field's descriptor.149                oneofs.setdefault(field.oneof, len(oneofs))150                field.descriptor.oneof_index = oneofs[field.oneof]151 152            # If this field references a message, it may be from another153            # proto file; ensure we know about the import (to faithfully154            # construct our file descriptor proto).155            if field.message and not isinstance(field.message, str):156                field_msg = field.message157                if hasattr(field_msg, "pb") and callable(field_msg.pb):158                    field_msg = field_msg.pb()159                # Sanity check: The field's message may not yet be defined if160                # it was a Message defined in the same file, and the file161                # descriptor proto has not yet been generated.162                #163                # We do nothing in this situation; everything will be handled164                # correctly when the file descriptor is created later.165                if field_msg:166                    proto_imports.add(field_msg.DESCRIPTOR.file.name)167 168            # Same thing, but for enums.169            elif field.enum and not isinstance(field.enum, str):170                field_enum = (171                    field.enum._meta.pb172                    if hasattr(field.enum, "_meta")173                    else field.enum.DESCRIPTOR174                )175 176                if field_enum:177                    proto_imports.add(field_enum.file.name)178 179            # Increment the field index counter.180            index += 1181 182        # As per descriptor.proto, all synthetic oneofs must be ordered after183        # 'real' oneofs.184        opt_attrs = {}185        for field in fields:186            if field.optional:187                field.oneof = "_{}".format(field.name)188                field.descriptor.oneof_index = oneofs[field.oneof] = len(oneofs)189                opt_attrs[field.name] = field.name190 191        # Generating a metaclass dynamically provides class attributes that192        # instances can't see. This provides idiomatically named constants193        # that enable the following pattern to check for field presence:194        #195        # class MyMessage(proto.Message):196        #     field = proto.Field(proto.INT32, number=1, optional=True)197        #198        # m = MyMessage()199        # MyMessage.field in m200        if opt_attrs:201            mcls = type("AttrsMeta", (mcls,), opt_attrs)202 203        # Determine the filename.204        # We determine an appropriate proto filename based on the205        # Python module.206        filename = _file_info._FileInfo.proto_file_name(207            new_attrs.get("__module__", name.lower())208        )209 210        # Get or create the information about the file, including the211        # descriptor to which the new message descriptor shall be added.212        file_info = _file_info._FileInfo.maybe_add_descriptor(filename, package)213 214        # Ensure any imports that would be necessary are assigned to the file215        # descriptor proto being created.216        for proto_import in proto_imports:217            if proto_import not in file_info.descriptor.dependency:218                file_info.descriptor.dependency.append(proto_import)219 220        # Retrieve any message options.221        opts = descriptor_pb2.MessageOptions(**new_attrs.pop("_pb_options", {}))222 223        # Create the underlying proto descriptor.224        desc = descriptor_pb2.DescriptorProto(225            name=name,226            field=[i.descriptor for i in fields],227            oneof_decl=[228                descriptor_pb2.OneofDescriptorProto(name=i) for i in oneofs.keys()229            ],230            options=opts,231        )232 233        # If any descriptors were nested under this one, they need to be234        # attached as nested types here.235        child_paths = [p for p in file_info.nested.keys() if local_path == p[:-1]]236        for child_path in child_paths:237            desc.nested_type.add().MergeFrom(file_info.nested.pop(child_path))238 239        # Same thing, but for enums240        child_paths = [p for p in file_info.nested_enum.keys() if local_path == p[:-1]]241        for child_path in child_paths:242            desc.enum_type.add().MergeFrom(file_info.nested_enum.pop(child_path))243 244        # Add the descriptor to the file if it is a top-level descriptor,245        # or to a "holding area" for nested messages otherwise.246        if len(local_path) == 1:247            file_info.descriptor.message_type.add().MergeFrom(desc)248        else:249            file_info.nested[local_path] = desc250 251        # Create the MessageInfo instance to be attached to this message.252        new_attrs["_meta"] = _MessageInfo(253            fields=fields,254            full_name=full_name,255            marshal=marshal,256            options=opts,257            package=package,258        )259 260        # Run the superclass constructor.261        cls = super().__new__(mcls, name, bases, new_attrs)262 263        # The info class and fields need a reference to the class just created.264        cls._meta.parent = cls265        for field in cls._meta.fields.values():266            field.parent = cls267 268        # Add this message to the _FileInfo instance; this allows us to269        # associate the descriptor with the message once the descriptor270        # is generated.271        file_info.messages[full_name] = cls272 273        # Generate the descriptor for the file if it is ready.274        if file_info.ready(new_class=cls):275            file_info.generate_file_pb(new_class=cls, fallback_salt=full_name)276 277        # Done; return the class.278        return cls279 280    @classmethod281    def __prepare__(mcls, name, bases, **kwargs):282        return collections.OrderedDict()283 284    @property285    def meta(cls):286        return cls._meta287 288    def __dir__(self):289        try:290            names = set(dir(type))291            names.update(292                (293                    "meta",294                    "pb",295                    "wrap",296                    "serialize",297                    "deserialize",298                    "to_json",299                    "from_json",300                    "to_dict",301                    "copy_from",302                )303            )304            desc = self.pb().DESCRIPTOR305            names.update(t.name for t in desc.nested_types)306            names.update(e.name for e in desc.enum_types)307 308            return names309        except AttributeError:310            return dir(type)311 312    def pb(cls, obj=None, *, coerce: bool = False):313        """Return the underlying protobuf Message class or instance.314 315        Args:316            obj: If provided, and an instance of ``cls``, return the317                underlying protobuf instance.318            coerce (bool): If provided, will attempt to coerce ``obj`` to319                ``cls`` if it is not already an instance.320        """321        if obj is None:322            return cls.meta.pb323        if not isinstance(obj, cls):324            if coerce:325                obj = cls(obj)326            else:327                raise TypeError(328                    "%r is not an instance of %s"329                    % (330                        obj,331                        cls.__name__,332                    )333                )334        return obj._pb335 336    def wrap(cls, pb):337        """Return a Message object that shallowly wraps the descriptor.338 339        Args:340            pb: A protocol buffer object, such as would be returned by341                :meth:`pb`.342        """343        # Optimized fast path.344        instance = cls.__new__(cls)345        super(cls, instance).__setattr__("_pb", pb)346        return instance347 348    def serialize(cls, instance) -> bytes:349        """Return the serialized proto.350 351        Args:352            instance: An instance of this message type, or something353                compatible (accepted by the type's constructor).354 355        Returns:356            bytes: The serialized representation of the protocol buffer.357        """358        return cls.pb(instance, coerce=True).SerializeToString()359 360    def deserialize(cls, payload: bytes) -> "Message":361        """Given a serialized proto, deserialize it into a Message instance.362 363        Args:364            payload (bytes): The serialized proto.365 366        Returns:367            ~.Message: An instance of the message class against which this368            method was called.369        """370        return cls.wrap(cls.pb().FromString(payload))371 372    def to_json(373        cls,374        instance,375        *,376        use_integers_for_enums=True,377        including_default_value_fields=True,378        preserving_proto_field_name=False,379        sort_keys=False,380        indent=2,381        float_precision=None,382    ) -> str:383        """Given a message instance, serialize it to json384 385        Args:386            instance: An instance of this message type, or something387                compatible (accepted by the type's constructor).388            use_integers_for_enums (Optional(bool)): An option that determines whether enum389                values should be represented by strings (False) or integers (True).390                Default is True.391            preserving_proto_field_name (Optional(bool)): An option that392                determines whether field name representations preserve393                proto case (snake_case) or use lowerCamelCase. Default is False.394            sort_keys (Optional(bool)): If True, then the output will be sorted by field names.395                Default is False.396            indent (Optional(int)): The JSON object will be pretty-printed with this indent level.397                An indent level of 0 or negative will only insert newlines.398                Pass None for the most compact representation without newlines.399            float_precision (Optional(int)): If set, use this to specify float field valid digits.400                Default is None.401        Returns:402            str: The json string representation of the protocol buffer.403        """404        return MessageToJson(405            cls.pb(instance),406            use_integers_for_enums=use_integers_for_enums,407            including_default_value_fields=including_default_value_fields,408            preserving_proto_field_name=preserving_proto_field_name,409            sort_keys=sort_keys,410            indent=indent,411            float_precision=float_precision,412        )413 414    def from_json(cls, payload, *, ignore_unknown_fields=False) -> "Message":415        """Given a json string representing an instance,416        parse it into a message.417 418        Args:419            paylod: A json string representing a message.420            ignore_unknown_fields (Optional(bool)): If True, do not raise errors421                for unknown fields.422 423        Returns:424            ~.Message: An instance of the message class against which this425            method was called.426        """427        instance = cls()428        Parse(payload, instance._pb, ignore_unknown_fields=ignore_unknown_fields)429        return instance430 431    def to_dict(432        cls,433        instance,434        *,435        use_integers_for_enums=True,436        preserving_proto_field_name=True,437        including_default_value_fields=True,438        float_precision=None,439    ) -> "Message":440        """Given a message instance, return its representation as a python dict.441 442        Args:443            instance: An instance of this message type, or something444                      compatible (accepted by the type's constructor).445            use_integers_for_enums (Optional(bool)): An option that determines whether enum446                values should be represented by strings (False) or integers (True).447                Default is True.448            preserving_proto_field_name (Optional(bool)): An option that449                determines whether field name representations preserve450                proto case (snake_case) or use lowerCamelCase. Default is True.451            including_default_value_fields (Optional(bool)): An option that452                determines whether the default field values should be included in the results.453                Default is True.454            float_precision (Optional(int)): If set, use this to specify float field valid digits.455                Default is None.456 457        Returns:458            dict: A representation of the protocol buffer using pythonic data structures.459                  Messages and map fields are represented as dicts,460                  repeated fields are represented as lists.461        """462        return MessageToDict(463            cls.pb(instance),464            including_default_value_fields=including_default_value_fields,465            preserving_proto_field_name=preserving_proto_field_name,466            use_integers_for_enums=use_integers_for_enums,467            float_precision=float_precision,468        )469 470    def copy_from(cls, instance, other):471        """Equivalent for protobuf.Message.CopyFrom472 473        Args:474            instance: An instance of this message type475            other: (Union[dict, ~.Message):476                A dictionary or message to reinitialize the values for this message.477        """478        if isinstance(other, cls):479            # Just want the underlying proto.480            other = Message.pb(other)481        elif isinstance(other, cls.pb()):482            # Don't need to do anything.483            pass484        elif isinstance(other, collections.abc.Mapping):485            # Coerce into a proto486            other = cls._meta.pb(**other)487        else:488            raise TypeError(489                "invalid argument type to copy to {}: {}".format(490                    cls.__name__, other.__class__.__name__491                )492            )493 494        # Note: we can't just run self.__init__ because this may be a message field495        # for a higher order proto; the memory layout for protos is NOT LIKE the496        # python memory model. We cannot rely on just setting things by reference.497        # Non-trivial complexity is (partially) hidden by the protobuf runtime.498        cls.pb(instance).CopyFrom(other)499 500 501class Message(metaclass=MessageMeta):502    """The abstract base class for a message.503 504    Args:505        mapping (Union[dict, ~.Message]): A dictionary or message to be506            used to determine the values for this message.507        ignore_unknown_fields (Optional(bool)): If True, do not raise errors for508            unknown fields. Only applied if `mapping` is a mapping type or there509            are keyword parameters.510        kwargs (dict): Keys and values corresponding to the fields of the511            message.512    """513 514    def __init__(515        self,516        mapping=None,517        *,518        ignore_unknown_fields=False,519        **kwargs,520    ):521        # We accept several things for `mapping`:522        #   * An instance of this class.523        #   * An instance of the underlying protobuf descriptor class.524        #   * A dict525        #   * Nothing (keyword arguments only).526        if mapping is None:527            if not kwargs:528                # Special fast path for empty construction.529                super().__setattr__("_pb", self._meta.pb())530                return531 532            mapping = kwargs533        elif isinstance(mapping, self._meta.pb):534            # Make a copy of the mapping.535            # This is a constructor for a new object, so users will assume536            # that it will not have side effects on the arguments being537            # passed in.538            #539            # The `wrap` method on the metaclass is the public API for taking540            # ownership of the passed in protobuf object.541            mapping = copy.deepcopy(mapping)542            if kwargs:543                mapping.MergeFrom(self._meta.pb(**kwargs))544 545            super().__setattr__("_pb", mapping)546            return547        elif isinstance(mapping, type(self)):548            # Just use the above logic on mapping's underlying pb.549            self.__init__(mapping=mapping._pb, **kwargs)550            return551        elif isinstance(mapping, collections.abc.Mapping):552            # Can't have side effects on mapping.553            mapping = copy.copy(mapping)554            # kwargs entries take priority for duplicate keys.555            mapping.update(kwargs)556        else:557            # Sanity check: Did we get something not a map? Error if so.558            raise TypeError(559                "Invalid constructor input for %s: %r"560                % (561                    self.__class__.__name__,562                    mapping,563                )564            )565 566        params = {}567        # Update the mapping to address any values that need to be568        # coerced.569        marshal = self._meta.marshal570        for key, value in mapping.items():571            (key, pb_type) = self._get_pb_type_from_key(key)572            if pb_type is None:573                if ignore_unknown_fields:574                    continue575 576                raise ValueError(577                    "Unknown field for {}: {}".format(self.__class__.__name__, key)578                )579 580            try:581                pb_value = marshal.to_proto(pb_type, value)582            except ValueError:583                # Underscores may be appended to field names584                # that collide with python or proto-plus keywords.585                # In case a key only exists with a `_` suffix, coerce the key586                # to include the `_` suffix. It's not possible to587                # natively define the same field with a trailing underscore in protobuf.588                # See related issue589                # https://github.com/googleapis/python-api-core/issues/227590                if isinstance(value, dict):591                    if _upb:592                        # In UPB, pb_type is MessageMeta which doesn't expose attrs like it used to in Python/CPP.593                        keys_to_update = [594                            item595                            for item in value596                            if item not in pb_type.DESCRIPTOR.fields_by_name597                            and f"{item}_" in pb_type.DESCRIPTOR.fields_by_name598                        ]599                    else:600                        keys_to_update = [601                            item602                            for item in value603                            if not hasattr(pb_type, item)604                            and hasattr(pb_type, f"{item}_")605                        ]606                    for item in keys_to_update:607                        value[f"{item}_"] = value.pop(item)608 609                pb_value = marshal.to_proto(pb_type, value)610 611            if pb_value is not None:612                params[key] = pb_value613 614        # Create the internal protocol buffer.615        super().__setattr__("_pb", self._meta.pb(**params))616 617    def _get_pb_type_from_key(self, key):618        """Given a key, return the corresponding pb_type.619 620        Args:621            key(str): The name of the field.622 623        Returns:624            A tuple containing a key and pb_type. The pb_type will be625            the composite type of the field, or the primitive type if a primitive.626            If no corresponding field exists, return None.627        """628 629        pb_type = None630 631        try:632            pb_type = self._meta.fields[key].pb_type633        except KeyError:634            # Underscores may be appended to field names635            # that collide with python or proto-plus keywords.636            # In case a key only exists with a `_` suffix, coerce the key637            # to include the `_` suffix. It's not possible to638            # natively define the same field with a trailing underscore in protobuf.639            # See related issue640            # https://github.com/googleapis/python-api-core/issues/227641            if f"{key}_" in self._meta.fields:642                key = f"{key}_"643                pb_type = self._meta.fields[key].pb_type644 645        return (key, pb_type)646 647    def __dir__(self):648        desc = type(self).pb().DESCRIPTOR649        names = {f_name for f_name in self._meta.fields.keys()}650        names.update(m.name for m in desc.nested_types)651        names.update(e.name for e in desc.enum_types)652        names.update(dir(object()))653        # Can't think of a better way of determining654        # the special methods than manually listing them.655        names.update(656            (657                "__bool__",658                "__contains__",659                "__dict__",660                "__getattr__",661                "__getstate__",662                "__module__",663                "__setstate__",664                "__weakref__",665            )666        )667 668        return names669 670    def __bool__(self):671        """Return True if any field is truthy, False otherwise."""672        return any(k in self and getattr(self, k) for k in self._meta.fields.keys())673 674    def __contains__(self, key):675        """Return True if this field was set to something non-zero on the wire.676 677        In most cases, this method will return True when ``__getattr__``678        would return a truthy value and False when it would return a falsy679        value, so explicitly calling this is not useful.680 681        The exception case is empty messages explicitly set on the wire,682        which are falsy from ``__getattr__``. This method allows to683        distinguish between an explicitly provided empty message and the684        absence of that message, which is useful in some edge cases.685 686        The most common edge case is the use of ``google.protobuf.BoolValue``687        to get a boolean that distinguishes between ``False`` and ``None``688        (or the same for a string, int, etc.). This library transparently689        handles that case for you, but this method remains available to690        accommodate cases not automatically covered.691 692        Args:693            key (str): The name of the field.694 695        Returns:696            bool: Whether the field's value corresponds to a non-empty697                wire serialization.698        """699        pb_value = getattr(self._pb, key)700        try:701            # Protocol buffers "HasField" is unfriendly; it only works702            # against composite, non-repeated fields, and raises ValueError703            # against any repeated field or primitive.704            #705            # There is no good way to test whether it is valid to provide706            # a field to this method, so sadly we are stuck with a707            # somewhat inefficient try/except.708            return self._pb.HasField(key)709        except ValueError:710            return bool(pb_value)711 712    def __delattr__(self, key):713        """Delete the value on the given field.714 715        This is generally equivalent to setting a falsy value.716        """717        self._pb.ClearField(key)718 719    def __eq__(self, other):720        """Return True if the messages are equal, False otherwise."""721        # If these are the same type, use internal protobuf's equality check.722        if isinstance(other, type(self)):723            return self._pb == other._pb724 725        # If the other type is the target protobuf object, honor that also.726        if isinstance(other, self._meta.pb):727            return self._pb == other728 729        # Ask the other object.730        return NotImplemented731 732    def __getattr__(self, key):733        """Retrieve the given field's value.734 735        In protocol buffers, the presence of a field on a message is736        sufficient for it to always be "present".737 738        For primitives, a value of the correct type will always be returned739        (the "falsy" values in protocol buffers consistently match those740        in Python). For repeated fields, the falsy value is always an empty741        sequence.742 743        For messages, protocol buffers does distinguish between an empty744        message and absence, but this distinction is subtle and rarely745        relevant. Therefore, this method always returns an empty message746        (following the official implementation). To check for message747        presence, use ``key in self`` (in other words, ``__contains__``).748 749        .. note::750 751            Some well-known protocol buffer types752            (e.g. ``google.protobuf.Timestamp``) will be converted to753            their Python equivalents. See the ``marshal`` module for754            more details.755        """756        (key, pb_type) = self._get_pb_type_from_key(key)757        if pb_type is None:758            raise AttributeError(759                "Unknown field for {}: {}".format(self.__class__.__name__, key)760            )761        pb_value = getattr(self._pb, key)762        marshal = self._meta.marshal763        return marshal.to_python(pb_type, pb_value, absent=key not in self)764 765    def __ne__(self, other):766        """Return True if the messages are unequal, False otherwise."""767        return not self == other768 769    def __repr__(self):770        return repr(self._pb)771 772    def __setattr__(self, key, value):773        """Set the value on the given field.774 775        For well-known protocol buffer types which are marshalled, either776        the protocol buffer object or the Python equivalent is accepted.777        """778        if key[0] == "_":779            return super().__setattr__(key, value)780        marshal = self._meta.marshal781        (key, pb_type) = self._get_pb_type_from_key(key)782        if pb_type is None:783            raise AttributeError(784                "Unknown field for {}: {}".format(self.__class__.__name__, key)785            )786 787        pb_value = marshal.to_proto(pb_type, value)788 789        # Clear the existing field.790        # This is the only way to successfully write nested falsy values,791        # because otherwise MergeFrom will no-op on them.792        self._pb.ClearField(key)793 794        # Merge in the value being set.795        if pb_value is not None:796            self._pb.MergeFrom(self._meta.pb(**{key: pb_value}))797 798    def __getstate__(self):799        """Serialize for pickling."""800        return self._pb.SerializeToString()801 802    def __setstate__(self, value):803        """Deserialization for pickling."""804        new_pb = self._meta.pb().FromString(value)805        super().__setattr__("_pb", new_pb)806 807 808class _MessageInfo:809    """Metadata about a message.810 811    Args:812        fields (Tuple[~.fields.Field]): The fields declared on the message.813        package (str): The proto package.814        full_name (str): The full name of the message.815        file_info (~._FileInfo): The file descriptor and messages for the816            file containing this message.817        marshal (~.Marshal): The marshal instance to which this message was818            automatically registered.819        options (~.descriptor_pb2.MessageOptions): Any options that were820            set on the message.821    """822 823    def __init__(824        self,825        *,826        fields: List[Field],827        package: str,828        full_name: str,829        marshal: Marshal,830        options: descriptor_pb2.MessageOptions,831    ) -> None:832        self.package = package833        self.full_name = full_name834        self.options = options835        self.fields = collections.OrderedDict((i.name, i) for i in fields)836        self.fields_by_number = collections.OrderedDict((i.number, i) for i in fields)837        self.marshal = marshal838        self._pb = None839 840    @property841    def pb(self) -> Type[message.Message]:842        """Return the protobuf message type for this descriptor.843 844        If a field on the message references another message which has not845        loaded, then this method returns None.846        """847        return self._pb848 849 850__all__ = ("Message",)851 
codekingpro/portable-devtools · Team Ai