_codec.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. from __future__ import annotations
  2. from typing import Protocol, TypeVar
  3. from protobuf import Message, Registry
  4. from protobuf.wkt import (
  5. api_pb,
  6. cpp_features_pb,
  7. descriptor_pb,
  8. duration_pb,
  9. empty_pb,
  10. field_mask_pb,
  11. go_features_pb,
  12. java_features_pb,
  13. source_context_pb,
  14. struct_pb,
  15. timestamp_pb,
  16. type_pb,
  17. wrappers_pb,
  18. )
  19. CODEC_NAME_PROTO = "proto"
  20. CODEC_NAME_JSON = "json"
  21. DEFAULT_REGISTRY = Registry(
  22. api_pb.desc(),
  23. cpp_features_pb.desc(),
  24. descriptor_pb.desc(),
  25. duration_pb.desc(),
  26. empty_pb.desc(),
  27. field_mask_pb.desc(),
  28. go_features_pb.desc(),
  29. java_features_pb.desc(),
  30. source_context_pb.desc(),
  31. struct_pb.desc(),
  32. timestamp_pb.desc(),
  33. type_pb.desc(),
  34. wrappers_pb.desc(),
  35. )
  36. T_contra = TypeVar("T_contra", contravariant=True)
  37. U = TypeVar("U")
  38. V = TypeVar("V", bound=Message)
  39. class Codec(Protocol[T_contra, U]):
  40. def name(self) -> str:
  41. """Returns the name of the codec.
  42. This corresponds to the content-type used in requests.
  43. """
  44. ...
  45. def encode(self, message: T_contra) -> bytes:
  46. """Marshals the given message."""
  47. ...
  48. def decode(self, data: bytes | bytearray, message_class: type[U]) -> U:
  49. """Unmarshals the given message."""
  50. ...
  51. class ProtoBinaryCodec(Codec[Message, V]):
  52. """Codec for the Protocol Buffers binary format."""
  53. def name(self) -> str:
  54. return "proto"
  55. def encode(self, message: Message) -> bytes:
  56. return message.to_binary()
  57. def decode(self, data: bytes | bytearray, message_class: type[V]) -> V:
  58. return message_class.from_binary(data)
  59. class ProtoJSONCodec(Codec[Message, V]):
  60. """Codec for the Protocol Buffers JSON format."""
  61. def __init__(self, name: str = "json", registry: Registry | None = None) -> None:
  62. self._name = name
  63. self._registry = registry or DEFAULT_REGISTRY
  64. def name(self) -> str:
  65. return self._name
  66. def encode(self, message: Message) -> bytes:
  67. return message.to_json(registry=self._registry).encode()
  68. def decode(self, data: bytes | bytearray, message_class: type[V]) -> V:
  69. return message_class.from_json(data, registry=self._registry)
  70. _proto_binary_codec = ProtoBinaryCodec()
  71. _proto_json_codec = ProtoJSONCodec()
  72. _default_codecs: list[Codec] = [_proto_binary_codec, _proto_json_codec]
  73. def get_default_codecs() -> list[Codec]:
  74. return _default_codecs
  75. def proto_binary_codec() -> Codec:
  76. """Returns the Protocol Buffers binary codec."""
  77. return _proto_binary_codec
  78. def proto_json_codec(registry: Registry | None = None) -> Codec:
  79. """Returns the Protocol Buffers JSON codec.
  80. Args:
  81. registry: An optional protobuf Registry to use for marshaling Any and extensions in messages.
  82. If not provided, a default registry containing WKTs will be used.
  83. """
  84. if registry:
  85. return ProtoJSONCodec(name=CODEC_NAME_JSON, registry=registry)
  86. return _proto_json_codec