| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716 |
- # Copyright (c) 2025-2026 Buf Technologies, Inc.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- from __future__ import annotations
- from copy import copy, deepcopy
- from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypeVar, overload
- from . import _native_message
- from ._descriptors import (
- DescField,
- DescFieldValueEnum,
- DescFieldValueList,
- DescFieldValueMap,
- DescFieldValueMessage,
- DescFieldValueScalar,
- DescFieldValueSingular,
- DescMessage,
- DescOneof,
- DescUnknownField,
- )
- from ._extension import Extension
- from ._field_values import default_value, is_zero_value
- from ._from_binary import FromBinaryOptions, read_message
- from ._native_message import object_setattr
- from ._oneof import Oneof
- from ._to_binary import ToBinaryOptions, write_message
- from ._unknown import get_unknown_field, has_unknown_field, set_unknown_field
- from ._wire import BinaryReader, BinaryWriter
- if TYPE_CHECKING:
- from collections.abc import Iterator
- from ._registry import Registry
- Self = TypeVar("Self", bound="Message")
- # TypeVar for making Message generic over its field names
- FieldNamesT = TypeVar("FieldNamesT", bound=str)
- M = TypeVar("M", bound="Message")
- E = TypeVar("E")
- class _MessageMeta(type):
- """Metaclass for Message to insert native extension when available."""
- def __new__(
- cls, name: str, bases: tuple[type, ...], classdict: dict[str, Any]
- ) -> _MessageMeta:
- is_message_base = not any(isinstance(base, _MessageMeta) for base in bases)
- # Don't eagerly import the member to allow easier overriding in benchmarks
- native_message_class = _native_message.NativeMessageClass
- if native_message_class:
- if is_message_base:
- # Python does not allow inheriting from multiple classes with fixed size, which includes
- # native classes and classes with __slots__. We go ahead and define any needed private
- # fields from Message in NativeMessage as well. A small amount of duplication, though it
- # also means we can use native alternatives where appropriate.
- classdict = {**classdict, "__slots__": ()}
- elif not any(
- isinstance(base, type) and issubclass(base, native_message_class)
- for base in bases
- ):
- bases = (native_message_class, *bases)
- return super().__new__(cls, name, bases, classdict)
- MessageMeta = _MessageMeta if _native_message.NativeMessageClass else type
- class Message(Generic[FieldNamesT], metaclass=MessageMeta): # noqa: PLW1641
- """Base class for Protobuf message types.
- Most `Message` subclasses are generated from `.proto` files. A message instance
- behaves like a regular Python object: construct it with keyword arguments using
- Python field names, then read and assign fields through attributes. `Message`
- provides all the shared runtime behavior for operating on messages.
- Thread safety:
- Message instances are not thread safe for concurrent mutation with any
- other operation. Concurrent reads without mutation are safe.
- Examples:
- ```python
- user = User(first_name="Alice", active=True)
- user.last_name = "Smith"
- ```
- """
- __slots__ = ("__weakref__", "_present", "_unknown_fields")
- if TYPE_CHECKING:
- _desc: ClassVar[DescMessage]
- _present: set[int]
- _unknown_fields: dict[int, list[bytes]] | None
- def __new__(cls: type[Self], *_args: Any, **_kwargs: Any) -> Self:
- msg = object.__new__(cls)
- object_setattr(msg, "_present", set())
- object_setattr(msg, "_unknown_fields", None)
- return msg
- def __init__(self, **kwargs: Any) -> None:
- """Initialize an instance of the message.
- Args:
- **kwargs: Field names (Python local names) and their values.
- """
- for local_name, default in self._desc._defaults:
- if local_name in kwargs:
- value = kwargs.pop(local_name)
- if value is not None:
- setattr(self, local_name, value)
- continue
- if isinstance(default, (list, dict)):
- object_setattr(self, local_name, default.__class__())
- else:
- object_setattr(self, local_name, default)
- # Error on unexpected argument
- if len(kwargs) > 0:
- msg = f"{type(self).__qualname__}.__init__() got an unexpected keyword argument '{next(iter(kwargs))}'"
- raise TypeError(msg)
- def to_json(
- self,
- *,
- registry: Registry | None = None,
- always_emit_implicit: bool = False,
- print_enums_as_ints: bool = False,
- use_proto_field_name: bool = False,
- ) -> str:
- """Serialize this message to a ProtoJSON string.
- By default, fields with implicit presence are not serialized if
- they are set to their zero value.
- A registry is required to serialize google.protobuf.Any fields
- and extensions. Extensions not found in the registry are silently
- omitted.
- Args:
- registry: A registry for resolving google.protobuf.Any messages
- and extensions.
- always_emit_implicit: By default, fields with implicit presence
- are omitted when set to their zero value (e.g. an empty
- list, a proto3 int32 field with value 0). If `True`, include
- these fields in the output.
- print_enums_as_ints: By default, the enum value name as defined
- in Protobuf is used. If `True`, use the numeric value instead.
- use_proto_field_name: By default, field names use the
- json_name field option, which defaults to lowerCamelCase.
- If `True`, use the Protobuf field name instead.
- Raises:
- ValueError: If a google.protobuf.Any field cannot be resolved
- through the registry.
- """
- from ._validate import validate # noqa: PLC0415
- validate(self)
- # Needs to be lazy import since JSON specially handles many WKTs.
- from ._to_json import ToJsonOptions, to_json # noqa: PLC0415
- return to_json(
- self,
- ToJsonOptions(
- always_emit_implicit=always_emit_implicit,
- print_enums_as_ints=print_enums_as_ints,
- use_proto_field_name=use_proto_field_name,
- registry=registry,
- ),
- )
- def to_binary(self, *, write_unknown_fields: bool = True) -> bytes:
- """Serialize this message to binary protobuf format.
- Args:
- write_unknown_fields: If `True`, unknown fields encountered
- during parsing are preserved in the output.
- Returns:
- The serialized binary protobuf bytes.
- """
- from ._validate import validate # noqa: PLC0415
- validate(self)
- writer = BinaryWriter()
- write_message(
- self, writer, ToBinaryOptions(write_unknown_fields=write_unknown_fields)
- )
- return writer.finish()
- def __copy__(self: Self) -> Self:
- """Create a shallow copy with independent presence and unknown fields tracking."""
- new: Self = self.__class__.__new__(type(self))
- for name in (
- *self._desc._fields_by_local_name,
- *self._desc._oneofs_by_local_name,
- ):
- if hasattr(self, name):
- object_setattr(new, name, getattr(self, name))
- new._present.update(self._present)
- if uf := self._unknown_fields:
- new._get_or_init_unknown_fields().update(
- {k: v.copy() for k, v in uf.items()}
- )
- return new
- def __deepcopy__(self: Self, _memo: dict[int, Any], /) -> Self:
- """Create a deep copy of the message."""
- from ._merge import merge_from # noqa: PLC0415
- new = type(self)()
- merge_from(new, self)
- return new
- def __repr__(self) -> str:
- """Return a string representation in `__init__` syntax.
- Unknown fields and extensions are not included in the output.
- """
- parts: list[str] = []
- for member in self._desc.members:
- if isinstance(member, DescOneof):
- if (value := getattr(self, member.local_name)) is not None:
- parts.append(f"{member.local_name}={value!r}")
- continue
- if member in self:
- value = self[member]
- parts.append(f"{member.local_name}={value!r}")
- return f"{self.__class__.__qualname__}({', '.join(parts)})"
- def __replace__(self: Self, **kwargs: Any) -> Self:
- """Create a copy by replacing fields.
- Similar to dataclasses.replace(), creates a shallow copy of the message
- with the specified fields updated to new values. This method is designed
- to work with `copy.replace()` available in Python 3.13+.
- Args:
- **kwargs: Field names and their new values.
- Returns:
- A new message instance with the specified fields replaced.
- Raises:
- AttributeError: If an unknown field name is provided.
- Examples:
- ```python
- msg1 = Message(field1=1, field2=2)
- msg2 = copy.replace(msg1, field1=10) # Python 3.13+
- # msg2 is Message(field1=10, field2=2)
- ```
- """
- # Create a shallow copy first
- new_instance = copy(self)
- for key, value in kwargs.items():
- setattr(new_instance, key, value)
- return new_instance
- def __setattr__(self, name: str, value: Any, /) -> None:
- """Set a field or oneof attribute by local name.
- Raises:
- AttributeError: If name is not a known field, oneof, or internal attribute.
- """
- if not self._desc._requires_presence:
- return object_setattr(self, name, value)
- field = self._desc._fields_by_local_name.get(name)
- if field is not None and field._requires_presence:
- self._set_field_number_present(field.number)
- object_setattr(self, name, value)
- return None
- def has_field(self, key: FieldNamesT, /) -> bool:
- """Check if a field is set by its proto name (e.g., `msg.has_field("field_name")`).
- This can be used to check whether a field with explicit presence has been set.
- Args:
- key: The proto field name as a string.
- Returns:
- `True` if the field is set, `False` otherwise.
- Raises:
- KeyError: If the field does not exist on this message.
- """
- return self._resolve_field(key) in self
- def clear_field(self, key: FieldNamesT, /) -> None:
- """Clear a field by its proto name (e.g., `msg.clear_field("field_name")`).
- This can be used to clear a field with explicit presence or reset a
- value to its default.
- Args:
- key: The proto field name as a string.
- Raises:
- KeyError: If the field does not exist on this message.
- """
- del self[self._resolve_field(key)]
- @overload
- def __getitem__(self, key: DescField | DescUnknownField, /) -> Any: ...
- @overload
- def __getitem__(self, key: Extension[M, E], /) -> E: ...
- def __getitem__(self, key: DescField | DescUnknownField | Extension, /) -> Any:
- """Get a field value by descriptor.
- Args:
- key: A `DescField`, `DescUnknownField`, or `Extension`.
- Returns:
- The field value, or the default if unset. For message fields the
- default is `None`; for scalars the zero value (`0`, `""`,
- `False`, etc.); for repeated/map fields an empty `list`/`dict`.
- If key is a oneof, returns a [`Oneof`][] if one of its fields is
- set, `None` otherwise.
- Raises:
- KeyError: If the field does not exist on this message.
- TypeError: If the key is not a DescField, DescUnknownField, or Extension.
- """
- if isinstance(key, (Extension, DescUnknownField)):
- if isinstance(key, Extension):
- key._assert_message_type(self)
- number = key._desc.number
- field_value = key._desc.value
- else:
- number = key.number
- field_value = key.value
- if (value := get_unknown_field(self, number, field_value)) is not None:
- return value
- return default_value(field_value)
- member = self._validate_member(key)
- return self._get_member(member)
- def _get_member(self, member: DescField) -> Any:
- """Get a field without validating."""
- if (
- isinstance(member.value, DescFieldValueSingular)
- and member.value.oneof is not None
- ):
- # Return the value if the field is selected in the oneof.
- # Fall back to the zero value.
- value = getattr(self, member.value.oneof.local_name)
- if isinstance(value, Oneof) and value.field == member.name:
- return value.value
- return default_value(member.value)
- # Return the attribute as is
- return getattr(self, member.local_name)
- @overload
- def __setitem__(self, key: DescField | DescUnknownField, value: Any, /) -> None: ...
- @overload
- def __setitem__(self, key: Extension[M, E], value: E, /) -> None: ...
- def __setitem__(
- self, key: DescField | DescUnknownField | Extension, value: Any, /
- ) -> None:
- """Set a field value by descriptor or proto name.
- Fields in a oneof can be set like regular fields, which resets siblings.
- Args:
- key: A `DescField`, `DescUnknownField`, or `Extension`. If key is a oneof, a
- [`Oneof`][] is accepted.
- value: The value to assign.
- Raises:
- KeyError: If the field does not exist on this message.
- TypeError: If the key is not a DescField, DescUnknownField, or Extension.
- """
- if isinstance(key, (Extension, DescUnknownField)):
- if isinstance(key, Extension):
- key._assert_message_type(self)
- number = key._desc.number
- field_value = key._desc.value
- else:
- number = key.number
- field_value = key.value
- set_unknown_field(self, number, field_value, value)
- return None
- member = self._validate_member(key)
- return self._set_member(member, value)
- def _set_member(self, member: DescField, value: Any) -> None:
- """Set a field without validating."""
- if (
- isinstance(member.value, DescFieldValueSingular)
- and member.value.oneof is not None
- ):
- # Set a new Oneof with the selected field and value
- value = Oneof(field=member.name, value=value)
- object_setattr(self, member.value.oneof.local_name, value)
- else:
- # Set the attribute as is
- object_setattr(self, member.local_name, value)
- if member._requires_presence:
- self._set_field_number_present(member.number)
- @overload
- def __contains__(self, key: DescField | DescUnknownField, /) -> bool: ...
- @overload
- def __contains__(self, key: Extension[M, E], /) -> bool: ...
- def __contains__(self, key: DescField | DescUnknownField | Extension, /) -> bool:
- """Check if a field is set (e.g., `desc in msg`).
- For fields with explicit presence, checks if the field has been set.
- For fields without presence (implicit presence), compares against the
- default value.
- Args:
- key: A `DescField`, `DescUnknownField`, or `Extension`. If key is a oneof, a
- [`Oneof`][] is accepted.
- Returns:
- `True` if the field is set or non-zero. If key is a oneof,
- `True` if any of its fields is set.
- Raises:
- KeyError: If the field does not exist on this message.
- TypeError: If the key is not a DescField, DescUnknownField, or Extension.
- """
- if isinstance(key, (Extension, DescUnknownField)):
- if isinstance(key, Extension):
- if not key._is_correct_message_type(self):
- return False
- number = key._desc.number
- field_value = key._desc.value
- else:
- number = key.number
- field_value = key.value
- return has_unknown_field(self, number, field_value)
- member = self._validate_member(key)
- return self._contains_member(member)
- def _contains_member(self, member: DescField) -> bool:
- """Check if a field is set without validating."""
- match field_value := member.value:
- case (
- DescFieldValueScalar(oneof=desc_oneof)
- | DescFieldValueMessage(oneof=desc_oneof)
- | DescFieldValueEnum(oneof=desc_oneof)
- ) if desc_oneof is not None:
- oneof = getattr(self, desc_oneof.local_name)
- return isinstance(oneof, Oneof) and oneof.field == member.name
- case DescFieldValueScalar(oneof=None) | DescFieldValueEnum(oneof=None):
- if member._requires_presence:
- return self._get_field_number_present(member.number)
- return not is_zero_value(field_value, getattr(self, member.local_name))
- @overload
- def __delitem__(self, key: DescField | DescUnknownField, /) -> None: ...
- @overload
- def __delitem__(self, key: Extension[M, E], /) -> None: ...
- def __delitem__(self, key: DescField | DescUnknownField | Extension, /) -> None:
- """Clear a field by its key (e.g., `del msg[desc]`).
- Clears the field to its default value. Does not delete the attribute.
- For message fields, sets to `None`; for scalars, the zero value;
- for repeated/map fields, clears to an empty `list`/`dict`.
- Args:
- key: A `DescField`, `DescUnknownField`, or `Extension`.
- Raises:
- KeyError: If the field does not exist on this message.
- TypeError: If the key is not a DescField, DescUnknownField, or Extension.
- """
- if isinstance(key, (Extension, DescUnknownField)):
- if isinstance(key, Extension):
- key._assert_message_type(self)
- number = key._desc.number
- else:
- number = key.number
- if uf := self._unknown_fields:
- uf.pop(number, None)
- return
- member = self._validate_member(key)
- self._del_member(member)
- def _del_member(self, member: DescField) -> None:
- """Delete a field without validating."""
- attr = member.local_name
- match field_value := member.value:
- case (
- DescFieldValueScalar() | DescFieldValueMessage() | DescFieldValueEnum()
- ):
- if field_value.oneof is None:
- # Bypass __setattr__ to avoid marking the field as present
- object_setattr(self, attr, default_value(field_value))
- self._clear_field_number_present(member.number)
- else:
- oneof = getattr(self, field_value.oneof.local_name)
- if isinstance(oneof, Oneof) and oneof.field == member.name:
- # Clear the oneof attribute
- object_setattr(self, field_value.oneof.local_name, None)
- case DescFieldValueList() | DescFieldValueMap():
- # Get the collection and clear it, fall back to setting a new collection
- if not hasattr(self, attr):
- object_setattr(self, attr, default_value(field_value))
- return
- value = getattr(self, attr)
- if not isinstance(value, list) and not isinstance(value, dict):
- object_setattr(self, attr, default_value(field_value))
- return
- value.clear()
- def __iter__(self) -> Iterator[DescField]:
- """Iterate over all [`DescField`][]s in this message that are set."""
- for field in self._desc.fields:
- if self._contains_member(field):
- yield field
- def __eq__(self, other: object, /) -> bool:
- """Compare two messages for equality.
- Two messages are considered equal when all of the following hold:
- - They have the same type.
- - For every field, both messages agree on whether the field is set or unset.
- - All set fields have equal values.
- NaN-valued floats are treated as equal to each other, consistent with
- Python's container equality semantics (e.g. `list`, `dict`).
- Extensions and unknown fields are not considered in the comparison.
- """
- if not isinstance(other, type(self)):
- return NotImplemented
- for field in self._desc.fields:
- self_set = field in self
- other_set = field in other
- if self_set != other_set:
- return False
- if self_set:
- self_val = self[field]
- other_val = other[field]
- if self_val is not other_val and self_val != other_val:
- return False
- return True
- @classmethod
- def from_json(
- cls: type[Self],
- json: str | bytes | bytearray,
- *,
- ignore_unknown_fields: bool = False,
- registry: Registry | None = None,
- ) -> Self:
- """Create a new message from a ProtoJSON string.
- Args:
- json: A str, bytes, or bytearray instance containing the ProtoJSON.
- ignore_unknown_fields:
- Proto3 JSON parser should reject unknown fields by default.
- This option ignores unknown fields in parsing, as well as unrecognized
- enum string representations.
- registry:
- This option is required to read `google.protobuf.Any` and extensions
- from JSON format.
- Raises:
- json.JSONDecodeError: If json_source is not valid JSON.
- TypeError: If the JSON structure does not match expected types.
- ValueError: If a google.protobuf.Any or an extension cannot be resolved
- through the registry.
- """
- from ._from_json import merge_from_json # noqa: PLC0415
- msg = cls()
- merge_from_json(
- msg, json, ignore_unknown_fields=ignore_unknown_fields, registry=registry
- )
- return msg
- @classmethod
- def from_binary(
- cls: type[Self], data: bytes, *, ignore_unknown_fields: bool = False
- ) -> Self:
- """Create a new message by parsing serialized binary data.
- To merge into an existing message, use [`merge_from_binary`][].
- Args:
- data: Serialized binary protobuf data.
- ignore_unknown_fields: If `True`, unknown fields in the binary data are silently discarded.
- """
- message = cls()
- message._merge_from_binary(data, ignore_unknown_fields=ignore_unknown_fields)
- return message
- @classmethod
- def desc(cls) -> DescMessage:
- """Get the associated DescMessage with this type."""
- return cls._desc
- def __getstate__(self) -> object:
- return self.to_binary()
- def __setstate__(self, state: object, /) -> None:
- if not isinstance(state, bytes):
- msg = f"invalid state for unpickling {self.__class__.__name__}: expected bytes, got {type(state).__name__}"
- raise TypeError(msg)
- self.__init__()
- self._merge_from_binary(state, ignore_unknown_fields=False)
- def _validate_member(self, key: DescField) -> DescField:
- """Validates the DescField is part of this message.
- Raises:
- KeyError: If the field does not exist on this message.
- TypeError: If the key is not a DescField.
- """
- if isinstance(key, DescField):
- if key.parent.type_name == self._desc.type_name:
- return key
- msg = f"{key!s} cannot be used with {self._desc!s}"
- raise KeyError(msg)
- msg = f"key must be a DescField, not {type(key).__name__}"
- raise TypeError(msg)
- def _resolve_field(self, key: str) -> DescField:
- if not isinstance(key, str):
- msg = f"key must be a str, not {type(key).__name__}"
- raise TypeError(msg)
- if field := self._desc._fields_by_name.get(key):
- return field
- if field := self._desc._fields_by_local_name.get(key):
- msg = (
- f"unknown key for {self._desc!s}: {key!r}, did you mean {field.name!r}?"
- )
- raise KeyError(msg)
- msg = f"unknown key for {self._desc!s}: {key!r}"
- raise KeyError(msg)
- def _merge_from_binary(self, data: bytes, ignore_unknown_fields: bool) -> None: # noqa: FBT001
- opts = FromBinaryOptions(ignore_unknown_fields=ignore_unknown_fields)
- read_message(
- self, BinaryReader(memoryview(data)), opts, depth=0, length=len(data)
- )
- def _merge_from(self: Self, source: Self, ignore_unknown_fields: bool) -> None: # noqa: FBT001
- for field in source:
- match field_value := field.value:
- case DescFieldValueMessage():
- if field in self:
- self[field]._merge_from(
- source[field], ignore_unknown_fields=ignore_unknown_fields
- )
- else:
- self[field] = deepcopy(source[field])
- case DescFieldValueList():
- target_list: list = self[field]
- if isinstance(field_value.element, DescMessage):
- target_list.extend(deepcopy(m) for m in source[field])
- else:
- target_list.extend(source[field])
- case DescFieldValueMap():
- target_map: dict = self[field]
- if isinstance(field_value.value, DescMessage):
- for key, value in source[field].items():
- target_map[key] = deepcopy(value)
- else:
- target_map.update(source[field])
- case _:
- self[field] = source[field]
- if not ignore_unknown_fields and (uf := source._unknown_fields):
- for key, value in uf.items():
- self._get_or_init_unknown_fields().setdefault(key, []).extend(value)
- # Methods for updating whether a field is present or not by number. Overridden by native code.
- def _get_field_number_present(self, number: int) -> bool:
- return number in self._present
- def _set_field_number_present(self, number: int) -> None:
- self._present.add(number)
- def _clear_field_number_present(self, number: int) -> None:
- self._present.discard(number)
- def _get_or_init_unknown_fields(self) -> dict[int, list[bytes]]:
- if (uf := self._unknown_fields) is None:
- uf = {}
- object_setattr(self, "_unknown_fields", uf)
- return uf
|