betterproto2-compiler 0.5.1__py3-none-any.whl → 0.7.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.
@@ -55,6 +55,32 @@ def parse_source_type_name(field_type_name: str, request: PluginRequestCompiler)
55
55
  raise ValueError(f"can't find type name: {field_type_name}")
56
56
 
57
57
 
58
+ def get_symbol_reference(
59
+ *,
60
+ package: str,
61
+ imports: set,
62
+ source_package: str,
63
+ symbol: str,
64
+ ) -> tuple[str, str | None]:
65
+ """
66
+ Return a Python symbol within a proto package. Adds the import if
67
+ necessary and returns it as well for usage. Unwraps well known type if required.
68
+ """
69
+ current_package: list[str] = package.split(".") if package else []
70
+ py_package: list[str] = source_package.split(".") if source_package else []
71
+
72
+ if py_package == current_package:
73
+ return (reference_sibling(symbol), None)
74
+
75
+ if py_package[: len(current_package)] == current_package:
76
+ return reference_descendent(current_package, imports, py_package, symbol)
77
+
78
+ if current_package[: len(py_package)] == py_package:
79
+ return reference_ancestor(current_package, imports, py_package, symbol)
80
+
81
+ return reference_cousin(current_package, imports, py_package, symbol)
82
+
83
+
58
84
  def get_type_reference(
59
85
  *,
60
86
  package: str,
@@ -73,30 +99,25 @@ def get_type_reference(
73
99
  if wrap and (source_package, source_type) in WRAPPED_TYPES:
74
100
  return WRAPPED_TYPES[(source_package, source_type)]
75
101
 
76
- current_package: list[str] = package.split(".") if package else []
77
- py_package: list[str] = source_package.split(".") if source_package else []
78
102
  py_type: str = pythonize_class_name(source_type)
79
-
80
- if py_package == current_package:
81
- return reference_sibling(py_type)
82
-
83
- if py_package[: len(current_package)] == current_package:
84
- return reference_descendent(current_package, imports, py_package, py_type)
85
-
86
- if current_package[: len(py_package)] == py_package:
87
- return reference_ancestor(current_package, imports, py_package, py_type)
88
-
89
- return reference_cousin(current_package, imports, py_package, py_type)
103
+ (ref, _) = get_symbol_reference(
104
+ package=package,
105
+ imports=imports,
106
+ source_package=source_package,
107
+ symbol=py_type,
108
+ )
109
+ return ref
90
110
 
91
111
 
92
- def reference_absolute(imports: set[str], py_package: list[str], py_type: str) -> str:
112
+ def reference_absolute(imports: set[str], py_package: list[str], py_type: str) -> tuple[str, str]:
93
113
  """
94
114
  Returns a reference to a python type located in the root, i.e. sys.path.
95
115
  """
96
116
  string_import = ".".join(py_package)
97
117
  string_alias = "__".join([safe_snake_case(name) for name in py_package])
98
- imports.add(f"import {string_import} as {string_alias}")
99
- return f"{string_alias}.{py_type}"
118
+ import_to_add = f"import {string_import} as {string_alias}"
119
+ imports.add(import_to_add)
120
+ return (f"{string_alias}.{py_type}", import_to_add)
100
121
 
101
122
 
102
123
  def reference_sibling(py_type: str) -> str:
@@ -106,7 +127,9 @@ def reference_sibling(py_type: str) -> str:
106
127
  return f"{py_type}"
107
128
 
108
129
 
109
- def reference_descendent(current_package: list[str], imports: set[str], py_package: list[str], py_type: str) -> str:
130
+ def reference_descendent(
131
+ current_package: list[str], imports: set[str], py_package: list[str], py_type: str
132
+ ) -> tuple[str, str]:
110
133
  """
111
134
  Returns a reference to a python type in a package that is a descendent of the
112
135
  current package, and adds the required import that is aliased to avoid name
@@ -116,15 +139,19 @@ def reference_descendent(current_package: list[str], imports: set[str], py_packa
116
139
  string_from = ".".join(importing_descendent[:-1])
117
140
  string_import = importing_descendent[-1]
118
141
  if string_from:
119
- string_alias = "_".join(importing_descendent)
120
- imports.add(f"from .{string_from} import {string_import} as {string_alias}")
121
- return f"{string_alias}.{py_type}"
142
+ string_alias = f"{'_'.join(importing_descendent)}"
143
+ import_to_add = f"from .{string_from} import {string_import} as {string_alias}"
144
+ imports.add(import_to_add)
145
+ return (f"{string_alias}.{py_type}", import_to_add)
122
146
  else:
123
- imports.add(f"from . import {string_import}")
124
- return f"{string_import}.{py_type}"
147
+ import_to_add = f"from . import {string_import}"
148
+ imports.add(import_to_add)
149
+ return (f"{string_import}.{py_type}", import_to_add)
125
150
 
126
151
 
127
- def reference_ancestor(current_package: list[str], imports: set[str], py_package: list[str], py_type: str) -> str:
152
+ def reference_ancestor(
153
+ current_package: list[str], imports: set[str], py_package: list[str], py_type: str
154
+ ) -> tuple[str, str]:
128
155
  """
129
156
  Returns a reference to a python type in a package which is an ancestor to the
130
157
  current package, and adds the required import that is aliased (if possible) to avoid
@@ -137,15 +164,19 @@ def reference_ancestor(current_package: list[str], imports: set[str], py_package
137
164
  string_import = py_package[-1]
138
165
  string_alias = f"_{'_' * distance_up}{string_import}__"
139
166
  string_from = f"..{'.' * distance_up}"
140
- imports.add(f"from {string_from} import {string_import} as {string_alias}")
141
- return f"{string_alias}.{py_type}"
167
+ import_to_add = f"from {string_from} import {string_import} as {string_alias}"
168
+ imports.add(import_to_add)
169
+ return (f"{string_alias}.{py_type}", import_to_add)
142
170
  else:
143
171
  string_alias = f"{'_' * distance_up}{py_type}__"
144
- imports.add(f"from .{'.' * distance_up} import {py_type} as {string_alias}")
145
- return string_alias
172
+ import_to_add = f"from .{'.' * distance_up} import {py_type} as {string_alias}"
173
+ imports.add(import_to_add)
174
+ return (string_alias, import_to_add)
146
175
 
147
176
 
148
- def reference_cousin(current_package: list[str], imports: set[str], py_package: list[str], py_type: str) -> str:
177
+ def reference_cousin(
178
+ current_package: list[str], imports: set[str], py_package: list[str], py_type: str
179
+ ) -> tuple[str, str]:
149
180
  """
150
181
  Returns a reference to a python type in a package that is not descendent, ancestor
151
182
  or sibling, and adds the required import that is aliased to avoid name conflicts.
@@ -161,5 +192,6 @@ def reference_cousin(current_package: list[str], imports: set[str], py_package:
161
192
  + "__".join([safe_snake_case(name) for name in py_package[len(shared_ancestry) :]])
162
193
  + "__"
163
194
  )
164
- imports.add(f"from {string_from} import {string_import} as {string_alias}")
165
- return f"{string_alias}.{py_type}"
195
+ import_to_add = f"from {string_from} import {string_import} as {string_alias}"
196
+ imports.add(import_to_add)
197
+ return (f"{string_alias}.{py_type}", import_to_add)
@@ -80,7 +80,7 @@ import dateutil.parser
80
80
 
81
81
  from ...message_pool import default_message_pool
82
82
 
83
- betterproto2.check_compiler_version("0.5.0")
83
+ betterproto2.check_compiler_version("0.7.0")
84
84
 
85
85
 
86
86
  class FieldCardinality(betterproto2.Enum):
@@ -17,7 +17,7 @@ import betterproto2
17
17
 
18
18
  from ....message_pool import default_message_pool
19
19
 
20
- betterproto2.check_compiler_version("0.5.0")
20
+ betterproto2.check_compiler_version("0.7.0")
21
21
 
22
22
 
23
23
  class CodeGeneratorResponseFeature(betterproto2.Enum):
@@ -33,17 +33,27 @@ def outputfile_compiler(output_file: OutputTemplate) -> str:
33
33
  loader=jinja2.FileSystemLoader(templates_folder),
34
34
  undefined=jinja2.StrictUndefined,
35
35
  )
36
- # Load the body first so we have a compleate list of imports needed.
36
+
37
+ # List of the symbols that should appear in the `__all__` variable of the file
38
+ all: list[str] = []
39
+
40
+ def add_to_all(name: str) -> str:
41
+ all.append(name)
42
+ return name
43
+
44
+ env.filters["add_to_all"] = add_to_all
45
+
37
46
  body_template = env.get_template("template.py.j2")
38
47
  header_template = env.get_template("header.py.j2")
39
48
 
49
+ # Load the body first do know the symbols defined in the file
40
50
  code = body_template.render(output_file=output_file)
41
- code = header_template.render(output_file=output_file, version=version) + "\n" + code
51
+ code = header_template.render(output_file=output_file, version=version, all=all) + "\n" + code
42
52
 
43
53
  try:
44
- # Sort imports, delete unused ones
54
+ # Sort imports, delete unused ones, sort __all__
45
55
  code = subprocess.check_output(
46
- ["ruff", "check", "--select", "I,F401,TCH005", "--fix", "--silent", "-"],
56
+ ["ruff", "check", "--select", "I,F401,TC005,RUF022", "--fix", "--silent", "-"],
47
57
  input=code,
48
58
  encoding="utf-8",
49
59
  )
@@ -53,6 +53,7 @@ from betterproto2_compiler.lib.google.protobuf import (
53
53
  MethodDescriptorProto,
54
54
  OneofDescriptorProto,
55
55
  ServiceDescriptorProto,
56
+ SourceCodeInfo,
56
57
  )
57
58
  from betterproto2_compiler.lib.google.protobuf.compiler import CodeGeneratorRequest
58
59
  from betterproto2_compiler.settings import Settings
@@ -216,6 +217,33 @@ class OutputTemplate:
216
217
  """
217
218
  return sorted([f.name for f in self.input_files])
218
219
 
220
+ def get_descriptor_name(self, source_file: FileDescriptorProto):
221
+ return f"{source_file.name.replace('/', '_').replace('.', '_').upper()}_DESCRIPTOR"
222
+
223
+ @property
224
+ def descriptors(self):
225
+ """Google protobuf library descriptors.
226
+
227
+ Returns
228
+ -------
229
+ str
230
+ A list of pool registrations for proto descriptors.
231
+ """
232
+ descriptors: list[str] = []
233
+
234
+ for f in self.input_files:
235
+ # Remove the source_code_info field since it is not needed at runtime.
236
+ source_code_info: SourceCodeInfo | None = f.source_code_info
237
+ f.source_code_info = None
238
+
239
+ descriptors.append(
240
+ f"{self.get_descriptor_name(f)} = default_google_proto_descriptor_pool.AddSerializedFile({bytes(f)})"
241
+ )
242
+
243
+ f.source_code_info = source_code_info
244
+
245
+ return "\n".join(descriptors)
246
+
219
247
 
220
248
  @dataclass(kw_only=True)
221
249
  class MessageCompiler(ProtoContentBase):
@@ -223,6 +251,7 @@ class MessageCompiler(ProtoContentBase):
223
251
 
224
252
  output_file: OutputTemplate
225
253
  proto_obj: DescriptorProto
254
+ prefixed_proto_name: str
226
255
  fields: list["FieldCompiler"] = field(default_factory=list)
227
256
  oneofs: list["OneofCompiler"] = field(default_factory=list)
228
257
  builtins_types: set[str] = field(default_factory=set)
@@ -233,7 +262,7 @@ class MessageCompiler(ProtoContentBase):
233
262
 
234
263
  @property
235
264
  def py_name(self) -> str:
236
- return pythonize_class_name(self.proto_name)
265
+ return pythonize_class_name(self.prefixed_proto_name)
237
266
 
238
267
  @property
239
268
  def deprecated(self) -> bool:
@@ -266,6 +295,17 @@ class MessageCompiler(ProtoContentBase):
266
295
 
267
296
  return methods_source
268
297
 
298
+ @property
299
+ def descriptor_name(self) -> str:
300
+ """Google protobuf library descriptor name.
301
+
302
+ Returns
303
+ -------
304
+ str
305
+ The Python name of the descriptor to reference.
306
+ """
307
+ return self.output_file.get_descriptor_name(self.source_file)
308
+
269
309
 
270
310
  def is_map(proto_field_obj: FieldDescriptorProto, parent_message: DescriptorProto) -> bool:
271
311
  """True if proto_field_obj is a map, otherwise False."""
@@ -353,8 +393,7 @@ class FieldCompiler(ProtoContentBase):
353
393
 
354
394
  @property
355
395
  def field_type(self) -> FieldType:
356
- # TODO it should be possible to remove constructor
357
- return FieldType(self.proto_obj.type)
396
+ return self.proto_obj.type
358
397
 
359
398
  @property
360
399
  def packed(self) -> bool:
@@ -411,12 +450,46 @@ class FieldCompiler(ProtoContentBase):
411
450
  def unwrapped_py_type(self) -> str:
412
451
  return self._py_type(wrap=False)
413
452
 
453
+ @property
454
+ def annotations(self) -> list[str]:
455
+ """List of the Pydantic annotation to add to the field."""
456
+ assert self.output_file.settings.pydantic_dataclasses
457
+
458
+ annotations = []
459
+
460
+ if self.proto_obj.type in (FieldType.TYPE_INT32, FieldType.TYPE_SFIXED32, FieldType.TYPE_SINT32):
461
+ annotations.append("pydantic.Field(ge=-2**31, le=2**31 - 1)")
462
+
463
+ elif self.proto_obj.type in (FieldType.TYPE_UINT32, FieldType.TYPE_FIXED32):
464
+ annotations.append("pydantic.Field(ge=0, le=2**32 - 1)")
465
+
466
+ elif self.proto_obj.type in (FieldType.TYPE_INT64, FieldType.TYPE_SFIXED64, FieldType.TYPE_SINT64):
467
+ annotations.append("pydantic.Field(ge=-2**63, le=2**63 - 1)")
468
+
469
+ elif self.proto_obj.type in (FieldType.TYPE_UINT64, FieldType.TYPE_FIXED64):
470
+ annotations.append("pydantic.Field(ge=0, le=2**64 - 1)")
471
+
472
+ elif self.proto_obj.type == FieldType.TYPE_FLOAT:
473
+ annotations.append("pydantic.AfterValidator(betterproto2.validators.validate_float32)")
474
+
475
+ elif self.proto_obj.type == FieldType.TYPE_STRING:
476
+ annotations.append("pydantic.AfterValidator(betterproto2.validators.validate_string)")
477
+
478
+ return annotations
479
+
414
480
  @property
415
481
  def annotation(self) -> str:
416
482
  py_type = self.py_type
417
483
 
418
484
  if self.use_builtins:
419
485
  py_type = f"builtins.{py_type}"
486
+
487
+ # Add the pydantic annotation if needed
488
+ if self.output_file.settings.pydantic_dataclasses:
489
+ annotations = self.annotations
490
+ if annotations:
491
+ py_type = f"typing.Annotated[{py_type}, {', '.join(annotations)}]"
492
+
420
493
  if self.repeated:
421
494
  return f"list[{py_type}]"
422
495
  if self.optional:
@@ -529,6 +602,7 @@ class EnumDefinitionCompiler(ProtoContentBase):
529
602
 
530
603
  output_file: OutputTemplate
531
604
  proto_obj: EnumDescriptorProto
605
+ prefixed_proto_name: str
532
606
  entries: list["EnumDefinitionCompiler.EnumEntry"] = field(default_factory=list)
533
607
 
534
608
  @dataclass(unsafe_hash=True, kw_only=True)
@@ -556,12 +630,23 @@ class EnumDefinitionCompiler(ProtoContentBase):
556
630
 
557
631
  @property
558
632
  def py_name(self) -> str:
559
- return pythonize_class_name(self.proto_name)
633
+ return pythonize_class_name(self.prefixed_proto_name)
560
634
 
561
635
  @property
562
636
  def deprecated(self) -> bool:
563
637
  return bool(self.proto_obj.options and self.proto_obj.options.deprecated)
564
638
 
639
+ @property
640
+ def descriptor_name(self) -> str:
641
+ """Google protobuf library descriptor name.
642
+
643
+ Returns
644
+ -------
645
+ str
646
+ The Python name of the descriptor to reference.
647
+ """
648
+ return self.output_file.get_descriptor_name(self.source_file)
649
+
565
650
 
566
651
  @dataclass(kw_only=True)
567
652
  class ServiceCompiler(ProtoContentBase):
@@ -35,20 +35,21 @@ from .models import (
35
35
 
36
36
  def traverse(
37
37
  proto_file: FileDescriptorProto,
38
- ) -> Generator[tuple[EnumDescriptorProto | DescriptorProto, list[int]], None, None]:
38
+ ) -> Generator[tuple[EnumDescriptorProto | DescriptorProto, list[int], str], None, None]:
39
39
  # Todo: Keep information about nested hierarchy
40
40
  def _traverse(
41
41
  path: list[int],
42
42
  items: list[EnumDescriptorProto] | list[DescriptorProto],
43
43
  prefix: str = "",
44
- ) -> Generator[tuple[EnumDescriptorProto | DescriptorProto, list[int]], None, None]:
44
+ ) -> Generator[tuple[EnumDescriptorProto | DescriptorProto, list[int], str], None, None]:
45
45
  for i, item in enumerate(items):
46
46
  # Adjust the name since we flatten the hierarchy.
47
- # Todo: don't change the name, but include full name in returned tuple
48
47
  should_rename = not isinstance(item, DescriptorProto) or not item.options or not item.options.map_entry
49
48
 
50
- item.name = next_prefix = f"{prefix}.{item.name}" if prefix and should_rename else item.name
51
- yield item, [*path, i]
49
+ # Record prefixed name but *do not* mutate original file.
50
+ # We use this prefixed name to create pythonized names.
51
+ prefixed_name = next_prefix = f"{prefix}.{item.name}" if prefix and should_rename else item.name
52
+ yield item, [*path, i], prefixed_name
52
53
 
53
54
  if isinstance(item, DescriptorProto):
54
55
  # Get nested types.
@@ -81,6 +82,7 @@ def get_settings(plugin_options: list[str]) -> Settings:
81
82
 
82
83
  return Settings(
83
84
  pydantic_dataclasses="pydantic_dataclasses" in plugin_options,
85
+ google_protobuf_descriptors="google_protobuf_descriptors" in plugin_options,
84
86
  client_generation=client_generation,
85
87
  server_generation=server_generation,
86
88
  )
@@ -109,12 +111,13 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse:
109
111
  # get the references to input/output messages for each service
110
112
  for output_package_name, output_package in request_data.output_packages.items():
111
113
  for proto_input_file in output_package.input_files:
112
- for item, path in traverse(proto_input_file):
114
+ for item, path, prefixed_proto_name in traverse(proto_input_file):
113
115
  read_protobuf_type(
114
116
  source_file=proto_input_file,
115
117
  item=item,
116
118
  path=path,
117
119
  output_package=output_package,
120
+ prefixed_proto_name=prefixed_proto_name,
118
121
  )
119
122
 
120
123
  # Read Services
@@ -168,6 +171,15 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse:
168
171
  )
169
172
  )
