betterproto2-compiler 0.2.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.
Files changed (32) hide show
  1. betterproto2_compiler/__init__.py +0 -0
  2. betterproto2_compiler/casing.py +140 -0
  3. betterproto2_compiler/compile/__init__.py +0 -0
  4. betterproto2_compiler/compile/importing.py +180 -0
  5. betterproto2_compiler/compile/naming.py +21 -0
  6. betterproto2_compiler/known_types/__init__.py +14 -0
  7. betterproto2_compiler/known_types/any.py +36 -0
  8. betterproto2_compiler/known_types/duration.py +25 -0
  9. betterproto2_compiler/known_types/timestamp.py +45 -0
  10. betterproto2_compiler/lib/__init__.py +0 -0
  11. betterproto2_compiler/lib/google/__init__.py +0 -0
  12. betterproto2_compiler/lib/google/protobuf/__init__.py +3338 -0
  13. betterproto2_compiler/lib/google/protobuf/compiler/__init__.py +235 -0
  14. betterproto2_compiler/lib/message_pool.py +3 -0
  15. betterproto2_compiler/plugin/__init__.py +3 -0
  16. betterproto2_compiler/plugin/__main__.py +3 -0
  17. betterproto2_compiler/plugin/compiler.py +70 -0
  18. betterproto2_compiler/plugin/main.py +47 -0
  19. betterproto2_compiler/plugin/models.py +643 -0
  20. betterproto2_compiler/plugin/module_validation.py +156 -0
  21. betterproto2_compiler/plugin/parser.py +272 -0
  22. betterproto2_compiler/plugin/plugin.bat +2 -0
  23. betterproto2_compiler/plugin/typing_compiler.py +163 -0
  24. betterproto2_compiler/py.typed +0 -0
  25. betterproto2_compiler/settings.py +9 -0
  26. betterproto2_compiler/templates/header.py.j2 +59 -0
  27. betterproto2_compiler/templates/template.py.j2 +258 -0
  28. betterproto2_compiler-0.2.0.dist-info/LICENSE.md +22 -0
  29. betterproto2_compiler-0.2.0.dist-info/METADATA +35 -0
  30. betterproto2_compiler-0.2.0.dist-info/RECORD +32 -0
  31. betterproto2_compiler-0.2.0.dist-info/WHEEL +4 -0
  32. betterproto2_compiler-0.2.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,643 @@
