betterproto2-compiler 0.0.3__py3-none-any.whl → 0.1.1__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.
Files changed (32) hide show
  1. betterproto2_compiler/compile/importing.py +11 -15
  2. betterproto2_compiler/lib/google/protobuf/__init__.py +0 -1
  3. betterproto2_compiler/lib/google/protobuf/compiler/__init__.py +229 -1
  4. betterproto2_compiler/plugin/compiler.py +5 -2
  5. betterproto2_compiler/plugin/models.py +83 -81
  6. betterproto2_compiler/plugin/module_validation.py +2 -7
  7. betterproto2_compiler/plugin/parser.py +30 -34
  8. betterproto2_compiler/plugin/typing_compiler.py +8 -12
  9. betterproto2_compiler/templates/header.py.j2 +2 -0
  10. betterproto2_compiler/templates/template.py.j2 +8 -1
  11. {betterproto2_compiler-0.0.3.dist-info → betterproto2_compiler-0.1.1.dist-info}/METADATA +3 -3
  12. betterproto2_compiler-0.1.1.dist-info/RECORD +26 -0
  13. {betterproto2_compiler-0.0.3.dist-info → betterproto2_compiler-0.1.1.dist-info}/WHEEL +1 -1
  14. betterproto2_compiler-0.1.1.dist-info/entry_points.txt +3 -0
  15. betterproto2_compiler/_types.py +0 -13
  16. betterproto2_compiler/enum.py +0 -180
  17. betterproto2_compiler/grpc/__init__.py +0 -0
  18. betterproto2_compiler/grpc/grpclib_client.py +0 -172
  19. betterproto2_compiler/grpc/grpclib_server.py +0 -32
  20. betterproto2_compiler/grpc/util/__init__.py +0 -0
  21. betterproto2_compiler/grpc/util/async_channel.py +0 -190
  22. betterproto2_compiler/lib/pydantic/__init__.py +0 -0
  23. betterproto2_compiler/lib/pydantic/google/__init__.py +0 -0
  24. betterproto2_compiler/lib/pydantic/google/protobuf/__init__.py +0 -2690
  25. betterproto2_compiler/lib/pydantic/google/protobuf/compiler/__init__.py +0 -209
  26. betterproto2_compiler/lib/std/__init__.py +0 -0
  27. betterproto2_compiler/lib/std/google/__init__.py +0 -0
  28. betterproto2_compiler/lib/std/google/protobuf/__init__.py +0 -2521
  29. betterproto2_compiler/lib/std/google/protobuf/compiler/__init__.py +0 -197
  30. betterproto2_compiler-0.0.3.dist-info/RECORD +0 -41
  31. betterproto2_compiler-0.0.3.dist-info/entry_points.txt +0 -3
  32. {betterproto2_compiler-0.0.3.dist-info → betterproto2_compiler-0.1.1.dist-info}/LICENSE.md +0 -0
@@ -31,36 +31,32 @@ reference to `A` to `B`'s `fields` attribute.
31
31
 
32
32
  import builtins
33
33
  import re
34
+ from collections.abc import Iterable, Iterator
34
35
  from dataclasses import (
35
36
  dataclass,
36
37
  field,
37
38
  )
38
39
  from typing import (
39
- Dict,
40
- Iterable,
41
- Iterator,
42
- List,
43
- Optional,
44
- Set,
45
- Type,
46
40
  Union,
47
41
  )
48
42
 
49
43
  import betterproto2
