Skip to content

Commit 798bf5a

Browse files
author
Aidan Jensen
committed
Pull comments from deprecation option for warning details
Signed-off-by: Aidan Jensen <[email protected]>
1 parent 19a0d54 commit 798bf5a

File tree

13 files changed

+182
-74
lines changed

13 files changed

+182
-74
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,16 @@ However, it may be helpful when migrating existing proto2 code, where the distin
219219
protoc --python_out=output/location --mypy_out=relax_strict_optional_primitives:output/location
220220
```
221221

222+
### `use_default_deprecation_warnings`
223+
224+
By default mypy-protobuf will pull the leading and trailing comments from the deprecation option definition,
225+
and insert it into the deprecation warning. This option will instead use a standard deprecation warning instead of comments.
226+
227+
```
228+
protoc --python_out=output/location --mypy_out=use_default_deprecation_warning:output/location
229+
230+
```
231+
222232
### Output suppression
223233

224234
To suppress output, you can run

mypy_protobuf/main.py

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,14 @@ def __init__(
149149
descriptors: Descriptors,
150150
readable_stubs: bool,
151151
relax_strict_optional_primitives: bool,
152+
use_default_deprecation_warnings: bool,
152153
grpc: bool,
153154
) -> None:
154155
self.fd = fd
155156
self.descriptors = descriptors
156157
self.readable_stubs = readable_stubs
157158
self.relax_strict_optional_primitives = relax_strict_optional_primitives
159+
self.use_default_depreaction_warnings = use_default_deprecation_warnings
158160
self.grpc = grpc
159161
self.lines: List[str] = []
160162
self.indent = ""
@@ -257,6 +259,47 @@ def _has_comments(self, scl: SourceCodeLocation) -> bool:
257259
sci_loc = self.source_code_info_by_scl.get(tuple(scl))
258260
return sci_loc is not None and bool(sci_loc.leading_detached_comments or sci_loc.leading_comments or sci_loc.trailing_comments)
259261

262+
def _get_comments(self, scl: SourceCodeLocation) -> List[str]:
263+
"""Return list of comment lines"""
264+
if not self._has_comments(scl):
265+
return []
266+
267+
sci_loc = self.source_code_info_by_scl.get(tuple(scl))
268+
assert sci_loc is not None
269+
270+
leading_detached_lines = []
271+
leading_lines = []
272+
trailing_lines = []
273+
for leading_detached_comment in sci_loc.leading_detached_comments:
274+
leading_detached_lines = self._break_text(leading_detached_comment)
275+
if sci_loc.leading_comments is not None:
276+
leading_lines = self._break_text(sci_loc.leading_comments)
277+
# Trailing comments also go in the header - to make sure it gets into the docstring
278+
if sci_loc.trailing_comments is not None:
279+
trailing_lines = self._break_text(sci_loc.trailing_comments)
280+
281+
lines = leading_detached_lines
282+
if leading_detached_lines and (leading_lines or trailing_lines):
283+
lines.append("")
284+
lines.extend(leading_lines)
285+
lines.extend(trailing_lines)
286+
287+
return lines
288+
289+
def _write_deprecation_warning(self, scl: SourceCodeLocation, default_message: str) -> None:
290+
if not self.use_default_depreaction_warnings and (comments := self._get_comments(scl)):
291+
self._write_line(
292+
'@{}("""{}""")',
293+
self._import("warnings", "deprecated"),
294+
"\\n".join(comments),
295+
)
296+
else:
297+
self._write_line(
298+
'@{}("{}")',
299+
self._import("warnings", "deprecated"),
300+
default_message,
301+
)
302+
260303
def _write_comments(self, scl: SourceCodeLocation) -> bool:
261304
"""Return true if any comments were written"""
262305
if not self._has_comments(scl):
@@ -371,9 +414,8 @@ def write_enums(
371414
wl("")
372415

373416
if enum.options.deprecated:
374-
wl(
375-
'@{}("{}")',
376-
self._import("warnings", "deprecated"),
417+
self._write_deprecation_warning(
418+
scl + [d.EnumDescriptorProto.OPTIONS_FIELD_NUMBER] + [d.EnumOptions.DEPRECATED_FIELD_NUMBER],
377419
"This enum has been marked as deprecated using proto enum options.",
378420
)
379421
if self._has_comments(scl):
@@ -422,9 +464,8 @@ def write_messages(
422464
class_name = desc.name if desc.name not in PYTHON_RESERVED else "_r_" + desc.name
423465
message_class = self._import("google.protobuf.message", "Message")
424466
if desc.options.deprecated:
425-
wl(
426-
'@{}("{}")',
427-
self._import("warnings", "deprecated"),
467+
self._write_deprecation_warning(
468+
scl_prefix + [i] + [d.DescriptorProto.OPTIONS_FIELD_NUMBER] + [d.MessageOptions.DEPRECATED_FIELD_NUMBER],
428469
"This message has been marked as deprecated using proto message options.",
429470
)
430471
wl("@{}", self._import("typing", "final"))
@@ -854,9 +895,8 @@ def write_grpc_services(
854895

855896
# The stub client
856897
if service.options.deprecated:
857-
wl(
858-
'@{}("{}")',
859-
self._import("warnings", "deprecated"),
898+
self._write_deprecation_warning(
899+
scl + [d.ServiceDescriptorProto.OPTIONS_FIELD_NUMBER] + [d.ServiceOptions.DEPRECATED_FIELD_NUMBER],
860900
"This stub has been marked as deprecated using proto service options.",
861901
)
862902
class_name = f"{service.name}Stub"
@@ -900,9 +940,8 @@ def write_grpc_services(
900940

901941
# The service definition interface
902942
if service.options.deprecated:
903-
wl(
904-
'@{}("{}")',
905-
self._import("warnings", "deprecated"),
943+
self._write_deprecation_warning(
944+
scl + [d.ServiceDescriptorProto.OPTIONS_FIELD_NUMBER] + [d.ServiceOptions.DEPRECATED_FIELD_NUMBER],
906945
"This servicer has been marked as deprecated using proto service options.",
907946
)
908947
wl(
@@ -917,9 +956,8 @@ def write_grpc_services(
917956
server = self._import("grpc", "Server")
918957
aserver = self._import("grpc.aio", "Server")
919958
if service.options.deprecated:
920-
wl(
921-
'@{}("{}")',
922-
self._import("warnings", "deprecated"),
959+
self._write_deprecation_warning(
960+
scl + [d.ServiceDescriptorProto.OPTIONS_FIELD_NUMBER] + [d.ServiceOptions.DEPRECATED_FIELD_NUMBER],
923961
"This servicer has been marked as deprecated using proto service options.",
924962
)
925963
wl(
@@ -1083,13 +1121,15 @@ def generate_mypy_stubs(
10831121
quiet: bool,
10841122
readable_stubs: bool,
10851123
relax_strict_optional_primitives: bool,
1124+
use_default_deprecation_warnings: bool,
10861125
) -> None:
10871126
for name, fd in descriptors.to_generate.items():
10881127
pkg_writer = PkgWriter(
10891128
fd,
10901129
descriptors,
10911130
readable_stubs,
10921131
relax_strict_optional_primitives,
1132+
use_default_deprecation_warnings,
10931133
grpc=False,
10941134
)
10951135

@@ -1115,13 +1155,15 @@ def generate_mypy_grpc_stubs(
11151155
quiet: bool,
11161156
readable_stubs: bool,
11171157
relax_strict_optional_primitives: bool,
1158+
use_default_deprecation_warnings: bool,
11181159
) -> None:
11191160
for name, fd in descriptors.to_generate.items():
11201161
pkg_writer = PkgWriter(
11211162
fd,
11221163
descriptors,
11231164
readable_stubs,
11241165
relax_strict_optional_primitives,
1166+
use_default_deprecation_warnings,
11251167
grpc=True,
11261168
)
11271169
pkg_writer.write_grpc_async_hacks()
@@ -1173,6 +1215,7 @@ def main() -> None:
11731215
"quiet" in request.parameter,
11741216
"readable_stubs" in request.parameter,
11751217
"relax_strict_optional_primitives" in request.parameter,
1218+
"use_default_deprecation_warnings" in request.parameter,
11761219
)
11771220

11781221

@@ -1185,6 +1228,7 @@ def grpc() -> None:
11851228
"quiet" in request.parameter,
11861229
"readable_stubs" in request.parameter,
11871230
"relax_strict_optional_primitives" in request.parameter,
1231+
"use_default_deprecation_warnings" in request.parameter,
11881232
)
11891233

11901234

proto/testproto/grpc/dummy.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ message DeprecatedRequest {
3030

3131
// Marking the service as deprecated
3232
service DeprecatedService {
33+
// This service is deprecated
3334
option deprecated = true;
3435
// DeprecatedMethod
3536
rpc DeprecatedMethod (DeprecatedRequest) returns (DummyReply) {

proto/testproto/test.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,11 @@ message DeprecatedMessage {
190190

191191
enum DeprecatedEnum {
192192
// This enum is deprecated
193+
// 2 lines of comments
194+
// "Quotes in comments"
195+
// and 'single quotes'
193196
option deprecated = true;
197+
// Trailing comment
194198

195199
DEPRECATED_ONE = 1;
196200
DEPRECATED_TWO = 2;

run_test.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ MYPY_PROTOBUF_VENV=venv_$PY_VER_MYPY_PROTOBUF
130130
find proto -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_out=quiet:test/generated
131131
find proto -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_out=readable_stubs:test/generated
132132
find proto -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_out=relax_strict_optional_primitives:test/generated
133+
find proto -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_out=use_default_deprecation_warnings:test/generated
133134
# Overwrite w/ run with mypy-protobuf without flags
134135
find proto -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_out=test/generated
135136

test/generated/testproto/grpc/dummy_pb2_grpc.pyi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ _DeprecatedServiceDeprecatedMethodNotDeprecatedRequestType = typing_extensions.T
237237
],
238238
)
239239

240-
@deprecated("This stub has been marked as deprecated using proto service options.")
240+
@deprecated("""This service is deprecated""")
241241
class DeprecatedServiceStub(typing.Generic[_DeprecatedServiceDeprecatedMethodType, _DeprecatedServiceDeprecatedMethodNotDeprecatedRequestType]):
242242
"""Marking the service as deprecated"""
243243

@@ -282,7 +282,7 @@ DeprecatedServiceAsyncStub: typing_extensions.TypeAlias = DeprecatedServiceStub[
282282
],
283283
]
284284

285-
@deprecated("This servicer has been marked as deprecated using proto service options.")
285+
@deprecated("""This service is deprecated""")
286286
class DeprecatedServiceServicer(metaclass=abc.ABCMeta):
287287
"""Marking the service as deprecated"""
288288

@@ -302,5 +302,5 @@ class DeprecatedServiceServicer(metaclass=abc.ABCMeta):
302302
) -> typing.Union[testproto.grpc.dummy_pb2.DummyReply, collections.abc.Awaitable[testproto.grpc.dummy_pb2.DummyReply]]:
303303
"""DeprecatedMethodNotDeprecatedRequest"""
304304

305-
@deprecated("This servicer has been marked as deprecated using proto service options.")
305+
@deprecated("""This service is deprecated""")
306306
def add_DeprecatedServiceServicer_to_server(servicer: DeprecatedServiceServicer, server: typing.Union[grpc.Server, grpc.aio.Server]) -> None: ...

test/generated/testproto/test_pb2.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class _DeprecatedEnumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper.
8383
DEPRECATED_ONE: _DeprecatedEnum.ValueType # 1
8484
DEPRECATED_TWO: _DeprecatedEnum.ValueType # 2
8585

86-
@deprecated("This enum has been marked as deprecated using proto enum options.")
86+
@deprecated("""This enum is deprecated\n2 lines of comments\n"Quotes in comments"\nand 'single quotes'\nTrailing comment""")
8787
class DeprecatedEnum(_DeprecatedEnum, metaclass=_DeprecatedEnumEnumTypeWrapper): ...
8888

8989
DEPRECATED_ONE: DeprecatedEnum.ValueType # 1
@@ -407,7 +407,7 @@ class SelfField(google.protobuf.message.Message):
407407

408408
Global___SelfField: typing_extensions.TypeAlias = SelfField
409409

410-
@deprecated("This message has been marked as deprecated using proto message options.")
410+
@deprecated("""This message is deprecated""")
411411
@typing.final
412412
class DeprecatedMessage(google.protobuf.message.Message):
413413
DESCRIPTOR: google.protobuf.descriptor.Descriptor

test_negative/output.expected.3.13

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,26 @@ test/generated/testproto/grpc/dummy_pb2_grpc.pyi:215: note: class testproto.grpc
44
test/generated/testproto/grpc/dummy_pb2_grpc.pyi:219: note: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
55
test/generated/testproto/grpc/dummy_pb2_grpc.pyi:245: note: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
66
test/generated/testproto/grpc/dummy_pb2_grpc.pyi:257: note: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
7-
test/generated/testproto/grpc/dummy_pb2_grpc.pyi:274: note: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub is deprecated: This stub has been marked as deprecated using proto service options. [deprecated]
7+
test/generated/testproto/grpc/dummy_pb2_grpc.pyi:274: note: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub is deprecated: This service is deprecated [deprecated]
88
test/generated/testproto/grpc/dummy_pb2_grpc.pyi:276: note: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
99
test/generated/testproto/grpc/dummy_pb2_grpc.pyi:292: note: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
10-
test/generated/testproto/grpc/dummy_pb2_grpc.pyi:306: note: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceServicer is deprecated: This servicer has been marked as deprecated using proto service options. [deprecated]
11-
test/generated/testproto/test_pb2.pyi:91: note: class testproto.test_pb2.DeprecatedEnum is deprecated: This enum has been marked as deprecated using proto enum options. [deprecated]
12-
test/generated/testproto/test_pb2.pyi:425: note: class testproto.test_pb2.DeprecatedMessage is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
10+
test/generated/testproto/grpc/dummy_pb2_grpc.pyi:306: note: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceServicer is deprecated: This service is deprecated [deprecated]
11+
test/generated/testproto/test_pb2.pyi:91: note: class testproto.test_pb2.DeprecatedEnum is deprecated: This enum is deprecated
12+
2 lines of comments
13+
"Quotes in comments"
14+
and 'single quotes'
15+
Trailing comment [deprecated]
16+
test/generated/testproto/test_pb2.pyi:425: note: class testproto.test_pb2.DeprecatedMessage is deprecated: This message is deprecated [deprecated]
1317
test_negative/negative.py:14: note: class testproto.grpc.dummy_pb2.DeprecatedRequest is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
14-
test_negative/negative.py:19: note: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceServicer is deprecated: This servicer has been marked as deprecated using proto service options. [deprecated]
15-
test_negative/negative.py:19: note: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub is deprecated: This stub has been marked as deprecated using proto service options. [deprecated]
16-
test_negative/negative.py:19: note: function testproto.grpc.dummy_pb2_grpc.add_DeprecatedServiceServicer_to_server is deprecated: This servicer has been marked as deprecated using proto service options. [deprecated]
17-
test_negative/negative.py:29: note: class testproto.test_pb2.DeprecatedEnum is deprecated: This enum has been marked as deprecated using proto enum options. [deprecated]
18-
test_negative/negative.py:30: note: class testproto.test_pb2.DeprecatedMessage is deprecated: This message has been marked as deprecated using proto message options. [deprecated]
18+
test_negative/negative.py:19: note: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceServicer is deprecated: This service is deprecated [deprecated]
19+
test_negative/negative.py:19: note: class testproto.grpc.dummy_pb2_grpc.DeprecatedServiceStub is deprecated: This service is deprecated [deprecated]
20+
test_negative/negative.py:19: note: function testproto.grpc.dummy_pb2_grpc.add_DeprecatedServiceServicer_to_server is deprecated: This service is deprecated [deprecated]
21+
test_negative/negative.py:29: note: class testproto.test_pb2.DeprecatedEnum is deprecated: This enum is deprecated
22+
2 lines of comments
23+
"Quotes in comments"
24+
and 'single quotes'
25+
Trailing comment [deprecated]
26+
test_negative/negative.py:30: note: class testproto.test_pb2.DeprecatedMessage is deprecated: This message is deprecated [deprecated]
1927
test_negative/negative.py:44: error: "Simple1" has no attribute "SerializeToStringg" [attr-defined]
2028
test_negative/negative.py:47: error: Argument 1 to "ParseFromString" of "Message" has incompatible type "Simple1"; expected "bytes" [arg-type]
2129
test_negative/negative.py:50: error: Argument 1 to "CopyFrom" of "Message" has incompatible type "bytes"; expected "Simple1" [arg-type]

0 commit comments

Comments
 (0)