Skip to content

Commit a2f0e2d

Browse files
committed
only generate method as property if deprecated
Signed-off-by: Aidan Jensen <aidandj.github@gmail.com>
1 parent 639175b commit a2f0e2d

14 files changed

+170
-125
lines changed

mypy_protobuf/main.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -580,10 +580,11 @@ def write_messages(
580580
if is_scalar(field) and field.label != d.FieldDescriptorProto.LABEL_REPEATED:
581581
# Scalar non repeated fields are r/w, generate getter and setter if deprecated
582582
scl_field = scl + [d.DescriptorProto.FIELD_FIELD_NUMBER, idx]
583+
deprecation_scl_field = scl_field + [d.FieldDescriptorProto.OPTIONS_FIELD_NUMBER] + [d.FieldOptions.DEPRECATED_FIELD_NUMBER]
583584
if field.options.deprecated:
584585
wl("@property")
585586
self._write_deprecation_warning(
586-
scl_field + [d.FieldDescriptorProto.OPTIONS_FIELD_NUMBER] + [d.FieldOptions.DEPRECATED_FIELD_NUMBER],
587+
deprecation_scl_field,
587588
"This field has been marked as deprecated using proto field options.",
588589
)
589590
body = " ..." if not self._has_comments(scl_field) else ""
@@ -594,7 +595,7 @@ def write_messages(
594595
wl("")
595596
wl(f"@{field.name}.setter")
596597
self._write_deprecation_warning(
597-
scl_field + [d.FieldDescriptorProto.OPTIONS_FIELD_NUMBER] + [d.FieldOptions.DEPRECATED_FIELD_NUMBER],
598+
deprecation_scl_field,
598599
"This field has been marked as deprecated using proto field options.",
599600
)
600601
body = " ..." if not self._has_comments(scl_field) else ""
@@ -953,7 +954,7 @@ def write_grpc_servicer_context(self) -> None:
953954
wl("...")
954955
wl("")
955956

956-
def write_grpc_stub_methods(self, service: d.ServiceDescriptorProto, scl_prefix: SourceCodeLocation, *, is_async: bool, both: bool = False, ignore_override_errors: bool = False) -> None:
957+
def write_grpc_stub_methods(self, service: d.ServiceDescriptorProto, scl_prefix: SourceCodeLocation, *, is_async: bool, both: bool = False, ignore_type_error: bool = False) -> None:
957958
wl = self._write_line
958959
methods = [(i, m) for i, m in enumerate(service.method) if m.name not in PYTHON_RESERVED]
959960
if not methods:
@@ -964,20 +965,30 @@ def type_str(method: d.MethodDescriptorProto, is_async: bool) -> str:
964965

965966
for i, method in methods:
966967
scl = scl_prefix + [d.ServiceDescriptorProto.METHOD_FIELD_NUMBER, i]
967-
wl("@property")
968-
if method.options.deprecated:
968+
is_deprecated = method.options.deprecated
969+
has_comments = self._has_comments(scl)
970+
971+
# Generate type annotation once
972+
if both:
973+
type_annotation = f"{self._import('typing', 'Union')}[{type_str(method, is_async=False)}, {type_str(method, is_async=True)}]"
974+
else:
975+
type_annotation = type_str(method, is_async=is_async)
976+
977+
if is_deprecated:
978+
wl("@property")
969979
self._write_deprecation_warning(
970-
scl + [d.MethodDescriptorProto.OPTIONS_FIELD_NUMBER] + [d.MethodOptions.DEPRECATED_FIELD_NUMBER],
980+
scl + [d.MethodDescriptorProto.OPTIONS_FIELD_NUMBER, d.MethodOptions.DEPRECATED_FIELD_NUMBER],
971981
"This method has been marked as deprecated using proto method options.",
972982
)
973-
if both:
974-
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 " ")
983+
wl(f"def {method.name}(self) -> {type_annotation}:{' ...' if not has_comments else ''}{' # type: ignore[override]' if ignore_type_error else ''}")
984+
985+
if has_comments:
986+
with self._indent():
987+
if not self._write_comments(scl):
988+
wl("...")
975989
else:
976-
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]")
977-
if self._has_comments(scl):
978-
with self._indent():
979-
if not self._write_comments(scl):
980-
wl("...")
990+
wl(f"{method.name}: {type_annotation}{' # type: ignore[assignment]' if ignore_type_error else ''}")
991+
self._write_comments(scl)
981992

982993
def write_grpc_methods(self, service: d.ServiceDescriptorProto, scl_prefix: SourceCodeLocation) -> None:
983994
wl = self._write_line
@@ -1094,7 +1105,7 @@ def write_grpc_services(
10941105
if self._write_comments(scl):
10951106
wl("")
10961107
wl("def __init__(self, channel: {}) -> None: ...", self._import("grpc.aio", "Channel"))
1097-
self.write_grpc_stub_methods(service, scl, is_async=True, ignore_assignment_errors=True)
1108+
self.write_grpc_stub_methods(service, scl, is_async=True, ignore_type_error=True)
10981109
else:
10991110
# ASYNC only - use Stub name (not AsyncStub) since there's only one type
11001111
wl("class {}:", class_name)

proto/testproto/grpc/dummy.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ service DeprecatedService {
4141
// Method is deprecated, but request message is not
4242
option deprecated = true;
4343
}
44+
rpc DeprecatedMethodNoComments(DeprecatedRequest) returns (DummyReply) {
45+
option deprecated = true;
46+
}
4447
}
4548

