Skip to content

Commit db0c2ea

Browse files
authored
Mark Messages as @final (#402)
* Bring back stubtest in run_test.sh * Add final decorator * Fix annotation * Some stubtest allowlist fixes * Fix stubtest allowlist * Update changelog * Switch to typing_extensions for final
1 parent f19c0ae commit db0c2ea

21 files changed

+52
-4
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Upcoming
22

3+
- Mark messages as @typing.final
4+
35
## 3.3.0
46

57
- Prefer (mypy_protobuf.options).casttype to (mypy_protobuf.casttype)

mypy_protobuf/extensions_pb2.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ else:
1616

1717
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1818

19+
@typing_extensions.final
1920
class FieldOptions(google.protobuf.message.Message):
2021
DESCRIPTOR: google.protobuf.descriptor.Descriptor
2122

mypy_protobuf/main.py

+2
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ def _import(self, path: str, name: str) -> str:
171171
stabilization = {
172172
"Literal": (3, 8),
173173
"TypeAlias": (3, 10),
174+
"final": (3, 8),
174175
}
175176
assert name in stabilization
176177
if not self.typing_extensions_min or self.typing_extensions_min < stabilization[name]:
@@ -406,6 +407,7 @@ def write_messages(
406407

407408
class_name = desc.name if desc.name not in PYTHON_RESERVED else "_r_" + desc.name
408409
message_class = self._import("google.protobuf.message", "Message")
410+
wl("@{}", self._import("typing_extensions", "final"))
409411
wl(f"class {class_name}({message_class}{addl_base}):")
410412
with self._indent():
411413
scl = scl_prefix + [i]

run_test.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ for PY_VER in $PY_VER_UNIT_TESTS; do
152152
# Run mypy
153153
FILES=( "test/" )
154154
mypy --custom-typeshed-dir="$CUSTOM_TYPESHED_DIR" --python-executable=$UNIT_TESTS_VENV/bin/python --python-version="$PY_VER_MYPY_TARGET" "${FILES[@]}"
155-
# PYTHONPATH=test/generated MYPYPATH=$MYPYPATH:test/generated python3 -m mypy.stubtest --custom-typeshed-dir=$CUSTOM_TYPESHED_DIR --allowlist stubtest_allowlist.txt testproto
155+
PYTHONPATH=test/generated MYPYPATH=$MYPYPATH:test/generated python3 -m mypy.stubtest --custom-typeshed-dir="$CUSTOM_TYPESHED_DIR" --allowlist stubtest_allowlist.txt testproto
156156

157157
# run mypy on negative-tests (expected mypy failures)
158158
NEGATIVE_FILES=( test_negative/negative.py test_negative/negative_3.8.py "${FILES[@]}" )

stubtest_allowlist.txt

+6-3
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@ testproto.readme_enum_pb2._?MyEnum(EnumTypeWrapper)?
4848
testproto.nested.nested_pb2.AnotherNested._?NestedEnum(EnumTypeWrapper)?
4949
testproto.nested.nested_pb2.AnotherNested.NestedMessage._?NestedEnum2(EnumTypeWrapper)?
5050

51-
# Some issues with our service stubs for now
52-
testproto.test_pb2.ATestService.*
53-
5451
# Part of an "EXPERIMENTAL API" according to comment. Not documented.
5552
testproto.grpc.dummy_pb2_grpc.DummyService
5653
testproto.grpc.import_pb2_grpc.SimpleService
54+
55+
# global prefix globals are generated, but aren't used at runtime
56+
.*_pb2\.global___.*
57+
58+
# Until we update typeshed, this will fail
59+
testproto.reexport_pb2.Empty

test/generated/google/protobuf/duration_pb2.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ else:
4444

4545
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
4646

47+
@typing_extensions.final
4748
class Duration(google.protobuf.message.Message, google.protobuf.internal.well_known_types.Duration):
4849
"""A Duration represents a signed, fixed-length span of time represented
4950
as a count of seconds and fractions of seconds at nanosecond

test/generated/mypy_protobuf/extensions_pb2.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ else:
1616

1717
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1818

19+
@typing_extensions.final
1920
class FieldOptions(google.protobuf.message.Message):
2021
DESCRIPTOR: google.protobuf.descriptor.Descriptor
2122

test/generated/testproto/Capitalized/Capitalized_pb2.pyi

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ else:
1414

1515
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1616

17+
@typing_extensions.final
1718
class lower(google.protobuf.message.Message):
1819
DESCRIPTOR: google.protobuf.descriptor.Descriptor
1920

@@ -28,6 +29,7 @@ class lower(google.protobuf.message.Message):
2829

2930
global___lower = lower
3031

32+
@typing_extensions.final
3133
class Upper(google.protobuf.message.Message):
3234
DESCRIPTOR: google.protobuf.descriptor.Descriptor
3335

@@ -44,6 +46,7 @@ class Upper(google.protobuf.message.Message):
4446

4547
global___Upper = Upper
4648

49+
@typing_extensions.final
4750
class lower2(google.protobuf.message.Message):
4851
DESCRIPTOR: google.protobuf.descriptor.Descriptor
4952

test/generated/testproto/__pycache__/__init__.py

Whitespace-only changes.

test/generated/testproto/comment_special_chars_pb2.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ else:
1414

1515
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1616

17+
@typing_extensions.final
1718
class Test(google.protobuf.message.Message):
1819
DESCRIPTOR: google.protobuf.descriptor.Descriptor
1920

test/generated/testproto/dot/com/__pycache__/__init__.py

Whitespace-only changes.

test/generated/testproto/dot/com/test_pb2.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ else:
1414

1515
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1616

17+
@typing_extensions.final
1718
class TestMessage(google.protobuf.message.Message):
1819
DESCRIPTOR: google.protobuf.descriptor.Descriptor
1920

test/generated/testproto/grpc/dummy_pb2.pyi

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ else:
1414

1515
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1616

17+
@typing_extensions.final
1718
class DummyRequest(google.protobuf.message.Message):
1819
DESCRIPTOR: google.protobuf.descriptor.Descriptor
1920

@@ -28,6 +29,7 @@ class DummyRequest(google.protobuf.message.Message):
2829

2930
global___DummyRequest = DummyRequest
3031

32+
@typing_extensions.final
3133
class DummyReply(google.protobuf.message.Message):
3234
DESCRIPTOR: google.protobuf.descriptor.Descriptor
3335

test/generated/testproto/inner/inner_pb2.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ else:
1515

1616
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1717

18+
@typing_extensions.final
1819
class Inner(google.protobuf.message.Message):
1920
DESCRIPTOR: google.protobuf.descriptor.Descriptor
2021

test/generated/testproto/nested/nested_pb2.pyi

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ else:
1717

1818
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1919

20+
@typing_extensions.final
2021
class Nested(google.protobuf.message.Message):
2122
DESCRIPTOR: google.protobuf.descriptor.Descriptor
2223

@@ -31,6 +32,7 @@ class Nested(google.protobuf.message.Message):
3132

3233
global___Nested = Nested
3334

35+
@typing_extensions.final
3436
class AnotherNested(google.protobuf.message.Message):
3537
DESCRIPTOR: google.protobuf.descriptor.Descriptor
3638

@@ -49,6 +51,7 @@ class AnotherNested(google.protobuf.message.Message):
4951
ONE: AnotherNested.NestedEnum.ValueType # 1
5052
TWO: AnotherNested.NestedEnum.ValueType # 2
5153

54+
@typing_extensions.final
5255
class NestedMessage(google.protobuf.message.Message):
5356
DESCRIPTOR: google.protobuf.descriptor.Descriptor
5457

test/generated/testproto/nopackage_pb2.pyi

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ else:
1616

1717
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1818

19+
@typing_extensions.final
1920
class NoPackage(google.protobuf.message.Message):
2021
"""Intentionally don't set a package - just to make sure we can handle it."""
2122

@@ -27,6 +28,7 @@ class NoPackage(google.protobuf.message.Message):
2728

2829
global___NoPackage = NoPackage
2930

31+
@typing_extensions.final
3032
class NoPackage2(google.protobuf.message.Message):
3133
DESCRIPTOR: google.protobuf.descriptor.Descriptor
3234

test/generated/testproto/test3_pb2.pyi

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ FOO3: OuterEnum.ValueType # 1
3636
BAR3: OuterEnum.ValueType # 2
3737
global___OuterEnum = OuterEnum
3838

39+
@typing_extensions.final
3940
class OuterMessage3(google.protobuf.message.Message):
4041
DESCRIPTOR: google.protobuf.descriptor.Descriptor
4142

@@ -50,6 +51,7 @@ class OuterMessage3(google.protobuf.message.Message):
5051

5152
global___OuterMessage3 = OuterMessage3
5253

54+
@typing_extensions.final
5355
class SimpleProto3(google.protobuf.message.Message):
5456
DESCRIPTOR: google.protobuf.descriptor.Descriptor
5557

@@ -66,6 +68,7 @@ class SimpleProto3(google.protobuf.message.Message):
6668
INNER1: SimpleProto3.InnerEnum.ValueType # 0
6769
INNER2: SimpleProto3.InnerEnum.ValueType # 1
6870

71+
@typing_extensions.final
6972
class MapScalarEntry(google.protobuf.message.Message):
7073
DESCRIPTOR: google.protobuf.descriptor.Descriptor
7174

@@ -81,6 +84,7 @@ class SimpleProto3(google.protobuf.message.Message):
8184
) -> None: ...
8285
def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ...
8386

87+
@typing_extensions.final
8488
class MapMessageEntry(google.protobuf.message.Message):
8589
DESCRIPTOR: google.protobuf.descriptor.Descriptor
8690

@@ -98,6 +102,7 @@ class SimpleProto3(google.protobuf.message.Message):
98102
def HasField(self, field_name: typing_extensions.Literal["value", b"value"]) -> builtins.bool: ...
99103
def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ...
100104

105+
@typing_extensions.final
101106
class EmailByUidEntry(google.protobuf.message.Message):
102107
DESCRIPTOR: google.protobuf.descriptor.Descriptor
103108

test/generated/testproto/test_extensions2_pb2.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ else:
1616

1717
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1818

19+
@typing_extensions.final
1920
class SeparateFileExtension(google.protobuf.message.Message):
2021
DESCRIPTOR: google.protobuf.descriptor.Descriptor
2122

test/generated/testproto/test_extensions3_pb2.pyi

+7
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,17 @@ import google.protobuf.descriptor_pb2
88
import google.protobuf.internal.containers
99
import google.protobuf.internal.extension_dict
1010
import google.protobuf.message
11+
import sys
1112
import testproto.test3_pb2
1213

14+
if sys.version_info >= (3, 8):
15+
import typing as typing_extensions
16+
else:
17+
import typing_extensions
18+
1319
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1420

21+
@typing_extensions.final
1522
class MessageOptionsTestMsg(google.protobuf.message.Message):
1623
DESCRIPTOR: google.protobuf.descriptor.Descriptor
1724

test/generated/testproto/test_no_generic_services_pb2.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ else:
1414

1515
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
1616

17+
@typing_extensions.final
1718
class Simple3(google.protobuf.message.Message):
1819
DESCRIPTOR: google.protobuf.descriptor.Descriptor
1920

test/generated/testproto/test_pb2.pyi

+11
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ DESCRIPTOR = 8;
6868
"""
6969
global___NamingConflicts = NamingConflicts
7070

71+
@typing_extensions.final
7172
class Simple1(google.protobuf.message.Message):
7273
"""Message with one of everything"""
7374

@@ -92,13 +93,15 @@ class Simple1(google.protobuf.message.Message):
9293
INNER2: Simple1.InnerEnum.ValueType # 2
9394
"""INNER2"""
9495

96+
@typing_extensions.final
9597
class InnerMessage(google.protobuf.message.Message):
9698
DESCRIPTOR: google.protobuf.descriptor.Descriptor
9799

98100
def __init__(
99101
self,
100102
) -> None: ...
101103

104+
@typing_extensions.final
102105
class EmailByUidEntry(google.protobuf.message.Message):
103106
DESCRIPTOR: google.protobuf.descriptor.Descriptor
104107

@@ -204,6 +207,7 @@ class Simple1(google.protobuf.message.Message):
204207

205208
global___Simple1 = Simple1
206209

210+
@typing_extensions.final
207211
class Simple2(google.protobuf.message.Message):
208212
DESCRIPTOR: google.protobuf.descriptor.Descriptor
209213

@@ -219,6 +223,7 @@ class Simple2(google.protobuf.message.Message):
219223

220224
global___Simple2 = Simple2
221225

226+
@typing_extensions.final
222227
class Extensions1(google.protobuf.message.Message):
223228
DESCRIPTOR: google.protobuf.descriptor.Descriptor
224229

@@ -237,6 +242,7 @@ class Extensions1(google.protobuf.message.Message):
237242

238243
global___Extensions1 = Extensions1
239244

245+
@typing_extensions.final
240246
class Extensions2(google.protobuf.message.Message):
241247
DESCRIPTOR: google.protobuf.descriptor.Descriptor
242248

@@ -255,6 +261,7 @@ class Extensions2(google.protobuf.message.Message):
255261

256262
global___Extensions2 = Extensions2
257263

264+
@typing_extensions.final
258265
class _r_None(google.protobuf.message.Message):
259266
DESCRIPTOR: google.protobuf.descriptor.Descriptor
260267

@@ -270,6 +277,7 @@ class _r_None(google.protobuf.message.Message):
270277

271278
global____r_None = _r_None
272279

280+
@typing_extensions.final
273281
class PythonReservedKeywords(google.protobuf.message.Message):
274282
DESCRIPTOR: google.protobuf.descriptor.Descriptor
275283

@@ -284,6 +292,7 @@ class PythonReservedKeywords(google.protobuf.message.Message):
284292
class _r_finally(_finally, metaclass=_finallyEnumTypeWrapper): ...
285293
valid_in_finally: PythonReservedKeywords._r_finally.ValueType # 2
286294

295+
@typing_extensions.final
287296
class _r_lambda(google.protobuf.message.Message):
288297
DESCRIPTOR: google.protobuf.descriptor.Descriptor
289298

@@ -343,6 +352,7 @@ class PythonReservedKeywords(google.protobuf.message.Message):
343352

344353
global___PythonReservedKeywords = PythonReservedKeywords
345354

355+
@typing_extensions.final
346356
class PythonReservedKeywordsSmall(google.protobuf.message.Message):
347357
"""Do one with just one arg - to make sure it's syntactically correct"""
348358

@@ -357,6 +367,7 @@ class PythonReservedKeywordsSmall(google.protobuf.message.Message):
357367

358368
global___PythonReservedKeywordsSmall = PythonReservedKeywordsSmall
359369

370+
@typing_extensions.final
360371
class SelfField(google.protobuf.message.Message):
361372
DESCRIPTOR: google.protobuf.descriptor.Descriptor
362373

0 commit comments

Comments
 (0)