1
+ """Plugin model dataclasses.
2
+
3
+ These classes are meant to be an intermediate representation
4
+ of protobuf objects. They are used to organize the data collected during parsing.
5
+
6
+ The general intention is to create a doubly-linked tree-like structure
7
+ with the following types of references:
8
+ - Downwards references: from message -> fields, from output package -> messages
9
+ or from service -> service methods
10
+ - Upwards references: from field -> message, message -> package.
11
+ - Input/output message references: from a service method to it's corresponding
12
+ input/output messages, which may even be in another package.
13
+
14
+ There are convenience methods to allow climbing up and down this tree, for
15
+ example to retrieve the list of all messages that are in the same package as
16
+ the current message.
17
+
18
+ Most of these classes take as inputs:
19
+ - proto_obj: A reference to it's corresponding protobuf object as
20
+ presented by the protoc plugin.
21
+ - parent: a reference to the parent object in the tree.
22
+
23
+ With this information, the class is able to expose attributes,
24
+ such as a pythonized name, that will be calculated from proto_obj.
25
+ """
26
+
27
+ import builtins
28
+ import inspect
29
+ import re
30
+ from collections.abc import Iterator
31
+ from dataclasses import (
32
+ dataclass,
33
+ field,
34
+ )
35
+
36
+ import betterproto2
37
+ from betterproto2 import unwrap
38
+
39
+ from betterproto2_compiler.compile.importing import get_type_reference, parse_source_type_name
40
+ from betterproto2_compiler.compile.naming import (
41
+ pythonize_class_name,
42
+ pythonize_enum_member_name,
43
+ pythonize_field_name,
44
+ pythonize_method_name,
45
+ )
46
+ from betterproto2_compiler.known_types import KNOWN_METHODS
47
+ from betterproto2_compiler.lib.google.protobuf import (
48
+ DescriptorProto,
49
+ EnumDescriptorProto,
50
+ FieldDescriptorProto,
51
+ FieldDescriptorProtoLabel,
52
+ FieldDescriptorProtoType,
53
+ FieldDescriptorProtoType as FieldType,
54
+ FileDescriptorProto,
55
+ MethodDescriptorProto,
56
+ OneofDescriptorProto,
57
+ ServiceDescriptorProto,
58
+ )
59
+ from betterproto2_compiler.lib.google.protobuf.compiler import CodeGeneratorRequest
60
+ from betterproto2_compiler.plugin.typing_compiler import TypingCompiler
61
+ from betterproto2_compiler.settings import Settings
62
+
63
+ # Organize proto types into categories
64
+ PROTO_FLOAT_TYPES = (
65
+ FieldDescriptorProtoType.TYPE_DOUBLE, # 1
66
+ FieldDescriptorProtoType.TYPE_FLOAT, # 2
67
+ )
68
+ PROTO_INT_TYPES = (
69
+ FieldDescriptorProtoType.TYPE_INT64, # 3
70
+ FieldDescriptorProtoType.TYPE_UINT64, # 4
71
+ FieldDescriptorProtoType.TYPE_INT32, # 5
72
+ FieldDescriptorProtoType.TYPE_FIXED64, # 6
73
+ FieldDescriptorProtoType.TYPE_FIXED32, # 7
74
+ FieldDescriptorProtoType.TYPE_UINT32, # 13
75
+ FieldDescriptorProtoType.TYPE_SFIXED32, # 15
76
+ FieldDescriptorProtoType.TYPE_SFIXED64, # 16
77
+ FieldDescriptorProtoType.TYPE_SINT32, # 17
78
+ FieldDescriptorProtoType.TYPE_SINT64, # 18
79
+ )
80
+ PROTO_BOOL_TYPES = (FieldDescriptorProtoType.TYPE_BOOL,) # 8
81
+ PROTO_STR_TYPES = (FieldDescriptorProtoType.TYPE_STRING,) # 9
82
+ PROTO_BYTES_TYPES = (FieldDescriptorProtoType.TYPE_BYTES,) # 12
83
+ PROTO_MESSAGE_TYPES = (
84
+ FieldDescriptorProtoType.TYPE_MESSAGE, # 11
85
+ FieldDescriptorProtoType.TYPE_ENUM, # 14
86
+ )
87
+ PROTO_MAP_TYPES = (FieldDescriptorProtoType.TYPE_MESSAGE,) # 11
88
+ PROTO_PACKED_TYPES = (
89
+ FieldDescriptorProtoType.TYPE_DOUBLE, # 1
90
+ FieldDescriptorProtoType.TYPE_FLOAT, # 2
91
+ FieldDescriptorProtoType.TYPE_INT64, # 3
92
+ FieldDescriptorProtoType.TYPE_UINT64, # 4
93
+ FieldDescriptorProtoType.TYPE_INT32, # 5
94
+ FieldDescriptorProtoType.TYPE_FIXED64, # 6
95
+ FieldDescriptorProtoType.TYPE_FIXED32, # 7
96
+ FieldDescriptorProtoType.TYPE_BOOL, # 8
97
+ FieldDescriptorProtoType.TYPE_UINT32, # 13
98
+ FieldDescriptorProtoType.TYPE_SFIXED32, # 15
99
+ FieldDescriptorProtoType.TYPE_SFIXED64, # 16
100
+ FieldDescriptorProtoType.TYPE_SINT32, # 17
101
+ FieldDescriptorProtoType.TYPE_SINT64, # 18
102
+ )
103
+
104
+
105
+ def get_comment(
106
+ proto_file: "FileDescriptorProto",
107
+ path: list[int],
108
+ ) -> str:
109
+ for sci_loc in unwrap(proto_file.source_code_info).location:
110
+ if list(sci_loc.path) == path:
111
+ all_comments = list(sci_loc.leading_detached_comments)
112
+ if sci_loc.leading_comments:
113
+ all_comments.append(sci_loc.leading_comments)
114
+ if sci_loc.trailing_comments:
115
+ all_comments.append(sci_loc.trailing_comments)
116
+
117
+ lines = []
118
+
119
+ for comment in all_comments:
120
+ lines += comment.split("\n")
121
+ lines.append("")
122
+
123
+ # Remove consecutive empty lines
124
+ lines = [line for i, line in enumerate(lines) if line or (i == 0 or lines[i - 1])]
125
+
126
+ if lines and not lines[-1]:
127
+ lines.pop() # Remove the last empty line
128
+
129
+ # It is common for one line comments to start with a space, for example: // comment
130
+ # We don't add this space to the generated file.
131
+ lines = [line[1:] if line and line[0] == " " else line for line in lines]
132
+
133
+ return "\n".join(lines)
134
+
135
+ return ""
136
+
137
+
138
+ @dataclass(kw_only=True)
139
+ class ProtoContentBase:
140
+ """Methods common to MessageCompiler, ServiceCompiler and ServiceMethodCompiler."""
141
+
142
+ source_file: FileDescriptorProto
143
+ path: list[int]
144
+
145
+ def ready(self) -> None:
146
+ """
147
+ This function is called after all the compilers are created, but before generating the output code.
148
+ """
149
+ pass
150
+
151
+ @property
152
+ def comment(self) -> str:
153
+ """Crawl the proto source code and retrieve comments
154
+ for this object.
155
+ """
156
+ return get_comment(proto_file=self.source_file, path=self.path)
157
+
158
+
159
+ @dataclass(kw_only=True)
160
+ class PluginRequestCompiler:
161
+ plugin_request_obj: CodeGeneratorRequest
162
+ output_packages: dict[str, "OutputTemplate"] = field(default_factory=dict)
163
+
164
+ @property
165
+ def all_messages(self) -> list["MessageCompiler"]:
166
+ """All of the messages in this request.
167
+
168
+ Returns
169
+ -------
170
+ List[MessageCompiler]
171
+ List of all of the messages in this request.
172
+ """
173
+ return [msg for output in self.output_packages.values() for msg in output.messages.values()]
174
+
175
+
176
+ @dataclass(kw_only=True)
177
+ class OutputTemplate:
178
+ """Representation of an output .py file.
179
+
180
+ Each output file corresponds to a .proto input file,
181
+ but may need references to other .proto files to be
182
+ built.
183
+ """
184
+
185
+ parent_request: PluginRequestCompiler
186
+ package_proto_obj: FileDescriptorProto
187
+ input_files: list[FileDescriptorProto] = field(default_factory=list)
188
+ imports_end: set[str] = field(default_factory=set)
189
+ messages: dict[str, "MessageCompiler"] = field(default_factory=dict)
190
+ enums: dict[str, "EnumDefinitionCompiler"] = field(default_factory=dict)
191
+ services: dict[str, "ServiceCompiler"] = field(default_factory=dict)
192
+
193
+ settings: Settings
194
+
195
+ @property
196
+ def package(self) -> str:
197
+ """Name of input package.
198
+
199
+ Returns
200
+ -------
201
+ str
202
+ Name of input package.
203
+ """
204
+ return self.package_proto_obj.package
205
+
206
+ @property
207
+ def input_filenames(self) -> list[str]:
208
+ """Names of the input files used to build this output.
209
+
210
+ Returns
211
+ -------
212
+ list[str]
213
+ Names of the input files used to build this output.
214
+ """
215
+ return sorted([f.name for f in self.input_files])
216
+
217
+
218
+ @dataclass(kw_only=True)
219
+ class MessageCompiler(ProtoContentBase):
220
+ """Representation of a protobuf message."""
221
+
222
+ output_file: OutputTemplate
223
+ proto_obj: DescriptorProto
224
+ fields: list["FieldCompiler"] = field(default_factory=list)
225
+ oneofs: list["OneofCompiler"] = field(default_factory=list)
226
+ builtins_types: set[str] = field(default_factory=set)
227
+
228
+ @property
229
+ def proto_name(self) -> str:
230
+ return self.proto_obj.name
231
+
232
+ @property
233
+ def py_name(self) -> str:
234
+ return pythonize_class_name(self.proto_name)
235
+
236
+ @property
237
+ def deprecated(self) -> bool:
238
+ return bool(self.proto_obj.options and self.proto_obj.options.deprecated)
239
+
240
+ @property
241
+ def deprecated_fields(self) -> Iterator[str]:
242
+ for f in self.fields:
243
+ if f.deprecated:
244
+ yield f.py_name
245
+
246
+ @property
247
+ def has_deprecated_fields(self) -> bool:
248
+ return any(self.deprecated_fields)
249
+
250
+ @property
251
+ def has_oneof_fields(self) -> bool:
252
+ return any(isinstance(field, OneOfFieldCompiler) for field in self.fields)
253
+
254
+ @property
255
+ def has_message_field(self) -> bool:
256
+ return any(
257
+ field.proto_obj.type in PROTO_MESSAGE_TYPES
258
+ for field in self.fields
259
+ if isinstance(field.proto_obj, FieldDescriptorProto)
260
+ )
261
+
262
+ @property
263
+ def custom_methods(self) -> list[str]:
264
+ """
265
+ Return a list of the custom methods.
266
+ """
267
+ methods_source: list[str] = []
268
+
269
+ for method in KNOWN_METHODS.get((self.source_file.package, self.py_name), []):
270
+ source = inspect.getsource(method)
271
+ methods_source.append(source.strip())
272
+
273
+ return methods_source
274
+
275
+
276
+ def is_map(proto_field_obj: FieldDescriptorProto, parent_message: DescriptorProto) -> bool:
277
+ """True if proto_field_obj is a map, otherwise False."""
278
+ if proto_field_obj.type == FieldDescriptorProtoType.TYPE_MESSAGE:
279
+ if not hasattr(parent_message, "nested_type"):
280
+ return False
281
+
282
+ # This might be a map...
283
+ message_type = proto_field_obj.type_name.split(".").pop().lower()
284
+ map_entry = f"{proto_field_obj.name.replace('_', '').lower()}entry"
285
+ if message_type == map_entry:
286
+ for nested in parent_message.nested_type: # parent message
287
+ if nested.name.replace("_", "").lower() == map_entry and nested.options and nested.options.map_entry:
288
+ return True
289
+ return False
290
+
291
+
292
+ def is_oneof(proto_field_obj: FieldDescriptorProto) -> bool:
293
+ """
294
+ True if proto_field_obj is a OneOf, otherwise False.
295
+ """
296
+ return not proto_field_obj.proto3_optional and proto_field_obj.oneof_index is not None
297
+
298
+
299
+ @dataclass(kw_only=True)
300
+ class FieldCompiler(ProtoContentBase):
301
+ typing_compiler: TypingCompiler
302
+ builtins_types: set[str] = field(default_factory=set)
303
+
304
+ message: MessageCompiler
305
+ proto_obj: FieldDescriptorProto
306
+
307
+ @property
308
+ def output_file(self) -> "OutputTemplate":
309
+ return self.message.output_file
310
+
311
+ def get_field_string(self) -> str:
312
+ """Construct string representation of this field as a field."""
313
+ name = f"{self.py_name}"
314
+ field_args = ", ".join(([""] + self.betterproto_field_args) if self.betterproto_field_args else [])
315
+
316
+ betterproto_field_type = (
317
+ f"betterproto2.field({self.proto_obj.number}, betterproto2.{str(self.field_type)}{field_args})"
318
+ )
319
+ if self.py_name in dir(builtins):
320
+ self.message.builtins_types.add(self.py_name)
321
+ return f'{name}: "{self.annotation}" = {betterproto_field_type}'
322
+
323
+ @property
324
+ def betterproto_field_args(self) -> list[str]:
325
+ args = []
326
+ if self.field_wraps:
327
+ args.append(f"wraps={self.field_wraps}")
328
+ if self.optional:
329
+ args.append("optional=True")
330
+ elif self.repeated:
331
+ args.append("repeated=True")
332
+ elif self.field_type == FieldType.TYPE_ENUM:
333
+ args.append(f"default_factory=lambda: {self.py_type}.try_value(0)")
334
+ return args
335
+
336
+ @property
337
+ def deprecated(self) -> bool:
338
+ return bool(self.proto_obj.options and self.proto_obj.options.deprecated)
339
+
340
+ @property
341
+ def use_builtins(self) -> bool:
342
+ return self.py_type in self.message.builtins_types or (
343
+ self.py_type == self.py_name and self.py_name in dir(builtins)
344
+ )
345
+
346
+ @property
347
+ def field_wraps(self) -> str | None:
348
+ """Returns betterproto wrapped field type or None."""
349
+ match_wrapper = re.match(r"\.google\.protobuf\.(.+)Value$", self.proto_obj.type_name)
350
+ if match_wrapper:
351
+ wrapped_type = "TYPE_" + match_wrapper.group(1).upper()
352
+ if hasattr(betterproto2, wrapped_type):
353
+ return f"betterproto2.{wrapped_type}"
354
+ return None
355
+
356
+ @property
357
+ def repeated(self) -> bool:
358
+ return self.proto_obj.label == FieldDescriptorProtoLabel.LABEL_REPEATED
359
+
360
+ @property
361
+ def optional(self) -> bool:
362
+ # TODO not for maps
363
+ return self.proto_obj.proto3_optional or (self.field_type == FieldType.TYPE_MESSAGE and not self.repeated)
364
+
365
+ @property
366
+ def field_type(self) -> FieldType:
367
+ # TODO it should be possible to remove constructor
368
+ return FieldType(self.proto_obj.type)
369
+
370
+ @property
371
+ def packed(self) -> bool:
372
+ """True if the wire representation is a packed format."""
373
+ return self.repeated and self.proto_obj.type in PROTO_PACKED_TYPES
374
+
375
+ @property
376
+ def py_name(self) -> str:
377
+ """Pythonized name."""
378
+ return pythonize_field_name(self.proto_name)
379
+
380
+ @property
381
+ def proto_name(self) -> str:
382
+ """Original protobuf name."""
383
+ return self.proto_obj.name
384
+
385
+ @property
386
+ def py_type(self) -> str:
387
+ """String representation of Python type."""
388
+ if self.proto_obj.type in PROTO_FLOAT_TYPES:
389
+ return "float"
390
+ elif self.proto_obj.type in PROTO_INT_TYPES:
391
+ return "int"
392
+ elif self.proto_obj.type in PROTO_BOOL_TYPES:
393
+ return "bool"
394
+ elif self.proto_obj.type in PROTO_STR_TYPES:
395
+ return "str"
396
+ elif self.proto_obj.type in PROTO_BYTES_TYPES:
397
+ return "bytes"
398
+ elif self.proto_obj.type in PROTO_MESSAGE_TYPES:
399
+ # Type referencing another defined Message or a named enum
400
+ return get_type_reference(
401
+ package=self.output_file.package,
402
+ imports=self.output_file.imports_end,
403
+ source_type=self.proto_obj.type_name,
404
+ request=self.output_file.parent_request,
405
+ settings=self.output_file.settings,
406
+ )
407
+ else:
408
+ raise NotImplementedError(f"Unknown type {self.proto_obj.type}")
409
+
410
+ @property
411
+ def annotation(self) -> str:
412
+ py_type = self.py_type
413
+ if self.use_builtins:
414
+ py_type = f"builtins.{py_type}"
415
+ if self.repeated:
416
+ return self.typing_compiler.list(py_type)
417
+ if self.optional:
418
+ return self.typing_compiler.optional(py_type)
419
+ return py_type
420
+
421
+
422
+ @dataclass(kw_only=True)
423
+ class OneOfFieldCompiler(FieldCompiler):
424
+ @property
425
+ def optional(self) -> bool:
426
+ return True
427
+
428
+ @property
429
+ def betterproto_field_args(self) -> list[str]:
430
+ args = super().betterproto_field_args
431
+ group = self.message.proto_obj.oneof_decl[unwrap(self.proto_obj.oneof_index)].name
432
+ args.append(f'group="{group}"')
433
+ return args
434
+
435
+
436
+ @dataclass(kw_only=True)
437
+ class MapEntryCompiler(FieldCompiler):
438
+ py_k_type: str = ""
439
+ py_v_type: str = ""
440
+ proto_k_type: str = ""
441
+ proto_v_type: str = ""
442
+
443
+ def ready(self) -> None:
444
+ """Explore nested types and set k_type and v_type if unset."""
445
+ map_entry = f"{self.proto_obj.name.replace('_', '').lower()}entry"
446
+ for nested in self.message.proto_obj.nested_type:
447
+ if nested.name.replace("_", "").lower() == map_entry and unwrap(nested.options).map_entry:
448
+ # Get Python types
449
+ self.py_k_type = FieldCompiler(
450
+ source_file=self.source_file,
451
+ proto_obj=nested.field[0], # key
452
+ typing_compiler=self.typing_compiler,
453
+ path=[],
454
+ message=self.message,
455
+ ).py_type
456
+ self.py_v_type = FieldCompiler(
457
+ source_file=self.source_file,
458
+ proto_obj=nested.field[1], # value
459
+ typing_compiler=self.typing_compiler,
460
+ path=[],
461
+ message=self.message,
462
+ ).py_type
463
+
464
+ # Get proto types
465
+ self.proto_k_type = unwrap(FieldDescriptorProtoType(nested.field[0].type).name)
466
+ self.proto_v_type = unwrap(FieldDescriptorProtoType(nested.field[1].type).name)
467
+ return
468
+
469
+ raise ValueError("can't find enum")
470
+
471
+ def get_field_string(self) -> str:
472
+ """Construct string representation of this field as a field."""
473
+ betterproto_field_type = (
474
+ f"betterproto2.field({self.proto_obj.number}, "
475
+ "betterproto2.TYPE_MAP, "
476
+ f"map_types=(betterproto2.{self.proto_k_type}, "
477
+ f"betterproto2.{self.proto_v_type}))"
478
+ )
479
+ if self.py_name in dir(builtins):
480
+ self.message.builtins_types.add(self.py_name)
481
+ return f'{self.py_name}: "{self.annotation}" = {betterproto_field_type}'
482
+
483
+ @property
484
+ def annotation(self) -> str:
485
+ return self.typing_compiler.dict(self.py_k_type, self.py_v_type)
486
+
487
+ @property
488
+ def repeated(self) -> bool:
489
+ return False # maps cannot be repeated
490
+
491
+
492
+ @dataclass(kw_only=True)
493
+ class OneofCompiler(ProtoContentBase):
494
+ proto_obj: OneofDescriptorProto
495
+
496
+ @property
497
+ def name(self) -> str:
498
+ return self.proto_obj.name
499
+
500
+
501
+ @dataclass(kw_only=True)
502
+ class EnumDefinitionCompiler(ProtoContentBase):
503
+ """Representation of a proto Enum definition."""
504
+
505
+ output_file: OutputTemplate
506
+ proto_obj: EnumDescriptorProto
507
+ entries: list["EnumDefinitionCompiler.EnumEntry"] = field(default_factory=list)
508
+
509
+ @dataclass(unsafe_hash=True, kw_only=True)
510
+ class EnumEntry:
511
+ """Representation of an Enum entry."""
512
+
513
+ name: str
514
+ value: int
515
+ comment: str
516
+
517
+ def __post_init__(self) -> None:
518
+ # Get entries/allowed values for this Enum
519
+ self.entries = [
520
+ self.EnumEntry(
521
+ name=pythonize_enum_member_name(entry_proto_value.name, self.proto_obj.name),
522
+ value=entry_proto_value.number,
523
+ comment=get_comment(proto_file=self.source_file, path=self.path + [2, entry_number]),
524
+ )
525
+ for entry_number, entry_proto_value in enumerate(self.proto_obj.value)
526
+ ]
527
+
528
+ @property
529
+ def proto_name(self) -> str:
530
+ return self.proto_obj.name
531
+
532
+ @property
533
+ def py_name(self) -> str:
534
+ return pythonize_class_name(self.proto_name)
535
+
536
+ @property
537
+ def deprecated(self) -> bool:
538
+ return bool(self.proto_obj.options and self.proto_obj.options.deprecated)
539
+
540
+
541
+ @dataclass(kw_only=True)
542
+ class ServiceCompiler(ProtoContentBase):
543
+ output_file: OutputTemplate
544
+ proto_obj: ServiceDescriptorProto
545
+ methods: list["ServiceMethodCompiler"] = field(default_factory=list)
546
+
547
+ @property
548
+ def proto_name(self) -> str:
549
+ return self.proto_obj.name
550
+
551
+ @property
552
+ def py_name(self) -> str:
553
+ return pythonize_class_name(self.proto_name)
554
+
555
+
556
+ @dataclass(kw_only=True)
557
+ class ServiceMethodCompiler(ProtoContentBase):
558
+ parent: ServiceCompiler
559
+ proto_obj: MethodDescriptorProto
560
+
561
+ @property
562
+ def py_name(self) -> str:
563
+ """Pythonized method name."""
564
+ return pythonize_method_name(self.proto_obj.name)
565
+
566
+ @property
567
+ def proto_name(self) -> str:
568
+ """Original protobuf name."""
569
+ return self.proto_obj.name
570
+
571
+ @property
572
+ def route(self) -> str:
573
+ package_part = f"{self.parent.output_file.package}." if self.parent.output_file.package else ""
574
+ return f"/{package_part}{self.parent.proto_name}/{self.proto_name}"
575
+
576
+ @property
577
+ def py_input_message_type(self) -> str:
578
+ """String representation of the Python type corresponding to the
579
+ input message.
580
+
581
+ Returns
582
+ -------
583
+ str
584
+ String representation of the Python type corresponding to the input message.
585
+ """
586
+ return get_type_reference(
587
+ package=self.parent.output_file.package,
588
+ imports=self.parent.output_file.imports_end,
589
+ source_type=self.proto_obj.input_type,
590
+ request=self.parent.output_file.parent_request,
591
+ unwrap=False,
592
+ settings=self.parent.output_file.settings,
593
+ )
594
+
595
+ @property
596
+ def is_input_msg_empty(self: "ServiceMethodCompiler") -> bool:
597
+ package, name = parse_source_type_name(self.proto_obj.input_type, self.parent.output_file.parent_request)
598
+
599
+ msg = self.parent.output_file.parent_request.output_packages[package].messages[name]
600
+
601
+ return not bool(msg.fields)
602
+
603
+ @property
604
+ def py_input_message_param(self) -> str:
605
+ """Param name corresponding to py_input_message_type.
606
+
607
+ Returns
608
+ -------
609
+ str
610
+ Param name corresponding to py_input_message_type.
611
+ """
612
+ return pythonize_field_name(self.py_input_message_type)
613
+
614
+ @property
615
+ def py_output_message_type(self) -> str:
616
+ """String representation of the Python type corresponding to the
617
+ output message.
618
+
619
+ Returns
620
+ -------
621
+ str
622
+ String representation of the Python type corresponding to the output message.
623
+ """
624
+ return get_type_reference(
625
+ package=self.parent.output_file.package,
626
+ imports=self.parent.output_file.imports_end,
627
+ source_type=self.proto_obj.output_type,
628
+ request=self.parent.output_file.parent_request,
629
+ unwrap=False,
630
+ settings=self.parent.output_file.settings,
631
+ )
632
+
633
+ @property
634
+ def client_streaming(self) -> bool:
635
+ return self.proto_obj.client_streaming
636
+
637
+ @property
638
+ def server_streaming(self) -> bool:
639
+ return self.proto_obj.server_streaming
640
+
641
+ @property
642
+ def deprecated(self) -> bool:
643
+ return bool(self.proto_obj.options and self.proto_obj.options.deprecated)