50
-
51
- from betterproto2_compiler.compile.naming import (
52
- pythonize_class_name,
53
- pythonize_field_name,
54
- pythonize_method_name,
55
- )
56
- from betterproto2_compiler.lib.google.protobuf import (
44
+ from betterproto2.lib.google.protobuf import (
57
45
  DescriptorProto,
58
46
  EnumDescriptorProto,
59
47
  FieldDescriptorProto,
60
48
  FieldDescriptorProtoLabel,
61
49
  FieldDescriptorProtoType,
50
+ FieldDescriptorProtoType as FieldType,
62
51
  FileDescriptorProto,
63
52
  MethodDescriptorProto,
53
+ OneofDescriptorProto,
54
+ )
55
+
56
+ from betterproto2_compiler.compile.naming import (
57
+ pythonize_class_name,
58
+ pythonize_field_name,
59
+ pythonize_method_name,
64
60
  )
65
61
  from betterproto2_compiler.lib.google.protobuf.compiler import CodeGeneratorRequest
66
62
 
@@ -122,31 +118,9 @@ PROTO_PACKED_TYPES = (
122
118
  )
123
119
 
124
120
 
125
- # TODO patch again to make field optional
126
- # def monkey_patch_oneof_index():
127
- # """
128
- # The compiler message types are written for proto2, but we read them as proto3.
129
- # For this to work in the case of the oneof_index fields, which depend on being able
130
- # to tell whether they were set, we have to treat them as oneof fields. This method
131
- # monkey patches the generated classes after the fact to force this behaviour.
132
- # """
133
- # object.__setattr__(
134
- # FieldDescriptorProto.__dataclass_fields__["oneof_index"].metadata[
135
- # "betterproto"
136
- # ],
137
- # "group",
138
- # "oneof_index",
139
- # )
140
- # object.__setattr__(
141
- # Field.__dataclass_fields__["oneof_index"].metadata["betterproto"],
142
- # "group",
143
- # "oneof_index",
144
- # )
145
-
146
-
147
121
  def get_comment(
148
122
  proto_file: "FileDescriptorProto",
149
- path: List[int],
123
+ path: list[int],
150
124
  ) -> str:
151
125
  for sci_loc in proto_file.source_code_info.location:
152
126
  if list(sci_loc.path) == path:
@@ -182,10 +156,10 @@ class ProtoContentBase:
182
156
 
183
157
  source_file: FileDescriptorProto
184
158
  typing_compiler: TypingCompiler
185
- path: List[int]
159
+ path: list[int]
186
160
  parent: Union["betterproto2.Message", "OutputTemplate"]
187
161
 
188
- __dataclass_fields__: Dict[str, object]
162
+ __dataclass_fields__: dict[str, object]
189
163
 
190
164
  def __post_init__(self) -> None:
191
165
  """Checks that no fake default fields were left as placeholders."""
@@ -225,10 +199,10 @@ class ProtoContentBase:
225
199
  @dataclass
226
200
  class PluginRequestCompiler:
227
201
  plugin_request_obj: CodeGeneratorRequest
228
- output_packages: Dict[str, "OutputTemplate"] = field(default_factory=dict)
202
+ output_packages: dict[str, "OutputTemplate"] = field(default_factory=dict)
229
203
 
230
204
  @property
231
- def all_messages(self) -> List["MessageCompiler"]:
205
+ def all_messages(self) -> list["MessageCompiler"]:
232
206
  """All of the messages in this request.
233
207
 
234
208
  Returns
@@ -250,11 +224,11 @@ class OutputTemplate:
250
224
 
251
225
  parent_request: PluginRequestCompiler
252
226
  package_proto_obj: FileDescriptorProto
253
- input_files: List[str] = field(default_factory=list)
254
- imports_end: Set[str] = field(default_factory=set)
255
- messages: Dict[str, "MessageCompiler"] = field(default_factory=dict)
256
- enums: Dict[str, "EnumDefinitionCompiler"] = field(default_factory=dict)
257
- services: Dict[str, "ServiceCompiler"] = field(default_factory=dict)
227
+ input_files: list[str] = field(default_factory=list)
228
+ imports_end: set[str] = field(default_factory=set)
229
+ messages: dict[str, "MessageCompiler"] = field(default_factory=dict)
230
+ enums: dict[str, "EnumDefinitionCompiler"] = field(default_factory=dict)
231
+ services: dict[str, "ServiceCompiler"] = field(default_factory=dict)
258
232
  pydantic_dataclasses: bool = False
259
233
  output: bool = True
260
234
  typing_compiler: TypingCompiler = field(default_factory=DirectImportTypingCompiler)
@@ -290,9 +264,10 @@ class MessageCompiler(ProtoContentBase):
290
264
  typing_compiler: TypingCompiler
291
265
  parent: Union["MessageCompiler", OutputTemplate] = PLACEHOLDER
292
266
  proto_obj: DescriptorProto = PLACEHOLDER
293
- path: List[int] = PLACEHOLDER
294
- fields: List[Union["FieldCompiler", "MessageCompiler"]] = field(default_factory=list)
295
- builtins_types: Set[str] = field(default_factory=set)
267
+ path: list[int] = PLACEHOLDER
268
+ fields: list[Union["FieldCompiler", "MessageCompiler"]] = field(default_factory=list)
269
+ oneofs: list["OneofCompiler"] = field(default_factory=list)
270
+ builtins_types: set[str] = field(default_factory=set)
296
271
 
297
272
  def __post_init__(self) -> None:
298
273
  # Add message to output file
@@ -328,11 +303,9 @@ class MessageCompiler(ProtoContentBase):
328
303
  @property
329
304
  def has_message_field(self) -> bool:
330
305
  return any(
331
- (
332
- field.proto_obj.type in PROTO_MESSAGE_TYPES
333
- for field in self.fields
334
- if isinstance(field.proto_obj, FieldDescriptorProto)
335
- )
306
+ field.proto_obj.type in PROTO_MESSAGE_TYPES
307
+ for field in self.fields
308
+ if isinstance(field.proto_obj, FieldDescriptorProto)
336
309
  )
337
310
 
338
311
 
@@ -347,7 +320,7 @@ def is_map(proto_field_obj: FieldDescriptorProto, parent_message: DescriptorProt
347
320
  map_entry = f"{proto_field_obj.name.replace('_', '').lower()}entry"
348
321
  if message_type == map_entry:
349
322
  for nested in parent_message.nested_type: # parent message
350
- if nested.name.replace("_", "").lower() == map_entry and nested.options.map_entry:
323
+ if nested.name.replace("_", "").lower() == map_entry and nested.options and nested.options.map_entry:
351
324
  return True
352
325
  return False
353
326
 
@@ -374,8 +347,8 @@ def is_oneof(proto_field_obj: FieldDescriptorProto) -> bool:
374
347
  class FieldCompiler(ProtoContentBase):
375
348
  source_file: FileDescriptorProto
376
349
  typing_compiler: TypingCompiler
377
- path: List[int] = PLACEHOLDER
378
- builtins_types: Set[str] = field(default_factory=set)
350
+ path: list[int] = PLACEHOLDER
351
+ builtins_types: set[str] = field(default_factory=set)
379
352
 
380
353
  parent: MessageCompiler = PLACEHOLDER
381
354
  proto_obj: FieldDescriptorProto = PLACEHOLDER
@@ -390,13 +363,16 @@ class FieldCompiler(ProtoContentBase):
390
363
  """Construct string representation of this field as a field."""
391
364
  name = f"{self.py_name}"
392
365
  field_args = ", ".join(([""] + self.betterproto_field_args) if self.betterproto_field_args else [])
393
- betterproto_field_type = f"betterproto2.{self.field_type}_field({self.proto_obj.number}{field_args})"
366
+
367
+ betterproto_field_type = (
368
+ f"betterproto2.field({self.proto_obj.number}, betterproto2.{str(self.field_type)}{field_args})"
369
+ )
394
370
  if self.py_name in dir(builtins):
395
371
  self.parent.builtins_types.add(self.py_name)
396
372
  return f'{name}: "{self.annotation}" = {betterproto_field_type}'
397
373
 
398
374
  @property
399
- def betterproto_field_args(self) -> List[str]:
375
+ def betterproto_field_args(self) -> list[str]:
400
376
  args = []
401
377
  if self.field_wraps:
402
378
  args.append(f"wraps={self.field_wraps}")
@@ -404,9 +380,9 @@ class FieldCompiler(ProtoContentBase):
404
380
  args.append("optional=True")
405
381
  if self.repeated:
406
382
  args.append("repeated=True")
407
- if self.field_type == "enum":
383
+ if self.field_type == FieldType.TYPE_ENUM:
408
384
  t = self.py_type
409
- args.append(f"enum_default_value=lambda: {t}.try_value(0)")
385
+ args.append(f"default_factory=lambda: {t}.try_value(0)")
410
386
  return args
411
387
 
412
388
  @property
@@ -416,7 +392,7 @@ class FieldCompiler(ProtoContentBase):
416
392
  )
417
393
 
418
394
  @property
419
- def field_wraps(self) -> Optional[str]:
395
+ def field_wraps(self) -> str | None:
420
396
  """Returns betterproto wrapped field type or None."""
421
397
  match_wrapper = re.match(r"\.google\.protobuf\.(.+)Value$", self.proto_obj.type_name)
422
398
  if match_wrapper:
@@ -428,17 +404,19 @@ class FieldCompiler(ProtoContentBase):
428
404
  @property
429
405
  def repeated(self) -> bool:
430
406
  return self.proto_obj.label == FieldDescriptorProtoLabel.LABEL_REPEATED and not is_map(
431
- self.proto_obj, self.parent
407
+ self.proto_obj,
408
+ self.parent,
432
409
  )
433
410
 
434
411
  @property
435
412
  def optional(self) -> bool:
436
- return self.proto_obj.proto3_optional or (self.field_type == "message" and not self.repeated)
413
+ # TODO not for maps
414
+ return self.proto_obj.proto3_optional or (self.field_type == FieldType.TYPE_MESSAGE and not self.repeated)
437
415
 
438
416
  @property
439
- def field_type(self) -> str:
440
- """String representation of proto field type."""
441
- return FieldDescriptorProtoType(self.proto_obj.type).name.lower().replace("type_", "")
417
+ def field_type(self) -> FieldType:
418
+ # TODO it should be possible to remove constructor
419
+ return FieldType(self.proto_obj.type)
442
420
 
443
421
  @property
444
422
  def packed(self) -> bool:
@@ -500,7 +478,7 @@ class OneOfFieldCompiler(FieldCompiler):
500
478
  return True
501
479
 
502
480
  @property
503
- def betterproto_field_args(self) -> List[str]:
481
+ def betterproto_field_args(self) -> list[str]:
504
482
  args = super().betterproto_field_args
505
483
  group = self.parent.proto_obj.oneof_decl[self.proto_obj.oneof_index].name
506
484
  args.append(f'group="{group}"')
@@ -509,8 +487,8 @@ class OneOfFieldCompiler(FieldCompiler):
509
487
 
510
488
  @dataclass
511
489
  class MapEntryCompiler(FieldCompiler):
512
- py_k_type: Optional[Type] = None
513
- py_v_type: Optional[Type] = None
490
+ py_k_type: type | None = None
491
+ py_v_type: type | None = None
514
492
  proto_k_type: str = ""
515
493
  proto_v_type: str = ""
516
494
 
@@ -547,13 +525,17 @@ class MapEntryCompiler(FieldCompiler):
547
525
 
548
526
  raise ValueError("can't find enum")
549
527
 
550
- @property
551
- def betterproto_field_args(self) -> List[str]:
552
- return [f"betterproto2.{self.proto_k_type}", f"betterproto2.{self.proto_v_type}"]
553
-
554
- @property
555
- def field_type(self) -> str:
556
- return "map"
528
+ def get_field_string(self) -> str:
529
+ """Construct string representation of this field as a field."""
530
+ betterproto_field_type = (
531
+ f"betterproto2.field({self.proto_obj.number}, "
532
+ "betterproto2.TYPE_MAP, "
533
+ f"map_types=(betterproto2.{self.proto_k_type}, "
534
+ f"betterproto2.{self.proto_v_type}))"
535
+ )
536
+ if self.py_name in dir(builtins):
537
+ self.parent.builtins_types.add(self.py_name)
538
+ return f'{self.py_name}: "{self.annotation}" = {betterproto_field_type}'
557
539
 
558
540
  @property
559
541
  def annotation(self) -> str:
@@ -564,12 +546,32 @@ class MapEntryCompiler(FieldCompiler):
564
546
  return False # maps cannot be repeated
565
547
 
566
548
 
549
+ @dataclass
550
+ class OneofCompiler(ProtoContentBase):
551
+ source_file: FileDescriptorProto
552
+ typing_compiler: TypingCompiler
553
+ path: list[int] = PLACEHOLDER
554
+
555
+ parent: MessageCompiler = PLACEHOLDER
556
+ proto_obj: OneofDescriptorProto = PLACEHOLDER
557
+
558
+ def __post_init__(self) -> None:
559
+ # Add oneof to message
560
+ if isinstance(self.parent, MessageCompiler): # TODO why?
561
+ self.parent.oneofs.append(self)
562
+ super().__post_init__()
563
+
564
+ @property
565
+ def name(self) -> str:
566
+ return self.proto_obj.name
567
+
568
+
567
569
  @dataclass
568
570
  class EnumDefinitionCompiler(MessageCompiler):
569
571
  """Representation of a proto Enum definition."""
570
572
 
571
573
  proto_obj: EnumDescriptorProto = PLACEHOLDER
572
- entries: List["EnumDefinitionCompiler.EnumEntry"] = PLACEHOLDER
574
+ entries: list["EnumDefinitionCompiler.EnumEntry"] = PLACEHOLDER
573
575
 
574
576
  @dataclass(unsafe_hash=True)
575
577
  class EnumEntry:
@@ -597,8 +599,8 @@ class ServiceCompiler(ProtoContentBase):
597
599
  source_file: FileDescriptorProto
598
600
  parent: OutputTemplate = PLACEHOLDER
599
601
  proto_obj: DescriptorProto = PLACEHOLDER
600
- path: List[int] = PLACEHOLDER
601
- methods: List["ServiceMethodCompiler"] = field(default_factory=list)
602
+ path: list[int] = PLACEHOLDER
603
+ methods: list["ServiceMethodCompiler"] = field(default_factory=list)
602
604
 
603
605
  def __post_init__(self) -> None:
604
606
  # Add service to output file
@@ -619,7 +621,7 @@ class ServiceMethodCompiler(ProtoContentBase):
619
621
  source_file: FileDescriptorProto
620
622
  parent: ServiceCompiler
621
623
  proto_obj: MethodDescriptorProto
622
- path: List[int] = PLACEHOLDER
624
+ path: list[int] = PLACEHOLDER
623
625
 
624
626
  def __post_init__(self) -> None:
625
627
  # Add method to service
@@ -1,15 +1,10 @@
1
1
  import re
2
2
  from collections import defaultdict
3
+ from collections.abc import Iterator
3
4
  from dataclasses import (
4
5
  dataclass,
5
6
  field,
6
7
  )
7
- from typing import (
8
- Dict,
9
- Iterator,
10
- List,
11
- Tuple,
12
- )
13
8
 
14
9
 
15
10
  @dataclass
@@ -17,7 +12,7 @@ class ModuleValidator:
17
12
  line_iterator: Iterator[str]
18
13
  line_number: int = field(init=False, default=0)
19
14
 
20
- collisions: Dict[str, List[Tuple[int, str]]] = field(init=False, default_factory=lambda: defaultdict(list))
15
+ collisions: dict[str, list[tuple[int, str]]] = field(init=False, default_factory=lambda: defaultdict(list))
21
16
 
22
17
  def add_import(self, imp: str, number: int, full_line: str):
23
18
  """
@@ -1,20 +1,14 @@
1
1
  import pathlib
2
2
  import sys
3
- from typing import (
4
- Generator,
5
- List,
6
- Set,
7
- Tuple,
8
- Union,
9
- )
3
+ from collections.abc import Generator
10
4
 
11
- from betterproto2_compiler.lib.google.protobuf import (
5
+ from betterproto2.lib.google.protobuf import (
12
6
  DescriptorProto,
13
7
  EnumDescriptorProto,
14
- FieldDescriptorProto,
15
8
  FileDescriptorProto,
16
9
  ServiceDescriptorProto,
17
10
  )
11
+
18
12
  from betterproto2_compiler.lib.google.protobuf.compiler import (
19
13
  CodeGeneratorRequest,
20
14
  CodeGeneratorResponse,
@@ -28,6 +22,7 @@ from .models import (
28
22
  FieldCompiler,
29
23
  MapEntryCompiler,
30
24
  MessageCompiler,
25
+ OneofCompiler,
31
26
  OneOfFieldCompiler,
32
27
  OutputTemplate,
33
28
  PluginRequestCompiler,
@@ -45,13 +40,13 @@ from .typing_compiler import (
45
40
 
46
41
  def traverse(
47
42
  proto_file: FileDescriptorProto,
48
- ) -> Generator[Tuple[Union[EnumDescriptorProto, DescriptorProto], List[int]], None, None]:
43
+ ) -> Generator[tuple[EnumDescriptorProto | DescriptorProto, list[int]], None, None]:
49
44
  # Todo: Keep information about nested hierarchy
50
45
  def _traverse(
51
- path: List[int],
52
- items: Union[List[EnumDescriptorProto], List[DescriptorProto]],
46
+ path: list[int],
47
+ items: list[EnumDescriptorProto] | list[DescriptorProto],
53
48
  prefix: str = "",
54
- ) -> Generator[Tuple[Union[EnumDescriptorProto, DescriptorProto], List[int]], None, None]:
49
+ ) -> Generator[tuple[EnumDescriptorProto | DescriptorProto, list[int]], None, None]:
55
50
  for i, item in enumerate(items):
56
51
  # Adjust the name since we flatten the hierarchy.
57
52
  # Todo: don't change the name, but include full name in returned tuple
@@ -82,7 +77,8 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse:
82
77
  if output_package_name not in request_data.output_packages:
83
78
  # Create a new output if there is no output for this package
84
79
  request_data.output_packages[output_package_name] = OutputTemplate(
85
- parent_request=request_data, package_proto_obj=proto_file
80
+ parent_request=request_data,
81
+ package_proto_obj=proto_file,
86
82
  )
87
83
  # Add this input file to the output corresponding to this package
88
84
  request_data.output_packages[output_package_name].input_files.append(proto_file)
@@ -144,7 +140,7 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse:
144
140
  service.ready()
145
141
 
146
142
  # Generate output files
147
- output_paths: Set[pathlib.Path] = set()
143
+ output_paths: set[pathlib.Path] = set()
148
144
  for output_package_name, output_package in request_data.output_packages.items():
149
145
  if not output_package.output:
150
146
  continue
@@ -158,7 +154,7 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse:
158
154
  name=str(output_path),
159
155
  # Render and then format the output file
160
156
  content=outputfile_compiler(output_file=output_package),
161
- )
157
+ ),
162
158
  )