4649
message ManyRequest1 {}

test_negative/negative.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,13 @@ def DeprecatedMethodNotDeprecatedRequest(
313313
) -> DummyReply:
314314
return DummyReply()
315315

316+
def DeprecatedMethodNoComments(
317+
self,
318+
request: DeprecatedRequest,
319+
context: grpc.ServicerContext,
320+
) -> DummyReply:
321+
return DummyReply()
322+
316323

317324
server = grpc.server(thread_pool=concurrent.futures.ThreadPoolExecutor())
318325
deprecated_servicer = DeprecatedServicer()

test_negative/output.expected.3.10

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -126,24 +126,24 @@ test_negative/negative.py:292: error: Return type "Iterator[DummyReply]" of "Str
126126
test_negative/negative.py:294: error: Argument 1 of "StreamUnary" is incompatible with supertype "testproto.grpc.dummy_pb2_grpc.DummyServiceServicer"; supertype defines the argument type as "_MaybeAsyncIterator[DummyRequest]" [override]
127127
test_negative/negative.py:294: note: This violates the Liskov substitution principle
128128
test_negative/negative.py:294: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
129-
test_negative/negative.py:321: error: function testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub.DeprecatedMethod is deprecated: This method has been marked as deprecated using proto method options. [deprecated]
130-
test_negative/negative.py:322: error: function testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub.DeprecatedMethodNotDeprecatedRequest is deprecated: Method is deprecated, but request message is not [deprecated]
131-
test_negative/negative.py:323: error: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceAsyncStub is deprecated: This service is deprecated [deprecated]
132-
test_negative/negative.py:326: error: function testproto.test_pb2._DeprecatedEnumEnumTypeWrapper.DEPRECATED_ONE is deprecated: This enum value has been marked as deprecated using proto enum value options. [deprecated]
133-
test_negative/negative.py:327: error: function testproto.test_pb2._DeprecatedEnumEnumTypeWrapper.DEPRECATED_TWO is deprecated: This enum value has been marked as deprecated using proto enum value options. [deprecated]
134-
test_negative/negative.py:337: error: Argument "implicit_singular" to "Editions2024Test" has incompatible type "None"; expected "str" [arg-type]
135-
test_negative/negative.py:339: error: Argument 1 to "HasField" of "Editions2024Test" has incompatible type "Literal['implicit_singular']"; expected "Literal['default_singular', b'default_singular', 'explicit_singular', b'explicit_singular', 'legacy', b'legacy', 'message_field', b'message_field']" [arg-type]
136-
test_negative/negative.py:350: error: Cannot instantiate abstract class "IncompleteServicer" with abstract attributes "StreamStream", "StreamUnary", "UnaryStream" and "UnaryUnary" [abstract]
137-
test_negative/negative.py:357: error: Argument 2 to "test_hasfield_alias" has incompatible type "Literal['not_a_field']"; expected "Literal['default_singular', b'default_singular', 'explicit_singular', b'explicit_singular', 'legacy', b'legacy', 'message_field', b'message_field']" [arg-type]
138-
test_negative/negative.py:364: error: Argument 2 to "test_clearfield_alias" has incompatible type "Literal['not_a_field']"; expected "Literal['default_singular', b'default_singular', 'explicit_singular', b'explicit_singular', 'implicit_singular', b'implicit_singular', 'legacy', b'legacy', 'message_field', b'message_field']" [arg-type]
139-
test_negative/negative.py:365: error: Property "message_field" defined in "Editions2024Test" is read-only [misc]
140-
test_negative/negative.py:376: error: Argument 2 to "test_whichoneof_alias" has incompatible type "Literal['not_a_oneof']"; expected "Literal['a_oneof', b'a_oneof']" [arg-type]
141-
test_negative/negative.py:379: error: All overload variants of "DummyServiceStub" require at least one argument [call-overload]
142-
test_negative/negative.py:379: note: Possible overload variants:
143-
test_negative/negative.py:379: note: def __new__(cls, channel: Channel) -> DummyServiceStub
144-
test_negative/negative.py:379: note: def __new__(cls, channel: Channel) -> DummyServiceAsyncStub
145-
test_negative/negative.py:380: error: No overload variant of "DummyServiceStub" matches argument type "str" [call-overload]
146-
test_negative/negative.py:380: note: Possible overload variants:
147-
test_negative/negative.py:380: note: def __new__(cls, channel: Channel) -> DummyServiceStub
148-
test_negative/negative.py:380: note: def __new__(cls, channel: Channel) -> DummyServiceAsyncStub
149-
Found 111 errors in 4 files (checked 4 source files)
129+
test_negative/negative.py:328: error: function testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub.DeprecatedMethod is deprecated: This method has been marked as deprecated using proto method options. [deprecated]
130+
test_negative/negative.py:329: error: function testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub.DeprecatedMethodNotDeprecatedRequest is deprecated: Method is deprecated, but request message is not [deprecated]
131+
test_negative/negative.py:330: error: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceAsyncStub is deprecated: This service is deprecated [deprecated]
132+
test_negative/negative.py:333: error: function testproto.test_pb2._DeprecatedEnumEnumTypeWrapper.DEPRECATED_ONE is deprecated: This enum value has been marked as deprecated using proto enum value options. [deprecated]
133+
test_negative/negative.py:334: error: function testproto.test_pb2._DeprecatedEnumEnumTypeWrapper.DEPRECATED_TWO is deprecated: This enum value has been marked as deprecated using proto enum value options. [deprecated]
134+
test_negative/negative.py:344: error: Argument "implicit_singular" to "Editions2024Test" has incompatible type "None"; expected "str" [arg-type]
135+
test_negative/negative.py:346: error: Argument 1 to "HasField" of "Editions2024Test" has incompatible type "Literal['implicit_singular']"; expected "Literal['default_singular', b'default_singular', 'explicit_singular', b'explicit_singular', 'legacy', b'legacy', 'message_field', b'message_field']" [arg-type]
136+
test_negative/negative.py:357: error: Cannot instantiate abstract class "IncompleteServicer" with abstract attributes "StreamStream", "StreamUnary", "UnaryStream" and "UnaryUnary" [abstract]
137+
test_negative/negative.py:364: error: Argument 2 to "test_hasfield_alias" has incompatible type "Literal['not_a_field']"; expected "Literal['default_singular', b'default_singular', 'explicit_singular', b'explicit_singular', 'legacy', b'legacy', 'message_field', b'message_field']" [arg-type]
138+
test_negative/negative.py:371: error: Argument 2 to "test_clearfield_alias" has incompatible type "Literal['not_a_field']"; expected "Literal['default_singular', b'default_singular', 'explicit_singular', b'explicit_singular', 'implicit_singular', b'implicit_singular', 'legacy', b'legacy', 'message_field', b'message_field']" [arg-type]
139+
test_negative/negative.py:372: error: Property "message_field" defined in "Editions2024Test" is read-only [misc]
140+
test_negative/negative.py:383: error: Argument 2 to "test_whichoneof_alias" has incompatible type "Literal['not_a_oneof']"; expected "Literal['a_oneof', b'a_oneof']" [arg-type]
141+
test_negative/negative.py:386: error: All overload variants of "DummyServiceStub" require at least one argument [call-overload]
142+
test_negative/negative.py:386: note: Possible overload variants:
143+
test_negative/negative.py:386: note: def __new__(cls, channel: Channel) -> DummyServiceStub
144+
test_negative/negative.py:386: note: def __new__(cls, channel: Channel) -> DummyServiceAsyncStub
145+
test_negative/negative.py:387: error: No overload variant of "DummyServiceStub" matches argument type "str" [call-overload]
146+
test_negative/negative.py:387: note: Possible overload variants:
147+
test_negative/negative.py:387: note: def __new__(cls, channel: Channel) -> DummyServiceStub
148+
test_negative/negative.py:387: note: def __new__(cls, channel: Channel) -> DummyServiceAsyncStub
149+
Found 114 errors in 4 files (checked 4 source files)

