betterproto2-compiler 0.2.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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)