163
159
 
164
160
  # Make each output directory a package with __init__ file
@@ -178,25 +174,9 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse:
178
174
  return response
179
175
 
180
176
 
181
- def _make_one_of_field_compiler(
182
- output_package: OutputTemplate,
183
- source_file: "FileDescriptorProto",
184
- parent: MessageCompiler,
185
- proto_obj: "FieldDescriptorProto",
186
- path: List[int],
187
- ) -> FieldCompiler:
188
- return OneOfFieldCompiler(
189
- source_file=source_file,
190
- parent=parent,
191
- proto_obj=proto_obj,
192
- path=path,
193
- typing_compiler=output_package.typing_compiler,
194
- )
195
-
196
-
197
177
  def read_protobuf_type(
198
178
  item: DescriptorProto,
199
- path: List[int],
179
+ path: list[int],
200
180
  source_file: "FileDescriptorProto",
201
181
  output_package: OutputTemplate,
202
182
  ) -> None:
@@ -222,7 +202,13 @@ def read_protobuf_type(
222
202
  typing_compiler=output_package.typing_compiler,
223
203
  )
224
204
  elif is_oneof(field):
225
- _make_one_of_field_compiler(output_package, source_file, message_data, field, path + [2, index])
205
+ OneOfFieldCompiler(
206
+ source_file=source_file,
207
+ parent=message_data,
208
+ proto_obj=field,
209
+ path=path + [2, index],
210
+ typing_compiler=output_package.typing_compiler,
211
+ )
226
212
  else:
227
213
  FieldCompiler(
228
214
  source_file=source_file,
@@ -231,6 +217,16 @@ def read_protobuf_type(
231
217
  path=path + [2, index],
232
218
  typing_compiler=output_package.typing_compiler,
233
219
  )
220
+
221
+ for index, oneof in enumerate(item.oneof_decl):
222
+ OneofCompiler(
223
+ source_file=source_file,
224
+ typing_compiler=output_package.typing_compiler,
225
+ path=path + [8, index],
226
+ parent=message_data,
227
+ proto_obj=oneof,
228
+ )
229
+
234
230
  elif isinstance(item, EnumDescriptorProto):
235
231
  # Enum
236
232
  EnumDefinitionCompiler(
@@ -1,15 +1,11 @@
1
1
  import abc
2
+ import builtins
2
3
  from collections import defaultdict
4
+ from collections.abc import Iterator
3
5
  from dataclasses import (
4
6
  dataclass,
5
7
  field,
6
8
  )
7
- from typing import (
8
- Dict,
9
- Iterator,
10
- Optional,
11
- Set,
12
- )
13
9
 
14
10
 
15
11
  class TypingCompiler(metaclass=abc.ABCMeta):
@@ -42,7 +38,7 @@ class TypingCompiler(metaclass=abc.ABCMeta):
42
38
  raise NotImplementedError
43
39
 
44
40
  @abc.abstractmethod
45
- def imports(self) -> Dict[str, Optional[Set[str]]]:
41
+ def imports(self) -> builtins.dict[str, set[str] | None]:
46
42
  """
47
43
  Returns either the direct import as a key with none as value, or a set of
48
44
  values to import from the key.
@@ -63,7 +59,7 @@ class TypingCompiler(metaclass=abc.ABCMeta):
63
59
 
64
60
  @dataclass
65
61
  class DirectImportTypingCompiler(TypingCompiler):
66
- _imports: Dict[str, Set[str]] = field(default_factory=lambda: defaultdict(set))
62
+ _imports: dict[str, set[str]] = field(default_factory=lambda: defaultdict(set))
67
63
 
68
64
  def optional(self, type_: str) -> str:
69
65
  self._imports["typing"].add("Optional")
@@ -93,7 +89,7 @@ class DirectImportTypingCompiler(TypingCompiler):
93
89
  self._imports["typing"].add("AsyncIterator")
94
90
  return f"AsyncIterator[{type_}]"
95
91
 
96
- def imports(self) -> Dict[str, Optional[Set[str]]]:
92
+ def imports(self) -> builtins.dict[str, set[str] | None]:
97
93
  return {k: v if v else None for k, v in self._imports.items()}
98
94
 
99
95
 
@@ -129,7 +125,7 @@ class TypingImportTypingCompiler(TypingCompiler):
129
125
  self._imported = True
130
126
  return f"typing.AsyncIterator[{type_}]"
131
127
 
132
- def imports(self) -> Dict[str, Optional[Set[str]]]:
128
+ def imports(self) -> builtins.dict[str, set[str] | None]:
133
129
  if self._imported:
134
130
  return {"typing": None}
135
131
  return {}
@@ -137,7 +133,7 @@ class TypingImportTypingCompiler(TypingCompiler):
137
133
 
138
134
  @dataclass
139
135
  class NoTyping310TypingCompiler(TypingCompiler):
140
- _imports: Dict[str, Set[str]] = field(default_factory=lambda: defaultdict(set))
136
+ _imports: dict[str, set[str]] = field(default_factory=lambda: defaultdict(set))
141
137
 
142
138
  def optional(self, type_: str) -> str:
143
139
  return f"{type_} | None"
@@ -163,5 +159,5 @@ class NoTyping310TypingCompiler(TypingCompiler):
163
159
  self._imports["collections.abc"].add("AsyncIterator")
164
160
  return f"AsyncIterator[{type_}]"
165
161
 
166
- def imports(self) -> Dict[str, Optional[Set[str]]]:
162
+ def imports(self) -> builtins.dict[str, set[str] | None]:
167
163
  return {k: v if v else None for k, v in self._imports.items()}
@@ -48,3 +48,5 @@ if TYPE_CHECKING:
48
48
  import grpclib.server
49
49
  from betterproto2.grpc.grpclib_client import MetadataLike
50
50
  from grpclib.metadata import Deadline
51
+
52
+ betterproto2.check_compiler_version("{{ version }}")
@@ -32,9 +32,16 @@ class {{ enum.py_name }}(betterproto2.Enum):
32
32
  @dataclass(eq=False, repr=False)
33
33
  {% endif %}
34
34
  class {{ message.py_name }}(betterproto2.Message):
35
- {% if message.comment %}
35
+ {% if message.comment or message.oneofs %}
36
36
  """
37
37
  {{ message.comment | indent(4) }}
38
+
39
+ {% if message.oneofs %}
40
+ Oneofs:
41
+ {% for oneof in message.oneofs %}
42
+ - {{ oneof.name }}: {{ oneof.comment | indent(12) }}
43
+ {% endfor %}
44
+ {% endif %}
38
45
  """
39
46
  {% endif %}
40
47
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: betterproto2_compiler
3
- Version: 0.0.3
3
+ Version: 0.1.1
4
4
  Summary: Compiler for betterproto2
5
5
  Home-page: https://github.com/betterproto/python-betterproto2-compiler
6
6
  License: MIT
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Programming Language :: Python :: 3.13
17
- Requires-Dist: betterproto2 (==0.0.2)
17
+ Requires-Dist: betterproto2 (>=0.1.1,<0.2.0)
18
18
  Requires-Dist: grpclib (>=0.4.1,<0.5.0)
19
19
  Requires-Dist: jinja2 (>=3.0.3)
20
20
  Requires-Dist: ruff (>=0.7.4,<0.8.0)
@@ -0,0 +1,26 @@
1
+ betterproto2_compiler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ betterproto2_compiler/casing.py,sha256=bMdI4W0hfYh6kV-DQIqFEjSfGYEqUtPciAzP64z5HLQ,3587
3
+ betterproto2_compiler/compile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ betterproto2_compiler/compile/importing.py,sha256=JgvVFlWiA8zeb8L_pcAbxH61tDGNNNx18kVXRrfmIlo,7400
5
+ betterproto2_compiler/compile/naming.py,sha256=zf0VOmNojzyv33upOGelGxjZTEDE8JULEEED5_3inHg,562
6
+ betterproto2_compiler/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ betterproto2_compiler/lib/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ betterproto2_compiler/lib/google/protobuf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ betterproto2_compiler/lib/google/protobuf/compiler/__init__.py,sha256=CX9UOXUUZD8GpeS7CWb1JTJoPIKi2e6lH7BCw6RJ8uI,9285
10
+ betterproto2_compiler/plugin/__init__.py,sha256=L3pW0b4CvkM5x53x_sYt1kYiSFPO0_vaeH6EQPq9FAM,43
11
+ betterproto2_compiler/plugin/__main__.py,sha256=vBQ82334kX06ImDbFlPFgiBRiLIinwNk3z8Khs6hd74,31
12
+ betterproto2_compiler/plugin/compiler.py,sha256=jICLI4-5rAOkWQI1v5j7JqIvoao-ZM9szMuq0OBRteA,2138
13
+ betterproto2_compiler/plugin/main.py,sha256=Q9PmcJqXuYYFe51l7AqHVzJrHqi2LWCUu80CZSQOOwk,1469
14
+ betterproto2_compiler/plugin/models.py,sha256=RfHRt8Iw_v9q-Rpi58VNHkcDfED5TwHWur2O4wr1Z8Q,24482
15
+ betterproto2_compiler/plugin/module_validation.py,sha256=RdPFwdmkbD6NKADaHC5eaPix_pz-yGxHvYJj8Ev48fA,4822
16
+ betterproto2_compiler/plugin/parser.py,sha256=AZihuRp7XCTKG04YNJcthR9fmarllfkPSfLS0l9exLI,9668
17
+ betterproto2_compiler/plugin/plugin.bat,sha256=lfLT1WguAXqyerLLsRL6BfHA0RqUE6QG79v-1BYVSpI,48
18
+ betterproto2_compiler/plugin/typing_compiler.py,sha256=IK6m4ggHXK7HL98Ed_WjvQ_yeWfIpf_fIBZ9SA8UcyM,4873
19
+ betterproto2_compiler/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ betterproto2_compiler/templates/header.py.j2,sha256=nxqsengMcM_IRqQYNVntPQ0gUFUPCy_1P1mcoLvbDos,1464
21
+ betterproto2_compiler/templates/template.py.j2,sha256=R_9a5IvX2wU6yKzcdjj-uMb09askExrShu4UX2ayQO8,8911
22
+ betterproto2_compiler-0.1.1.dist-info/LICENSE.md,sha256=Pgl2pReU-2yw2miGeQ55UFlyzqAZ_EpYVyZ2nWjwRv4,1121
23
+ betterproto2_compiler-0.1.1.dist-info/METADATA,sha256=G9d9cnAsNG8x00x3wWxDDcWZdXOaZyaitkblivnE8lw,1170
24
+ betterproto2_compiler-0.1.1.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
25
+ betterproto2_compiler-0.1.1.dist-info/entry_points.txt,sha256=re3Qg8lLljbVobeeKH2f1FVQZ114wfZkGv3zCZTD8Ok,84
26
+ betterproto2_compiler-0.1.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.1
2
+ Generator: poetry-core 2.0.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ protoc-gen-python_betterproto2=betterproto2_compiler.plugin:main
3
+
@@ -1,13 +0,0 @@
1
- from typing import (
2
- TYPE_CHECKING,
3
- TypeVar,
4
- )
5
-
6
- if TYPE_CHECKING:
7
- from grpclib._typing import IProtoMessage
8
-
9
- from . import Message
10
-
11
- # Bound type variable to allow methods to return `self` of subclasses
12
- T = TypeVar("T", bound="Message")
13
- ST = TypeVar("ST", bound="IProtoMessage")