@@ -15,24 +15,24 @@ See [Changelog](CHANGELOG.md) for recent changes.
1515
1616Earlier releases might work, but aren't tested
1717
18- - [ protoc >= 23.4 ] ( https://github.com/protocolbuffers/protobuf/releases )
19- - [ python-protobuf >= 5.28.2 ] ( https://pypi.org/project/protobuf/ ) - matching protoc release
20- - [ python >= 3.8 ] ( https://www.python.org/downloads/source/ ) - for running mypy-protobuf plugin.
18+ - [ protoc >= 32.0 ] ( https://github.com/protocolbuffers/protobuf/releases )
19+ - [ python-protobuf >= 6.32 ] ( https://pypi.org/project/protobuf/ ) - matching protoc release
20+ - [ python >= 3.9 ] ( https://www.python.org/downloads/source/ ) - for running mypy-protobuf plugin.
2121
2222## Requirements to run typecheckers on stubs generated by mypy-protobuf
2323
2424Earlier releases might work, but aren't tested
2525
26- - [ mypy >= v1.11.2 ] ( https://pypi.org/project/mypy ) or [ pyright >= 1.1.383] ( https://github.com/microsoft/pyright )
27- - [ python-protobuf >= 5.28.2 ] ( https://pypi.org/project/protobuf/ ) - matching protoc release
28- - [ types-protobuf >= 5.28 ] ( https://pypi.org/project/types-protobuf/ ) - for stubs from the google.protobuf library
26+ - [ mypy >= v1.14.0 ] ( https://pypi.org/project/mypy ) or [ pyright >= 1.1.383] ( https://github.com/microsoft/pyright )
27+ - [ python-protobuf >= 6.32 ] ( https://pypi.org/project/protobuf/ ) - matching protoc release
28+ - [ types-protobuf >= 6.32 ] ( https://pypi.org/project/types-protobuf/ ) - for stubs from the google.protobuf library
2929
3030### To run typecheckers on code generated with grpc plugin - you'll additionally need
3131
3232Earlier releases might work, but aren't tested
3333
34- - [ grpcio>=1.66.2 ] ( https://pypi.org/project/grpcio/ )
35- - [ grpcio-tools>=1.66.2 ] ( https://pypi.org/project/grpcio-tools/ )
34+ - [ grpcio>=1.70 ] ( https://pypi.org/project/grpcio/ )
35+ - [ grpcio-tools>=1.70 ] ( https://pypi.org/project/grpcio-tools/ )
3636- [ grpc-stubs>=1.53.0.5] ( https://pypi.org/project/grpc-stubs/ )
3737
3838Other configurations may work, but are not continuously tested currently.
@@ -92,6 +92,117 @@ an executable protoc-gen-mypy. On windows it installs to `protoc-gen-mypy.exe`
9292
9393See [ Changelog] ( CHANGELOG.md ) for full listing
9494
95+ ### Differences between the protobuf compiler (` --pyi_out ` ) and ` mypy-protobuf `
96+
97+ (As of 11/17/2025)
98+
99+ * ` mypy-protobuf ` generates stubs for GRPC services and clients
100+ * ` mypy-protobuf ` generates correctly typed ` HasField ` methods depending on field presence, ` pyi_out ` does not type ` HasField ` arguments
101+ * ` mypy-protobuf ` generates correctly typed constructors dependinding on field presence.
102+ * ` mypy-protobuf ` generates correctly typed ` HasField ` , ` WhichOneof ` , and ` ClearField ` methods.
103+ * There are differences in how ` mypy-protobuf ` and ` pyi_out ` generate enums. See [ this issue] ( https://github.com/protocolbuffers/protobuf/issues/8175 ) for details
104+
105+ #### Examples
106+
107+ ` mypy-protobuf ` :
108+
109+ ``` python
110+ import builtins
111+ import google.protobuf.descriptor
112+ import google.protobuf.message
113+ import sys
114+ import typing
115+
116+ if sys.version_info >= (3 , 10 ):
117+ import typing as typing_extensions
118+ else :
119+ import typing_extensions
120+
121+ DESCRIPTOR : google.protobuf.descriptor.FileDescriptor
122+
123+ @typing.final
124+ class Editions2024SubMessage (google .protobuf .message .Message ):
125+ DESCRIPTOR : google.protobuf.descriptor.Descriptor
126+
127+ THING_FIELD_NUMBER : builtins.int
128+ thing: builtins.str
129+ def __init__ (
130+ self ,
131+ * ,
132+ thing : builtins.str | None = ... ,
133+ ) -> None : ...
134+ def HasField (self , field_name : typing.Literal[" thing" , b " thing" ]) -> builtins.bool: ...
135+ def ClearField (self , field_name : typing.Literal[" thing" , b " thing" ]) -> None : ...
136+
137+ Global___Editions2024SubMessage: typing_extensions.TypeAlias = Editions2024SubMessage
138+
139+ @typing.final
140+ class Editions2024Test (google .protobuf .message .Message ):
141+ DESCRIPTOR : google.protobuf.descriptor.Descriptor
142+
143+ LEGACY_FIELD_NUMBER : builtins.int
144+ EXPLICIT_SINGULAR_FIELD_NUMBER : builtins.int
145+ MESSAGE_FIELD_FIELD_NUMBER : builtins.int
146+ IMPLICIT_SINGULAR_FIELD_NUMBER : builtins.int
147+ DEFAULT_SINGULAR_FIELD_NUMBER : builtins.int
148+ legacy: builtins.str
149+ """ Expect to be always set"""
150+ explicit_singular: builtins.str
151+ """ Expect HasField generated"""
152+ implicit_singular: builtins.str
153+ """ Expect implicit field presence, no HasField generated"""
154+ default_singular: builtins.str
155+ """ Not set, should default to EXPLICIT"""
156+ @ property
157+ def message_field (self ) -> Global___Editions2024SubMessage:
158+ """ Expect HasField generated?"""
159+
160+ def __init__ (
161+ self ,
162+ * ,
163+ legacy : builtins.str | None = ... ,
164+ explicit_singular : builtins.str | None = ... ,
165+ message_field : Global___Editions2024SubMessage | None = ... ,
166+ implicit_singular : builtins.str = ... ,
167+ default_singular : builtins.str | None = ... ,
168+ ) -> None : ...
169+ def HasField (self , field_name : typing.Literal[" default_singular" , b " default_singular" , " explicit_singular" , b " explicit_singular" , " legacy" , b " legacy" , " message_field" , b " message_field" ]) -> builtins.bool: ...
170+ def ClearField (self , field_name : typing.Literal[" default_singular" , b " default_singular" , " explicit_singular" , b " explicit_singular" , " implicit_singular" , b " implicit_singular" , " legacy" , b " legacy" , " message_field" , b " message_field" ]) -> None : ...
171+
172+ Global___Editions2024Test: typing_extensions.TypeAlias = Editions2024Test
173+ ```
174+
175+ Builtin pyi generator:
176+
177+ ``` python
178+ from google.protobuf import descriptor as _descriptor
179+ from google.protobuf import message as _message
180+ from collections.abc import Mapping as _Mapping
181+ from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union
182+
183+ DESCRIPTOR : _descriptor.FileDescriptor
184+
185+ class Editions2024SubMessage (_message .Message ):
186+ __slots__ = ()
187+ THING_FIELD_NUMBER : _ClassVar[int ]
188+ thing: str
189+ def __init__ (self , thing : _Optional[str ] = ... ) -> None : ...
190+
191+ class Editions2024Test (_message .Message ):
192+ __slots__ = ()
193+ LEGACY_FIELD_NUMBER : _ClassVar[int ]
194+ EXPLICIT_SINGULAR_FIELD_NUMBER : _ClassVar[int ]
195+ MESSAGE_FIELD_FIELD_NUMBER : _ClassVar[int ]
196+ IMPLICIT_SINGULAR_FIELD_NUMBER : _ClassVar[int ]
197+ DEFAULT_SINGULAR_FIELD_NUMBER : _ClassVar[int ]
198+ legacy: str
199+ explicit_singular: str
200+ message_field: Editions2024SubMessage
201+ implicit_singular: str
202+ default_singular: str
203+ def __init__ (self , legacy : _Optional[str ] = ... , explicit_singular : _Optional[str ] = ... , message_field : _Optional[_Union[Editions2024SubMessage, _Mapping]] = ... , implicit_singular : _Optional[str ] = ... , default_singular : _Optional[str ] = ... ) -> None : ...
204+ ```
205+
95206### Bring comments from .proto files to docstrings in .pyi files
96207
97208Comments in the .proto files on messages, fields, enums, enum variants, extensions, services, and methods
0 commit comments