betterproto2-compiler 0.7.1__py3-none-any.whl → 0.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,13 +13,14 @@ from .google_values import (
13
13
  UInt32Value,
14
14
  UInt64Value,
15
15
  )
16
+ from .struct import ListValue, Struct, Value
16
17
  from .timestamp import Timestamp
17
18
 
18
19
  # For each (package, message name), lists the methods that should be added to the message definition.
19
20
  # The source code of the method is read from the `known_types` folder. If imports are needed, they can be directly added
20
21
  # to the template file: they will automatically be removed if not necessary.
21
22
  KNOWN_METHODS: dict[tuple[str, str], list[Callable]] = {
22
- ("google.protobuf", "Any"): [Any.pack, Any.unpack, Any.to_dict],
23
+ ("google.protobuf", "Any"): [Any.pack, Any.unpack, Any.to_dict, Any.from_dict],
23
24
  ("google.protobuf", "Timestamp"): [
24
25
  Timestamp.from_datetime,
25
26
  Timestamp.to_datetime,
@@ -92,6 +93,18 @@ KNOWN_METHODS: dict[tuple[str, str], list[Callable]] = {
92
93
  BytesValue.from_wrapped,
93
94
  BytesValue.to_wrapped,
94
95
  ],
96
+ ("google.protobuf", "Struct"): [
97
+ Struct.from_dict,
98
+ Struct.to_dict,
99
+ ],
100
+ ("google.protobuf", "ListValue"): [
101
+ ListValue.from_dict,
102
+ ListValue.to_dict,
103
+ ],
104
+ ("google.protobuf", "Value"): [
105
+ Value.from_dict,
106
+ Value.to_dict,
107
+ ],
95
108
  }
96
109
 
97
110
  # A wrapped type is the type of a message that is automatically replaced by a known Python type.
@@ -8,7 +8,8 @@ default_message_pool = betterproto2.MessagePool() # Only for typing purpose
8
8
 
9
9
 
10
10
  class Any(VanillaAny):
11
- def pack(self, message: betterproto2.Message, message_pool: "betterproto2.MessagePool | None" = None) -> None:
11
+ @classmethod
12
+ def pack(cls, message: betterproto2.Message, message_pool: "betterproto2.MessagePool | None" = None) -> "Any":
12
13
  """
13
14
  Pack the given message in the `Any` object.
14
15
 
@@ -17,8 +18,10 @@ class Any(VanillaAny):
17
18
  """
18
19
  message_pool = message_pool or default_message_pool
19
20
 
20
- self.type_url = message_pool.type_to_url[type(message)]
21
- self.value = bytes(message)
21
+ type_url = message_pool.type_to_url[type(message)]
22
+ value = bytes(message)
23
+
24
+ return cls(type_url=type_url, value=value)
22
25
 
23
26
  def unpack(self, message_pool: "betterproto2.MessagePool | None" = None) -> betterproto2.Message | None:
24
27
  """
@@ -54,3 +57,21 @@ class Any(VanillaAny):
54
57
  output["value"] = value.to_dict(**kwargs)
55
58
 
56
59
  return output
60
+
61
+ # TODO typing
62
+ @classmethod
63
+ def from_dict(cls, value, *, ignore_unknown_fields: bool = False):
64
+ value = dict(value) # Make a copy
65
+
66
+ type_url = value.pop("@type", None)
67
+ msg_cls = default_message_pool.url_to_type.get(type_url, None)
68
+
69
+ if not msg_cls:
70
+ raise TypeError(f"Can't unpack unregistered type: {type_url}")
71
+
72
+ if not msg_cls.to_dict == betterproto2.Message.to_dict:
73
+ value = value["value"]
74
+
75
+ return cls(
76
+ type_url=type_url, value=bytes(msg_cls.from_dict(value, ignore_unknown_fields=ignore_unknown_fields))
77
+ )
@@ -30,7 +30,7 @@ class Duration(VanillaDuration):
30
30
 
31
31
  # TODO typing
32
32
  @classmethod
33
- def from_dict(cls, value):
33
+ def from_dict(cls, value, *, ignore_unknown_fields: bool = False):
34
34
  if isinstance(value, str):
35
35
  if not re.match(r"^\d+(\.\d+)?s$", value):
36
36
  raise ValueError(f"Invalid duration string: {value}")
@@ -38,7 +38,7 @@ class Duration(VanillaDuration):
38
38
  seconds = float(value[:-1])
39
39
  return Duration(seconds=int(seconds), nanos=int((seconds - int(seconds)) * 1e9))
40
40
 
41
- return super().from_dict(value)
41
+ return super().from_dict(value, ignore_unknown_fields=ignore_unknown_fields)
42
42
 
43
43
  # TODO typing
44
44
  def to_dict(
@@ -0,0 +1,111 @@
1
+ import typing
2
+
3
+ import betterproto2
4
+
5
+ from betterproto2_compiler.lib.google.protobuf import (
6
+ ListValue as VanillaListValue,
7
+ NullValue,
8
+ Struct as VanillaStruct,
9
+ Value as VanillaValue,
10
+ )
11
+
12
+
13
+ class Struct(VanillaStruct):
14
+ # TODO typing
15
+ @classmethod
16
+ def from_dict(cls, value, *, ignore_unknown_fields: bool = False):
17
+ assert isinstance(value, dict)
18
+
19
+ fields: dict[str, Value] = {}
20
+
21
+ for key, val in value.items():
22
+ fields[key] = Value.from_dict(val)
23
+
24
+ return cls(fields=fields) # type: ignore[reportArgumentType]
25
+
26
+ # TODO typing
27
+ def to_dict(
28
+ self,
29
+ *,
30
+ output_format: betterproto2.OutputFormat = betterproto2.OutputFormat.PROTO_JSON,
31
+ casing: betterproto2.Casing = betterproto2.Casing.CAMEL,
32
+ include_default_values: bool = False,
33
+ ) -> dict[str, typing.Any] | typing.Any:
34
+ # If the output format is PYTHON, we should have kept the wrapped type without building the real class
35
+ assert output_format == betterproto2.OutputFormat.PROTO_JSON
36
+
37
+ return {
38
+ key: value.to_dict(
39
+ output_format=output_format, casing=casing, include_default_values=include_default_values
40
+ )
41
+ for key, value in self.fields.items()
42
+ }
43
+
44
+
45
+ # We can't use the unwrap mechanism to support directly using a Python object due to the None case: it would then be
46
+ # impossible to distinguish between the absence of the message and a None value.
47
+ class Value(VanillaValue):
48
+ # TODO typing
49
+ @classmethod
50
+ def from_dict(cls, value, *, ignore_unknown_fields: bool = False):
51
+ match value:
52
+ case bool() as b:
53
+ return cls(bool_value=b)
54
+ case int() | float() as num:
55
+ return cls(number_value=num)
56
+ case str() as s:
57
+ return cls(string_value=s)
58
+ case list() as l:
59
+ return cls(list_value=ListValue.from_dict(l))
60
+ case dict() as d:
61
+ return cls(struct_value=Struct.from_dict(d))
62
+ case None:
63
+ return cls(null_value=NullValue.NULL_VALUE)
64
+ raise ValueError(f"Unknown value type: {type(value)}")
65
+
66
+ # TODO typing
67
+ def to_dict(
68
+ self,
69
+ *,
70
+ output_format: betterproto2.OutputFormat = betterproto2.OutputFormat.PROTO_JSON,
71
+ casing: betterproto2.Casing = betterproto2.Casing.CAMEL,
72
+ include_default_values: bool = False,
73
+ ) -> dict[str, typing.Any] | typing.Any:
74
+ # If the output format is PYTHON, we should have kept the wrapped type without building the real class
75
+ assert output_format == betterproto2.OutputFormat.PROTO_JSON
76
+
77
+ match self:
78
+ case Value(null_value=NullValue.NULL_VALUE):
79
+ return None
80
+ case Value(bool_value=bool(b)):
81
+ return b
82
+ case Value(number_value=int(num)) | Value(number_value=float(num)):
83
+ return num
84
+ case Value(string_value=str(s)):
85
+ return s
86
+ case Value(list_value=ListValue(values=l)):
87
+ return [v.to_dict() for v in l]
88
+ case Value(struct_value=Struct(fields=f)):
89
+ return {k: v.to_dict() for k, v in f.items()}
90
+
91
+ raise ValueError("Invalid value")
92
+
93
+
94
+ class ListValue(VanillaListValue):
95
+ # TODO typing
96
+ @classmethod
97
+ def from_dict(cls, value, *, ignore_unknown_fields: bool = False):
98
+ return cls(values=[Value.from_dict(v) for v in value])
99
+
100
+ # TODO typing
101
+ def to_dict(
102
+ self,
103
+ *,
104
+ output_format: betterproto2.OutputFormat = betterproto2.OutputFormat.PROTO_JSON,
105
+ casing: betterproto2.Casing = betterproto2.Casing.CAMEL,
106
+ include_default_values: bool = False,
107
+ ) -> dict[str, typing.Any] | typing.Any:
108
+ # If the output format is PYTHON, we should have kept the wrapped type without building the real class
109
+ assert output_format == betterproto2.OutputFormat.PROTO_JSON
110
+
111
+ return [value.to_dict() for value in self.values]
@@ -55,13 +55,13 @@ class Timestamp(VanillaTimestamp):
55
55
 
56
56
  # TODO typing
57
57
  @classmethod
58
- def from_dict(cls, value):
58
+ def from_dict(cls, value, *, ignore_unknown_fields: bool = False):
59
59
  if isinstance(value, str):
60
60
  dt = dateutil.parser.isoparse(value)
61
61
  dt = dt.astimezone(datetime.timezone.utc)
62
62
  return Timestamp.from_datetime(dt)
63
63
 
64
- return super().from_dict(value)
64
+ return super().from_dict(value, ignore_unknown_fields=ignore_unknown_fields)
65
65
 
66
66
  # TODO typing
67
67
  def to_dict(
@@ -0,0 +1,12 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # sources: protobuf.proto
3
+ # plugin: python-betterproto2
4
+ # This file has been @generated
5
+
6
+ __all__ = ()
7
+
8
+
9
+ import betterproto2
10
+
11
+ _COMPILER_VERSION = "0.8.0"
12
+ betterproto2.check_compiler_version(_COMPILER_VERSION)