Skip to content

Commit f4443be

Browse files
committed
Support comments and deprecation for methods and enum values
* Express methods as properties, allows for deprecation * Express enum values as properties, allows for deprecation Signed-off-by: Aidan Jensen <aidandj.github@gmail.com>
1 parent f35584a commit f4443be

31 files changed

+1867
-1288
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
- Add `_WhichOneofArgType_<oneof_name>` and `_WhichOneofReturnType_<oneof_name>` type aliases
1414
- Use `__new__` overloads for async stubs instead of `TypeVar` based `__init__` overloads.
1515
- https://github.com/nipunn1313/mypy-protobuf/issues/707
16+
- Export stub methods as properties instead of attributes. This allows for deprecation
17+
- Export enum fields as properties on class level (not module level) enums. This allows for deprecation
1618

1719
## 3.7.0
1820

mypy_protobuf/main.py

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ def _get_comments(self, scl: SourceCodeLocation) -> List[str]:
315315

316316
return lines
317317

318-
def _write_deprecation_warning(self, scl: SourceCodeLocation, default_message: str) -> None:
318+
def _get_deprecation_message(self, scl: SourceCodeLocation, default_message: str) -> str:
319319
msg = default_message
320320
if not self.use_default_depreaction_warnings and (comments := self._get_comments(scl)):
321321
# Make sure the comment string is a valid python string literal
@@ -327,6 +327,10 @@ def _write_deprecation_warning(self, scl: SourceCodeLocation, default_message: s
327327
except SyntaxError as e:
328328
print(f"Warning: Deprecation comment {joined} could not be parsed as a python string literal. Using default deprecation message. {e}", file=sys.stderr)
329329
pass
330+
return msg
331+
332+
def _write_deprecation_warning(self, scl: SourceCodeLocation, default_message: str) -> None:
333+
msg = self._get_deprecation_message(scl, default_message)
330334
self._write_line(
331335
'@{}("""{}""")',
332336
self._import("warnings", "deprecated"),
@@ -387,16 +391,33 @@ def write_enum_values(
387391
values: Iterable[Tuple[int, d.EnumValueDescriptorProto]],
388392
value_type: str,
389393
scl_prefix: SourceCodeLocation,
394+
*,
395+
as_properties: bool = False,
390396
) -> None:
391397
for i, val in values:
392398
if val.name in PYTHON_RESERVED:
393399
continue
394400

395401
scl = scl_prefix + [i]
396-
self._write_line(
397-
f"{val.name}: {value_type} # {val.number}",
398-
)
399-
self._write_comments(scl)
402+
# Class level
403+
if as_properties:
404+
self._write_line("@property")
405+
if val.options.deprecated:
406+
self._write_deprecation_warning(
407+
scl + [d.EnumValueDescriptorProto.OPTIONS_FIELD_NUMBER] + [d.EnumOptions.DEPRECATED_FIELD_NUMBER],
408+
"This enum value has been marked as deprecated using proto enum value options.",
409+
)
410+
self._write_line(
411+
f"def {val.name}(self) -> {value_type}: {'' if self._has_comments(scl) else '...'} # {val.number}",
412+
)
413+
with self._indent():
414+
self._write_comments(scl)
415+
# Module level
416+
else:
417+
self._write_line(
418+
f"{val.name}: {value_type} # {val.number}",
419+
)
420+
self._write_comments(scl)
400421

401422
def write_module_attributes(self) -> None:
402423
wl = self._write_line
@@ -443,6 +464,7 @@ def write_enums(
443464
[(i, v) for i, v in enumerate(enum_proto.value) if v.name not in PROTO_ENUM_RESERVED],
444465
value_type_helper_fq,
445466
scl + [d.EnumDescriptorProto.VALUE_FIELD_NUMBER],
467+
as_properties=True,
446468
)
447469
wl("")
448470

@@ -461,6 +483,7 @@ def write_enums(
461483
if prefix == "":
462484
wl("")
463485

486+
# Write the module level constants for enum values
464487
self.write_enum_values(
465488
enumerate(enum_proto.value),
466489
value_type_fq,
@@ -878,7 +901,7 @@ def write_grpc_servicer_context(self) -> None:
878901
wl("...")
879902
wl("")
880903

881-
def write_grpc_stub_methods(self, service: d.ServiceDescriptorProto, scl_prefix: SourceCodeLocation, *, is_async: bool, both: bool = False, ignore_assignment_errors: bool = False) -> None:
904+
def write_grpc_stub_methods(self, service: d.ServiceDescriptorProto, scl_prefix: SourceCodeLocation, *, is_async: bool, both: bool = False, ignore_override_errors: bool = False) -> None:
882905
wl = self._write_line
883906
methods = [(i, m) for i, m in enumerate(service.method) if m.name not in PYTHON_RESERVED]
884907
if not methods:
@@ -889,17 +912,20 @@ def type_str(method: d.MethodDescriptorProto, is_async: bool) -> str:
889912

890913
for i, method in methods:
891914
scl = scl_prefix + [d.ServiceDescriptorProto.METHOD_FIELD_NUMBER, i]
892-
if both:
893-
wl(
894-
"{}: {}[{}, {}]",
895-
method.name,
896-
self._import("typing", "Union"),
897-
type_str(method, is_async=False),
898-
type_str(method, is_async=True),
915+
wl("@property")
916+
if method.options.deprecated:
917+
self._write_deprecation_warning(
918+
scl + [d.MethodDescriptorProto.OPTIONS_FIELD_NUMBER] + [d.MethodOptions.DEPRECATED_FIELD_NUMBER],
919+
"This method has been marked as deprecated using proto method options.",
899920
)
921+
if both:
922+
wl("def {}(self) -> {}[{}, {}]:{}", method.name, self._import("typing", "Union"), type_str(method, is_async=False), type_str(method, is_async=True), " ..." if not self._has_comments(scl) else " ")
900923
else:
901-
wl("{}: {}{}", method.name, type_str(method, is_async=is_async), "" if not ignore_assignment_errors else " # type: ignore[assignment]")
902-
self._write_comments(scl)
924+
wl("def {}(self) -> {}:{}{}", method.name, type_str(method, is_async=is_async), " ..." if not self._has_comments(scl) else "", "" if not ignore_override_errors else " # type: ignore[override]")
925+
if self._has_comments(scl):
926+
with self._indent():
927+
if not self._write_comments(scl):
928+
wl("...")
903929

904930
def write_grpc_methods(self, service: d.ServiceDescriptorProto, scl_prefix: SourceCodeLocation) -> None:
905931
wl = self._write_line

proto/testproto/grpc/dummy.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ service DeprecatedService {
3838
}
3939
// DeprecatedMethodNotDeprecatedRequest
4040
rpc DeprecatedMethodNotDeprecatedRequest(DummyRequest) returns (DummyReply) {
41+
// Method is deprecated, but request message is not
4142
option deprecated = true;
4243
}
4344
}

proto/testproto/test.proto

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ enum DeprecatedEnum {
193193
option deprecated = true;
194194
// Trailing comment
195195

196-
DEPRECATED_ONE = 1;
197-
DEPRECATED_TWO = 2;
196+
DEPRECATED_ONE = 1 [
197+
// This enum value is deprecated but this message doesn't show
198+
deprecated = true
199+
]; // Trailing comment for enum value
200+
DEPRECATED_TWO = 2 [deprecated = true];
201+
NOT_DEPRECATED = 3;
198202
}

stubtest_allowlist.txt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ testproto.nested.nested_pb2.AnotherNested.NestedMessage.NESTED_ENUM2
3636
testproto.nested.nested_pb2.AnotherNested.INVALID
3737
testproto.test_pb2.DEPRECATED_ONE
3838
testproto.test_pb2.DEPRECATED_TWO
39+
testproto.test_pb2.NOT_DEPRECATED
3940

4041
# Our enum types and helper types aren't there at runtime (Dynamic EnumTypeWrapper at runtime)
4142
# .*\..*EnumTypeWrapper$
@@ -246,9 +247,9 @@ testproto.test_pb2.Extensions1.ext
246247
.*_WhichOneofReturnType.*
247248
.*_WhichOneofArgType.*
248249

249-
#
250-
testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub.__init__
251-
testproto.grpc.dummy_pb2_grpc.DummyServiceStub.__init__
252-
testproto.grpc.dummy_pb2_grpc.ManyRPCsServiceStub.__init__
253-
testproto.grpc.import_pb2_grpc.SimpleServiceStub.__init__
250+
# Because we represent methods as properties, they do not exist at runtime
251+
testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub.*
252+
testproto.grpc.dummy_pb2_grpc.DummyServiceStub.*
253+
testproto.grpc.dummy_pb2_grpc.ManyRPCsServiceStub.*
254+
testproto.grpc.import_pb2_grpc.SimpleServiceStub.*
254255
testproto.grpc.dummy_pb2_grpc.EmptyServiceStub.__init__

0 commit comments

Comments
 (0)