test_negative/output.expected.3.10.omit_linenos

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
test/generated/testproto/grpc/dummy_pb2.pyi: error: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
22
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: error: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceAsyncStub is deprecated: This service is deprecated [deprecated]
33
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: error: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
4+
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: error: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
45
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: error: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub is deprecated: This service is deprecated [deprecated]
56
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: error: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
67
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: note: Error code "deprecated" not covered by "type: ignore" comment
78
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: error: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
9+
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: note: Error code "deprecated" not covered by "type: ignore" comment
10+
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: error: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
11+
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: error: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
812
test/generated/testproto/grpc/dummy_pb2_grpc.pyi: error: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceServicer is deprecated: This service is deprecated [deprecated]
913
test/generated/testproto/test_pb2.pyi: error: class testproto.test_pb2.DeprecatedEnum is deprecated: This enum is deprecated
1014
2 lines of comments
@@ -145,4 +149,4 @@ test_negative/negative.py: error: No overload variant of "DummyServiceStub" matc
145149
test_negative/negative.py: note: Possible overload variants:
146150
test_negative/negative.py: note: def __new__(cls, channel: Channel) -> DummyServiceStub
147151
test_negative/negative.py: note: def __new__(cls, channel: Channel) -> DummyServiceAsyncStub
148-
Found 111 errors in 4 files (checked 4 source files)
152+
Found 114 errors in 4 files (checked 4 source files)

0 commit comments

Comments
 (0)