170
173
 
174
+ if settings.google_protobuf_descriptors:
175
+ response.file.append(
176
+ CodeGeneratorResponseFile(
177
+ name="google_proto_descriptor_pool.py",
178
+ content="from google.protobuf import descriptor_pool\n\n"
179
+ + "default_google_proto_descriptor_pool = descriptor_pool.DescriptorPool()\n",
180
+ )
181
+ )
182
+
171
183
  for output_package_name in sorted(output_paths.union(init_files)):
172
184
  print(f"Writing {output_package_name}", file=sys.stderr)
173
185
 
@@ -179,6 +191,7 @@ def read_protobuf_type(
179
191
  path: list[int],
180
192
  source_file: "FileDescriptorProto",
181
193
  output_package: OutputTemplate,
194
+ prefixed_proto_name: str,
182
195
  ) -> None:
183
196
  if isinstance(item, DescriptorProto):
184
197
  if item.options and item.options.map_entry:
@@ -188,10 +201,11 @@ def read_protobuf_type(
188
201
  message_data = MessageCompiler(
189
202
  source_file=source_file,
190
203
  output_file=output_package,
204
+ prefixed_proto_name=prefixed_proto_name,
191
205
  proto_obj=item,
192
206
  path=path,
193
207
  )
194
- output_package.messages[message_data.proto_name] = message_data
208
+ output_package.messages[message_data.prefixed_proto_name] = message_data
195
209
 
196
210
  for index, field in enumerate(item.field):
197
211
  if is_map(field, item):
@@ -243,10 +257,11 @@ def read_protobuf_type(
243
257
  enum = EnumDefinitionCompiler(
244
258
  source_file=source_file,
245
259
  output_file=output_package,
260
+ prefixed_proto_name=prefixed_proto_name,
246
261
  proto_obj=item,
247
262
  path=path,
248
263
  )
249
- output_package.enums[enum.proto_name] = enum
264
+ output_package.enums[enum.prefixed_proto_name] = enum
250
265
 
251
266
 
252
267
  def read_protobuf_service(
@@ -68,6 +68,7 @@ class ServerGeneration(StrEnum):
68
68
  @dataclass
69
69
  class Settings:
70
70
  pydantic_dataclasses: bool
71
+ google_protobuf_descriptors: bool
71
72
 
72
73
  client_generation: ClientGeneration
73
74
  server_generation: ServerGeneration
@@ -6,15 +6,8 @@
6
6
  # This file has been @generated
7
7
 
8
8
  __all__ = (
9
- {% for _, enum in output_file.enums|dictsort(by="key") %}
10
- "{{ enum.py_name }}",
11
- {%- endfor -%}
12
- {% for _, message in output_file.messages|dictsort(by="key") %}
13
- "{{ message.py_name }}",
14
- {%- endfor -%}
15
- {% for _, service in output_file.services|dictsort(by="key") %}
16
- "{{ service.py_name }}Stub",
17
- "{{ service.py_name }}Base",
9
+ {%- for name in all -%}
10
+ "{{ name }}",
18
11
  {%- endfor -%}
19
12
  )
20
13
 
@@ -28,6 +21,7 @@ import typing
28
21
  from typing import TYPE_CHECKING
29
22
 
30
23
  {% if output_file.settings.pydantic_dataclasses %}
24
+ import pydantic
31
25
  from pydantic.dataclasses import dataclass
32
26
  from pydantic import model_validator
33
27
  {%- else -%}
@@ -38,12 +32,19 @@ import betterproto2
38
32
  from betterproto2.grpc.grpclib_server import ServiceBase
39
33
  import grpc
40
34
  import grpclib
35
+ from google.protobuf.descriptor import Descriptor, EnumDescriptor
41
36
 
42
37
  {# Import the message pool of the generated code. #}
43
38
  {% if output_file.package %}
44
39
  from {{ "." * output_file.package.count(".") }}..message_pool import default_message_pool
40
+ {% if output_file.settings.google_protobuf_descriptors %}
41
+ from {{ "." * output_file.package.count(".") }}..google_proto_descriptor_pool import default_google_proto_descriptor_pool
42
+ {% endif %}
45
43
  {% else %}
46
44
  from .message_pool import default_message_pool
45
+ {% if output_file.settings.google_protobuf_descriptors %}
46
+ from .google_proto_descriptor_pool import default_google_proto_descriptor_pool
47
+ {% endif %}
47
48
  {% endif %}
48
49
 
49
50
  if TYPE_CHECKING:
@@ -51,4 +52,5 @@ if TYPE_CHECKING:
51
52
  from betterproto2.grpc.grpclib_client import MetadataLike
52
53
  from grpclib.metadata import Deadline
53
54
 
54
- betterproto2.check_compiler_version("{{ version }}")
55
+ _COMPILER_VERSION="{{ version }}"
56
+ betterproto2.check_compiler_version(_COMPILER_VERSION)
@@ -1,4 +1,4 @@
1
- class {% block class_name %}{% endblock %}({% block inherit_from %}{% endblock %}):
1
+ class {% filter add_to_all %}{% block class_name %}{% endblock %}{% endfilter %}({% block inherit_from %}{% endblock %}):
2
2
  {% block service_docstring scoped %}
3
3
  {% if service.comment %}
4
4
  """
@@ -1,11 +1,18 @@
1
1
  {% for _, enum in output_file.enums|dictsort(by="key") %}
2
- class {{ enum.py_name }}(betterproto2.Enum):
2
+ class {{ enum.py_name | add_to_all }}(betterproto2.Enum):
3
3
  {% if enum.comment %}
4
4
  """
5
5
  {{ enum.comment | indent(4) }}
6
6
  """
7
7
  {% endif %}
8
8
 
9
+ {% if output_file.settings.google_protobuf_descriptors %}
10
+ {# Add descriptor class property to be more drop-in compatible with other libraries. #}
11
+ @betterproto2.classproperty
12
+ def DESCRIPTOR(self) -> EnumDescriptor:
13
+ return {{ enum.descriptor_name }}.enum_types_by_name['{{ enum.prefixed_proto_name }}']
14
+ {% endif %}
15
+
9
16
  {% for entry in enum.entries %}
10
17
  {{ entry.name }} = {{ entry.value }}
11
18
  {% if entry.comment %}
@@ -31,7 +38,7 @@ class {{ enum.py_name }}(betterproto2.Enum):
31
38
  {% else %}
32
39
  @dataclass(eq=False, repr=False)
33
40
  {% endif %}
34
- class {{ message.py_name }}(betterproto2.Message):
41
+ class {{ message.py_name | add_to_all }}(betterproto2.Message):
35
42
  {% if message.comment or message.oneofs %}
36
43
  """
37
44
  {{ message.comment | indent(4) }}
@@ -45,6 +52,13 @@ class {{ message.py_name }}(betterproto2.Message):
45
52
  """
46
53
  {% endif %}
47
54
 
55
+ {% if output_file.settings.google_protobuf_descriptors %}
56
+ {# Add descriptor class property to be more drop-in compatible with other libraries. #}
57
+ @betterproto2.classproperty
58
+ def DESCRIPTOR(self) -> Descriptor:
59
+ return {{ message.descriptor_name }}.message_types_by_name['{{ message.prefixed_proto_name }}']
60
+ {% endif %}
61
+
48
62
  {% for field in message.fields %}
49
63
  {{ field.get_field_string() }}
50
64
  {% if field.comment %}
@@ -81,7 +95,7 @@ class {{ message.py_name }}(betterproto2.Message):
81
95
  {{ method_source }}
82
96
  {% endfor %}
83
97
 
84
- default_message_pool.register_message("{{ output_file.package }}", "{{ message.proto_name }}", {{ message.py_name }})
98
+ default_message_pool.register_message("{{ output_file.package }}", "{{ message.prefixed_proto_name }}", {{ message.py_name }})
85
99
 
86
100
 
87
101
  {% endfor %}
@@ -102,9 +116,14 @@ default_message_pool.register_message("{{ output_file.package }}", "{{ message.p
102
116
  {{ i }}
103
117
  {% endfor %}
104
118
 
119
+ {% if output_file.settings.google_protobuf_descriptors %}
120
+ {# Add descriptors to Google protobuf's default pool to be more drop-in compatible with other libraries. #}
121
+ {{ output_file.descriptors }}
122
+ {% endif %}
123
+
105
124
  {% if output_file.settings.server_generation == "async" %}
106
125
  {% for _, service in output_file.services|dictsort(by="key") %}
107
- class {{ service.py_name }}Base(ServiceBase):
126
+ class {{ (service.py_name + "Base") | add_to_all }}(ServiceBase):
108
127
  {% if service.comment %}
109
128
  """
110
129
  {{ service.comment | indent(4) }}
@@ -127,6 +146,10 @@ class {{ service.py_name }}Base(ServiceBase):
127
146
  {% endif %}
128
147
 
129
148
  raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
149
+ {% if method.server_streaming %}
150
+ {# yielding here changes the return type from a coroutine to an async_generator #}
151
+ yield {{ method.py_output_message_type }}()
152
+ {% endif %}
130
153
 
131
154
  {% endfor %}
132
155
 
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: betterproto2_compiler
3
+ Version: 0.7.0
4
+ Summary: Compiler for betterproto2
5
+ Project-URL: Documentation, https://betterproto.github.io/python-betterproto2/
6
+ Project-URL: Repository, https://github.com/betterproto/python-betterproto2
7
+ Author-email: Adrien Vannson <adrien.vannson@protonmail.com>, "Daniel G. Taylor" <danielgtaylor@gmail.com>
8
+ License-Expression: MIT
9
+ Keywords: compiler,gRPC,protobuf
10
+ Requires-Python: <4.0,>=3.10
11
+ Requires-Dist: betterproto2[grpclib]<0.8,>=0.7.0
12
+ Requires-Dist: jinja2>=3.0.3
13
+ Requires-Dist: ruff~=0.9.3
14
+ Requires-Dist: strenum<0.5,>=0.4.15; python_version == '3.10'
15
+ Requires-Dist: typing-extensions<5,>=4.7.1
@@ -1,7 +1,9 @@
1
1
  betterproto2_compiler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  betterproto2_compiler/casing.py,sha256=HSXLXAOqZzEnu-tC1SZjpW0LIjzdPqUNJEwy1BHzfgg,3056
3
+ betterproto2_compiler/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ betterproto2_compiler/settings.py,sha256=Y867wBQad2EunzlsZEd0OjPaXGwzqcBmPKYMG9Q_2Dw,2014
3
5
  betterproto2_compiler/compile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- betterproto2_compiler/compile/importing.py,sha256=cJZ1BqjSHaDwXlfseH7f73pMnBZTiqLyOQfQfyganVM,6256
6
+ betterproto2_compiler/compile/importing.py,sha256=yfIJ7oEXR1Ghe5meGzOE0ORf1Sr7Xa2Pr_slOw-rT3s,7137
5
7
  betterproto2_compiler/compile/naming.py,sha256=zf0VOmNojzyv33upOGelGxjZTEDE8JULEEED5_3inHg,562
6
8
  betterproto2_compiler/known_types/__init__.py,sha256=nrWckuv4hGhL8-tW7V5TD5qXs1Sa5vC7zMusGnz7jsE,3485
7
9
  betterproto2_compiler/known_types/any.py,sha256=E3OoAoUU9xrGHmYEvF0YnrwQdTUuY4h54XbKU0eGxQ8,1897
@@ -9,27 +11,24 @@ betterproto2_compiler/known_types/duration.py,sha256=M-qsFeiHsw5Z_AoSata1ZUjfkho
9
11
  betterproto2_compiler/known_types/google_values.py,sha256=7JoPXVs6cVg_ihIWlWIDElSSuW0BymRnPHerz1bFuH4,6688
10
12
  betterproto2_compiler/known_types/timestamp.py,sha256=y1sNWG2Q0FWv6nIte1UTifFVCsryp7T8foXZqp4qhQQ,3409
11
13
  betterproto2_compiler/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- betterproto2_compiler/lib/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- betterproto2_compiler/lib/google/protobuf/__init__.py,sha256=yC7k_A1XA9-CPzhIaJfWJSIBpqyaLx7IVVuvXrqD-iQ,102114
14
- betterproto2_compiler/lib/google/protobuf/compiler/__init__.py,sha256=IriT5naeEkcxA-R2EpzOGBMLVGgVO6CXqvrR8HVaR28,9600
15
14
  betterproto2_compiler/lib/message_pool.py,sha256=4-cRhhiM6bmfpUJZ8qxc8LEyqHBHpLCcotjbyZxl7JM,71
15
+ betterproto2_compiler/lib/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ betterproto2_compiler/lib/google/protobuf/__init__.py,sha256=r5R69Q9iYg8FZX6J6pCY6xnOTxgl_-zz2Xwn4uGGPyw,102114
17
+ betterproto2_compiler/lib/google/protobuf/compiler/__init__.py,sha256=SaJT2xsLVFPvTd12R2V2wjEWeP4JewKMPJlIKeyZ2Mk,9600
16
18
  betterproto2_compiler/plugin/__init__.py,sha256=L3pW0b4CvkM5x53x_sYt1kYiSFPO0_vaeH6EQPq9FAM,43
17
19
  betterproto2_compiler/plugin/__main__.py,sha256=vBQ82334kX06ImDbFlPFgiBRiLIinwNk3z8Khs6hd74,31
18
- betterproto2_compiler/plugin/compiler.py,sha256=3sPbCtdzjAGftVvoGe5dvxE8uTzJ78hABA-_f-1rEUo,2471
20
+ betterproto2_compiler/plugin/compiler.py,sha256=9jZcNlwxWLUQlZyCLKG33P2xCoJgqaIQHIgcZM40JGY,2730
19
21
  betterproto2_compiler/plugin/main.py,sha256=b1jDEdG1Iau-4cPq89uSjU0SHwC278SxqwiuFwIF8fA,1288
20
- betterproto2_compiler/plugin/models.py,sha256=rzCKsdoV-0QCwmRN82x2QZ6029W9EAAJCFbLGMkVoz8,22215
22
+ betterproto2_compiler/plugin/models.py,sha256=gZ0nIvZbmHJ6OKyQ0gEsbZ1DRicABIOLMge1nyFkOCY,25212
21
23
  betterproto2_compiler/plugin/module_validation.py,sha256=JnP8dSN83eJJVDP_UPJsHzq7E7Md3lah0PnKXDbFW5Q,4808
22
- betterproto2_compiler/plugin/parser.py,sha256=XJd7n78E6Gnv04L1faGyTCOV3v0sv1eD5-AenNhPDMQ,10197
24
+ betterproto2_compiler/plugin/parser.py,sha256=GHVZGpC_lxvQRGgDaJTCS4ab9sUST7XAef2wj2UKqOg,10966
23
25
  betterproto2_compiler/plugin/plugin.bat,sha256=lfLT1WguAXqyerLLsRL6BfHA0RqUE6QG79v-1BYVSpI,48
24
- betterproto2_compiler/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- betterproto2_compiler/settings.py,sha256=ggLU8qYTTACwdNN94cSznQchu-fJSJxZz041FXRwq7Y,1976
26
- betterproto2_compiler/templates/header.py.j2,sha256=nTUJ-BioeTTCrEr2ZbxPPUl6iBqHOxXr_NAVOGa8jYg,1622
27
- betterproto2_compiler/templates/service_stub.py.j2,sha256=r0AefgNbDCh-iDgFNV7aNx8fNe5kQY-8TNew-T_tUXc,929
26
+ betterproto2_compiler/templates/header.py.j2,sha256=4C88YH5jtEzlBcUP054rk6lK5pQ1n5_TCzlH_3VrztY,1754
27
+ betterproto2_compiler/templates/service_stub.py.j2,sha256=2fhbty6uw57EyxOskGcNlZjIjGELMKWY--pvq5ZEjFw,967
28
28
  betterproto2_compiler/templates/service_stub_async.py.j2,sha256=JNOAa8FPhzYS5D0zi0DPESVEwAjkdFsVQZ008Qi4JmE,2968
29
29
  betterproto2_compiler/templates/service_stub_sync.py.j2,sha256=V7HJIQJEgivX8VEBt7Ju6cXG5FeeCY9QyYMy1kicElM,2642
30
- betterproto2_compiler/templates/template.py.j2,sha256=kP1TMTK7BCv6sEFTGXPZs_jg39lC_IdvKTFWPHm8BgU,5415
31
- betterproto2_compiler-0.5.1.dist-info/LICENSE.md,sha256=Pgl2pReU-2yw2miGeQ55UFlyzqAZ_EpYVyZ2nWjwRv4,1121
32
- betterproto2_compiler-0.5.1.dist-info/METADATA,sha256=7wZ7cc4n5R4S9lnl2jBYY89Hib9WynJySv0zFk_3zjM,1225
33
- betterproto2_compiler-0.5.1.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
34
- betterproto2_compiler-0.5.1.dist-info/entry_points.txt,sha256=re3Qg8lLljbVobeeKH2f1FVQZ114wfZkGv3zCZTD8Ok,84
35
- betterproto2_compiler-0.5.1.dist-info/RECORD,,
30
+ betterproto2_compiler/templates/template.py.j2,sha256=1leU6V5AmlgPvKYCjBYYeVPAnfvcDrl9KQHgiuNiigo,6572
31
+ betterproto2_compiler-0.7.0.dist-info/METADATA,sha256=rHB3App3Zkmtp7OKB8vlesiZRaz7Ba7tdb5XzwSA5wk,658
32
+ betterproto2_compiler-0.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
33
+ betterproto2_compiler-0.7.0.dist-info/entry_points.txt,sha256=MXDaz7YfiaWx8KiSzArjUPLt6eTlMRbqzE4jCjXozuI,85
34
+ betterproto2_compiler-0.7.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.2
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ protoc-gen-python_betterproto2 = betterproto2_compiler.plugin:main
@@ -1,22 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 Daniel G. Taylor
4
- Copyright (c) 2024 The betterproto contributors
5
-
6
- Permission is hereby granted, free of charge, to any person obtaining a copy
7
- of this software and associated documentation files (the "Software"), to deal
8
- in the Software without restriction, including without limitation the rights
9
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- copies of the Software, and to permit persons to whom the Software is
11
- furnished to do so, subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- SOFTWARE.
@@ -1,36 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: betterproto2_compiler
3
- Version: 0.5.1
4
- Summary: Compiler for betterproto2
5
- License: MIT
6
- Keywords: protobuf,gRPC,compiler
7
- Author: Adrien Vannson
8
- Author-email: adrien.vannson@protonmail.com
9
- Requires-Python: >=3.10,<4.0
10
- Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Programming Language :: Python :: 3.12
15
- Classifier: Programming Language :: Python :: 3.13
16
- Requires-Dist: betterproto2[grpclib] (>=0.5.1,<0.6.0)
17
- Requires-Dist: jinja2 (>=3.0.3)
18
- Requires-Dist: ruff (>=0.9.3,<0.10.0)
19
- Requires-Dist: strenum (>=0.4.15,<0.5.0) ; python_version == "3.10"
20
- Requires-Dist: typing-extensions (>=4.7.1,<5.0.0)
21
- Project-URL: Documentation, https://betterproto.github.io/python-betterproto2-compiler/
22
- Project-URL: Repository, https://github.com/betterproto/python-betterproto2-compiler
23
- Description-Content-Type: text/markdown
24
-
25
- # Betterproto2 compiler
26
-
27
- ![](https://github.com/betterproto/python-betterproto2-compiler/actions/workflows/ci.yml/badge.svg)
28
-
29
-
30
-
31
- ## License
32
-
33
- Copyright © 2019 Daniel G. Taylor
34
-
35
- Copyright © 2024 The betterproto contributors
36
-
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- protoc-gen-python_betterproto2=betterproto2_compiler.plugin:main
3
-