├── .flake8 ├── .gitattributes ├── .github ├── dependabot.yml ├── package.json └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── mypy_protobuf ├── __init__.py ├── extensions_pb2.py ├── extensions_pb2.pyi ├── main.py └── protoc_gen_mypy.bat ├── mypy_requirements.txt ├── proto ├── google │ └── protobuf │ │ └── duration.proto ├── mypy_protobuf │ └── extensions.proto └── testproto │ ├── Capitalized │ └── Capitalized.proto │ ├── comment_special_chars.proto │ ├── dot.com │ └── test.proto │ ├── grpc │ ├── dummy.proto │ └── import.proto │ ├── inner │ └── inner.proto │ ├── nested │ └── nested.proto │ ├── nopackage.proto │ ├── readme_enum.proto │ ├── reexport.proto │ ├── test.proto │ ├── test3.proto │ ├── test_extensions2.proto │ ├── test_extensions3.proto │ └── test_no_generic_services.proto ├── pyproject.toml ├── run_test.sh ├── setup.cfg ├── stubtest_allowlist.txt ├── test ├── __init__.py ├── generated │ ├── google │ │ └── protobuf │ │ │ └── duration_pb2.pyi │ ├── mypy_protobuf │ │ ├── __init__.py │ │ └── extensions_pb2.pyi │ └── testproto │ │ ├── Capitalized │ │ ├── Capitalized_pb2.pyi │ │ └── __init__.py │ │ ├── __init__.py │ │ ├── comment_special_chars_pb2.pyi │ │ ├── dot │ │ ├── __init__.py │ │ └── com │ │ │ ├── __init__.py │ │ │ └── test_pb2.pyi │ │ ├── grpc │ │ ├── __init__.py │ │ ├── dummy_pb2.pyi │ │ ├── dummy_pb2_grpc.pyi │ │ ├── import_pb2.pyi │ │ └── import_pb2_grpc.pyi │ │ ├── inner │ │ ├── __init__.py │ │ └── inner_pb2.pyi │ │ ├── nested │ │ ├── __init__.py │ │ └── nested_pb2.pyi │ │ ├── nopackage_pb2.pyi │ │ ├── readme_enum_pb2.pyi │ │ ├── reexport_pb2.pyi │ │ ├── test3_pb2.pyi │ │ ├── test_extensions2_pb2.pyi │ │ ├── test_extensions3_pb2.pyi │ │ ├── test_no_generic_services_pb2.pyi │ │ └── test_pb2.pyi ├── test_generated_mypy.py ├── test_grpc_async_usage.py └── test_grpc_usage.py ├── test_negative ├── __init__.py ├── negative.py ├── output.expected.3.8 └── output.expected.3.8.omit_linenos └── test_requirements.txt /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | per-file-ignores = 3 | *.py: E203, E301, E302, E305, E501 4 | *.pyi: E301, E302, E305, E501, E701, E741, F401, F403, F405, F822, Y037 5 | *_pb2.pyi: E301, E302, E305, E501, E701, E741, F401, F403, F405, F822, Y037, Y021 6 | *_pb2_grpc.pyi: E301, E302, E305, E501, E701, E741, F401, F403, F405, F822, Y037, Y021, Y023 7 | 8 | extend_exclude = venv*,*_pb2.py,*_pb2_grpc.py,build/ 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | test_negative/output.expected.2.7 linguist-generated=true 2 | test_negative/output.expected.3.8 linguist-generated=true 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | - package-ecosystem: "npm" 13 | directory: ".github" 14 | schedule: 15 | interval: "daily" 16 | - package-ecosystem: "github-actions" 17 | directory: "/" 18 | schedule: 19 | interval: "daily" 20 | -------------------------------------------------------------------------------- /.github/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fake_package_json_for_github_action_to_read_version", 3 | "private": true, 4 | "devDependencies": { 5 | "pyright": "1.1.385" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Run on git push, PR, or manually from the Actions tab 4 | on: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | run_test: 12 | name: ${{ matrix.py-ver-mypy-protobuf }} 13 | runs-on: ubuntu-20.04 14 | # Some CI issues regarding ubuntu-latest 15 | # runs-on: ubuntu-latest 16 | env: 17 | PY_VER_MYPY: 3.8.17 18 | PY_VER_UNIT_TESTS_3: 3.8.17 19 | strategy: 20 | matrix: 21 | # Running mypy-protobuf itself 22 | py-ver-mypy-protobuf: [3.8.17, 3.9.17, 3.10.12, 3.11.4] 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Read version numbers 26 | run: | 27 | echo ::set-output name=PROTOBUF_VERSION::$(grep "^protobuf>=" test_requirements.txt | cut -f2 -d=) 28 | echo ::set-output name=PYRIGHT_VERSION::$(grep '"pyright"' .github/package.json | cut -d\" -f4) 29 | id: read_versions 30 | - name: Cache pyenv 31 | uses: actions/cache@v4 32 | with: 33 | path: | 34 | ~/.pyenv 35 | !~/.pyenv/versions 36 | key: pyenv-installation-2 37 | - name: Cache pyenv mypy-protobuf ver 38 | uses: actions/cache@v4 39 | with: 40 | path: ~/.pyenv/versions/${{matrix.py-ver-mypy-protobuf}} 41 | key: pyenv-${{matrix.py-ver-mypy-protobuf}}-${{hashFiles('setup.py')}} 42 | - name: Cache pyenv unit tests 3 ver 43 | uses: actions/cache@v4 44 | with: 45 | path: ~/.pyenv/versions/${{env.PY_VER_UNIT_TESTS_3}} 46 | key: pyenv-${{env.PY_VER_UNIT_TESTS_3}}-${{hashFiles('setup.py')}} 47 | - name: Cache pyenv mypy ver 48 | uses: actions/cache@v4 49 | with: 50 | path: ~/.pyenv/versions/${{env.PY_VER_MYPY}} 51 | key: pyenv-${{env.PY_VER_MYPY}}-${{hashFiles('setup.py')}} 52 | - name: Install pyenv 53 | run: | 54 | if [ ! -e ~/.pyenv/bin/pyenv ]; then 55 | rm -rf ~/.pyenv 56 | curl https://pyenv.run | bash 57 | fi 58 | 59 | export PATH="$HOME/.pyenv/bin:$PATH" 60 | eval "$(pyenv init -)" 61 | eval "$(pyenv init --path)" 62 | eval "$(pyenv virtualenv-init -)" 63 | 64 | for PY in ${{matrix.py-ver-mypy-protobuf}} ${{env.PY_VER_MYPY}} ${{env.PY_VER_UNIT_TESTS_3}}; do 65 | if [ ! -e ~/.pyenv/versions/$PY ]; then 66 | pyenv install --skip-existing $PY 67 | pyenv shell $PY 68 | python -m pip install virtualenv 69 | fi 70 | done 71 | - name: Run Tests (./run_test.sh) 72 | env: 73 | PY_VER_MYPY_PROTOBUF: ${{matrix.py-ver-mypy-protobuf}} 74 | VALIDATE: 1 75 | run: | 76 | export PATH="$HOME/.pyenv/bin:$PATH" 77 | eval "$(pyenv init -)" 78 | eval "$(pyenv init --path)" 79 | eval "$(pyenv virtualenv-init -)" 80 | ./run_test.sh 81 | 82 | - name: Run Pyright 83 | uses: jakebailey/pyright-action@v2 84 | with: 85 | version: "${{ steps.read_versions.outputs.PYRIGHT_VERSION }}" 86 | 87 | linting: 88 | name: Linting 89 | runs-on: ubuntu-latest 90 | steps: 91 | - uses: actions/checkout@v4 92 | - uses: actions/setup-python@v5 93 | with: 94 | python-version: "3.8" 95 | - name: Run formatters and linters 96 | run: | 97 | pip3 install black==24.3.0 isort flake8 flake8-pyi flake8-noqa flake8-bugbear 98 | black --check --extend-exclude '(_pb2_grpc|_pb2).pyi?$' . 99 | isort --check . --diff 100 | flake8 . 101 | - name: run shellcheck 102 | uses: reviewdog/action-shellcheck@v1 103 | with: 104 | # By default, shellcheck tries to make sure any external files referenced actually exist 105 | shellcheck_flags: -e SC1091 106 | 107 | sanity_check_windows: 108 | name: Sanity Check Windows Executable 109 | runs-on: windows-latest 110 | steps: 111 | - uses: actions/checkout@v4 112 | - uses: actions/setup-python@v5 113 | with: 114 | python-version: "3.8" 115 | - name: Read versions 116 | run: echo ::set-output name=PROTOBUF_VERSION::$(grep "^protobuf>=" test_requirements.txt | cut -f2 -d=) 117 | id: read_versions 118 | - name: Install Protoc 119 | uses: arduino/setup-protoc@v3 120 | with: 121 | version: "${{ steps.read_versions.outputs.PROTOBUF_VERSION }}" 122 | repo-token: ${{ secrets.GITHUB_TOKEN }} 123 | - name: Run Protoc 124 | run: | 125 | pip3 install -e . 126 | mkdir wintestout 127 | protoc --python_out=wintestout --mypy_out=wintestout proto\mypy_protobuf\extensions.proto 128 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /main 2 | .*.swp 3 | __pycache__/ 4 | .cache 5 | .mypy_cache/ 6 | /env/ 7 | /mypy_env/ 8 | *.pyc 9 | /test/generated/**/*.py 10 | !/test/generated/**/__init__.py 11 | .pytest_cache 12 | /build/ 13 | /dist/ 14 | *.egg-info 15 | .idea/ 16 | .DS_Store 17 | /venv_* 18 | /protoc_* 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Upcoming 2 | 3 | - Mark top-level mangled identifiers as `TypeAlias`. 4 | - Change the top-level mangling prefix from `global___` to `Global___` to respect 5 | [Y042](https://github.com/PyCQA/flake8-pyi/blob/main/ERRORCODES.md#list-of-warnings) naming convention. 6 | - Support client stub async typing overloads 7 | 8 | ## 3.6.0 9 | 10 | - Remove 3.7 compatibility for typing_extensions.final/Literal 11 | - Bump protobuf to 4.25.3 12 | 13 | ## 3.5.0 14 | 15 | - Add gRPC aio stub and servicer generation (#489) 16 | - Bump tested dependencies to pyright==1.1.319, mypy==1.4.1, protobuf==4.23.4, grpcio-tools==1.56.2 17 | - Drop support for py 3.7. Add support for py 3.11. 18 | - Don't add unnecessary flake8 noqa F821 comments. (Become compatible with flake8-pyi>=23.5.0.) 19 | 20 | ## 3.4.0 21 | 22 | - Mark messages as @typing.final 23 | - Switch to use the reserved mypy-protobuf extension option numbers https://github.com/protocolbuffers/protobuf/blob/main/docs/options.md 24 | - Bump protobuf dependency to 4.21.8 25 | - Skip unnecessary flake8 noqa F821 on module scope fwd reference classes 26 | 27 | ## 3.3.0 28 | 29 | - Prefer (mypy_protobuf.options).casttype to (mypy_protobuf.casttype) 30 | - Allows us to use a single extension number 31 | - Applies to casttype,keytype,valuetype 32 | - Deprecate (but still support) the old-style extension 33 | - Prefer importing from `typing` over `typing_extensions` on new enough python versions 34 | - Support emitting module docstrings 35 | - Make generated code flake8 compatible 36 | - Convert extensions.proto to proto3 37 | - Drop support for Python 3.6 [EOL] 38 | - Bump to grpc-stubs 1.24.7 39 | - Require protobuf 3.19.4 40 | - Bump support to mypy 0.941 41 | - Use PEP 604 Union syntax `X | None` rather than `Optional[X]` 42 | - Moved from dropbox/mypy-protobuf to nipunn1313/mypy-protobuf 43 | - Nipunn is the primary maintainer and plans to continue maintenance! 44 | 45 | ## 3.2.0 46 | 47 | - Remove unnecessary `...` in generated files. 48 | - Reorder/reference enum classes to avoid forward references. 49 | - Support `*_FIELD_NUMBER` for extensions 50 | - Bump types-protobuf dependency to 3.19 51 | - Require protobuf 3.19.3 52 | - Support DESCRIPTOR: ServiceDescriptor in py generic services 53 | - More accurately represent method names in py generic services (done -> callback, self -> inst) 54 | - More accurately represent method names in grpc services (`request` -> `request_iterator`) 55 | - Internal: Get tests to pass on pure-python protobuf impl (minor semantic differences) 56 | - Internal: Bump pyright in testing to 1.1.206 57 | - Internal: Use stubtest to validate generated stubs match generated runtime 58 | 59 | ## 3.1.0 60 | 61 | - Require protobuf 3.19.1 62 | - Change `EnumTypeWrapper.V` to `EnumTypeWrapper.ValueType` per https://github.com/protocolbuffers/protobuf/pull/8182. 63 | Will allow for unquoted annotations starting with protobuf 3.20.0. `.V` will continue to work for the foreseeable 64 | future for backward compatibility. 65 | - suppress pyright warning reportSelfClsParameterName when a proto field is named `self` 66 | - Allow optional constructor keywords for primitive field types in proto3, following this [chart](https://github.com/protocolbuffers/protobuf/blob/master/docs/field_presence.md#presence-in-proto3-apis). 67 | - Reorder Enum helper classes to eliminate pycharm errors 68 | 69 | ## 3.0.0 70 | 71 | - Drop support for targeting python 2.7 72 | - Generate py3 specific syntax for unicode strings (`""` rather than `u""`) 73 | - Now requires protobuf 3.18.0 74 | - Handle escaping properly in docstrings and attribute strings (#296) 75 | - Use three-digit version number 3.0.0 for minor and patch releases 76 | - Codify pyright support in README 77 | 78 | ## 2.10 79 | 80 | - Switch from setup.py to pyproject.toml and setup.cfg per https://packaging.python.org/tutorials/packaging-projects/ 81 | - Remove dependency on grpcio-tools. mypy-protobuf doesn't need it to run. It's only needed to run mypy afterward. 82 | - Avoid relative imports in `_grpc_pb2.pyi` stubs. grpc stubs themselves don't use relative imports, nor `__init__.py` files 83 | - Use `"""` docstring style comments in generated code rather than `#` style so it shows up in IDEs. 84 | - Disambiguate messages from reserved python keywords (eg `None`) with prefix `_r_` rather than `__` - since `__` is considered private. 85 | - Bump tests to use pyright 1.1.169, types-protobuf 3.17.4, pytest 6.2.5, grpc-stubs 1.24.7, grpcio-tools 1.40.0 86 | - Since upstream protobuf has dropped support with 3.18.0, 2.10 will be the last mypy-protobuf that supports targeting python 2.7. Updated docs for this. 87 | 88 | ## 2.9 89 | 90 | - [Rename master branch to main](https://github.com/github/renaming) 91 | - Make `install_requires` requirements for grpcio-tools and types-protobuf use >= 92 | 93 | ## 2.8 94 | 95 | - Propagate comments from .proto files to .pyi files 96 | - Add protobuf type stubs to the setup requirements 97 | - Fix [#239](https://github.com/nipunn1313/mypy-protobuf/issues/239) Remove type: ignore used in enum by pulling `V` into a separate class. 98 | - Use pytest 6.2.4 for internal test suites on python3 99 | - Remove `protoc_gen_mypy.bat` as the entry-points method creates protoc-gen-mypy.exe. Add test confirming. 100 | 101 | ## 2.7 102 | 103 | - Fix [#244](https://github.com/nipunn1313/mypy-protobuf/issues/244) - support extensions defined at module scope with proper types, matching extensions defined within Messages. See [`_ExtensionDict`](https://github.com/python/typeshed/blob/4765978f6ceeb24e10bdf93c0d4b72dfb35836d4/stubs/protobuf/google/protobuf/internal/extension_dict.pyi#L9) 104 | 105 | ```proto 106 | extend google.protobuf.MessageOptions { 107 | string test_message_option = 51234; 108 | } 109 | ``` 110 | 111 | ```python 112 | # Used to generate 113 | test_message_option: google.protobuf.descriptor.FieldDescriptor = ... 114 | 115 | # Now generates 116 | test_message_option: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MessageOptions, typing.Text] = ... 117 | ``` 118 | 119 | Fix repeated extensions as well - to generate RepeatedScalarFieldContainer and RepeatedCompositeFieldContainer 120 | 121 | - Now requires [types-protobuf](https://pypi.org/project/types-protobuf/) 3.17.3 122 | - Fix [#238](https://github.com/nipunn1313/mypy-protobuf/issues/238) - handling enum variants that name conflict with EnumTypeWrapper methods 123 | - Improve mypy-protobuf testsuite expected-errors file to make insertions/deletions easier to code review 124 | - Fix [#227](https://github.com/nipunn1313/mypy-protobuf/issues/227) - improve support for messages/enums with python reserved keyword names 125 | - Order fields within a message in original .proto file order Previously, they were grouped by scalar/nonscalar. Remove 126 | that grouping to make it a bit easier to correlate .proto files to .pyi files. 127 | 128 | ## 2.6 129 | 130 | - Bump protoc support to 3.17.3 131 | - Use latest python versions in tests (3.6.14 3.7.11 3.8.11 3.9.6) 132 | - Support reserved names for message types. Previously generated invalid mypy. 133 | 134 | ```proto 135 | message M { 136 | message None {} 137 | None none = 1; 138 | } 139 | ``` 140 | 141 | - Support `protoc-gen-mypy -V` and `protoc-gen-mypy --version` to print version number 142 | - Return `Optional[Literal[...]]` instead of `Literal[...]` from WhichOneof to support 143 | cases in which none of the fields of the WhichOneof are set. See the following example. 144 | 145 | ```python 146 | def hello(name: str) -> None: ... 147 | n = proto.WhichOneof("name") 148 | hello(n) # Will now result in a mypy error. 149 | assert n is not None 150 | hello(n) # Should work ok 151 | ``` 152 | 153 | - Bump mypy version to 0.910, utilizing stubs types-protobuf==0.1.14. See https://mypy-lang.blogspot.com/2021/05/the-upcoming-switch-to-modular-typeshed.html 154 | - Bump grpcio version tested to 1.38.1 and grpc-stubs to 1.24.6 155 | - Generate a `# type: ignore` for enum generated stubs to avoid circular dependency described in #214. Bandaid solution. 156 | 157 | ## 2.5 158 | 159 | - Organized generated enum code to prevent definition ordering issues in Pyright-based linters 160 | - Changed type generation for `grpcio` stubs to use the `MultiCallable` API ([see here](https://grpc.github.io/grpc/python/grpc.html#multi-callable-interfaces)) . This requires using the `grpc-stubs` typings for grpcio. This change should allow calling stub methods with common parameters (`timeout`, `metadata`, etc.) as well as calling methods on the `MultiCallable` object (e.g. `my_stub.MyRpcMethod.future()`). 161 | - Update stubs to mark repeated scalar and repeated enum fields as read-only 162 | 163 | ## 2.4 164 | 165 | - Add support for `_FIELD_NUMBER` generated fields on messages as specified [in the spec](https://developers.google.com/protocol-buffers/docs/reference/python-generated#fields) 166 | 167 | ## 2.3 168 | 169 | - Fix CI caching across version updates. 170 | - Added unit testing for EnumTypeWrapper.Value usage 171 | - Reexport of top level enum values when using `import public` 172 | 173 | ## 2.2 174 | 175 | - Fix bug where module level `DESCRIPTOR: FileDescriptor` was not being generated for files w public imports 176 | 177 | ## 2.1 178 | 179 | - Fix crash when a import public reexport when dependent proto file is not listed on command line for generation 180 | 181 | ## 2.0 182 | 183 | Non Backward Compatible Changes 184 | 185 | - Dropping support for running mypy-protobuf in python <= 3.5. Note you can still generate stubs target-compatible to python2 186 | - Type proto Enum values for as `MyEnum.V` rather than `MyEnumValue` for import ergonomics, 187 | allowing the caller to import `MyEnum` rather than conditionally importing `MyEnumValue` 188 | - Default disallow `None` as argument for primitive fields of constructors in proto3. 189 | Provided `relax_strict_optional_primitives` flag to relax this strictness if you prefer. 190 | 191 | New Features 192 | 193 | - Support for `grpcio` stubs generation 194 | - Allow `mypy_protobuf.py` to be run directly as a script 195 | - Add support for proto's [`well_known_types`](https://developers.google.com/protocol-buffers/docs/reference/python-generated#wkt) 196 | - Support message fields named `self` - by renaming the constructor's `self` to `self_` 197 | - Rename extensions proto from `mypy/mypy.proto` to `mypy_protobuf/extensions.proto` 198 | - Add support for mypy-proto extensions `mypy_protobuf.casttype`, `mypy_protobuf.keytype`, and `mypy_protobuf.valuetype` 199 | - Add support for `import public` proto imports - by reexporting in generated code 200 | 201 | Output Format 202 | 203 | - Generate fully qualified references rather than mangling 204 | - Import builtins library rather than mangling builtins 205 | - Use fully qualified names rather than mangling imports 206 | - Only mangle-alias top-level identifiers w/ `global___` to avoid conflict w/ fields of same name [previously was mangling inner messages as well] 207 | - Add support for `readable_stubs` parameter for mangle-free output code w/o fully-qualified references to message. 208 | (in many cases this is good enough and easier to read) 209 | - Generate `arg: Optional[type] = ...` instead of `arg: Optional[type] = None` 210 | - Avoid importing google.protobuf.message.Message unless it's needed 211 | 212 | Internal Improvements 213 | 214 | - Add support for python 3.9 to CI 215 | - Update mypy-protobuf CI to target 3.8 rather than 3.5 216 | - Inline mypy annotations, eliminate six, and remove `__future__` import in `mypy_protobuf_lib.py` 217 | - Flatten directory structure (remove python subdirectory). Updated unreleased installation instructions. 218 | - Deprecate and delete the go/ implementation 219 | 220 | ## 1.24 221 | 222 | - Bump required mypy version to 0.800 223 | - Bump required protobuf version to 3.14 (pi!!) 224 | - Update the autogenerated `.pyi` file header to cite `mypy-protobuf` 225 | - Reorganize mypy-protobuf testsuite files to more closely match autogeneration into a `generated` directory 226 | - Remove incorrect `type___` prefixed alias for inner enum type wrappers 227 | - Add support for extension fields in proto2 messages 228 | - Overwrite typing for `Message.Extensions` mapping to support better type inference for proto2 message extensions 229 | - Support `Message.HasExtension` and `Message.ClearExtension` 230 | - Bump python-protobuf from 3.11.3 to 3.13.0 231 | - Add support for optional proto3 fields 232 | - Support ScalarMap and MessageMap generated types for map types in proto. This will allow us to support `get_or_create` 233 | 234 | ```proto 235 | message Message { 236 | map map_message = 17 237 | } 238 | ``` 239 | 240 | and 241 | 242 | ```python 243 | message.map_message.get_or_create(0) 244 | ``` 245 | 246 | Before (1.23) 247 | 248 | ``` 249 | main.py:4: error: "MutableMapping[str, Nested]" has no attribute "get_or_create" [attr-defined] 250 | ``` 251 | 252 | After (1.24) - there is no error 253 | 254 | ## 1.23 255 | 256 | - Inherit FromString from superclass Message - rather than re-generating here. Fixes bug 257 | in python2 usage `google/protobuf/type_pb2.pyi:92: error: Argument 1 of "FromString" is incompatible with supertype "Message"; supertype defines the argument type as "ByteString" [override]` 258 | 259 | ## 1.22 260 | 261 | - Update tested/required mypy version to 0.780 (picks up new typeshed annotations). Includes improved typing/error messages on Message. 262 | 263 | Before (mypy < 0.780): 264 | 265 | ``` 266 | test_negative/negative.py:26: error: Argument 1 to "CopyFrom" of "Message" has incompatible type "str"; expected "Message" 267 | ``` 268 | 269 | After (mypy >= 0.780: 270 | 271 | ``` 272 | test_negative/negative.py:26: error: Argument 1 to "CopyFrom" of "Message" has incompatible type "str"; expected "Simple1" 273 | ``` 274 | 275 | - Update generated EnumTypeWrapper to be instances of EnumTypeWrapper - for more consistency 276 | with generated python code. Most caller code should not require mypy type changes. Egh 277 | `ProtoEnum.Value('first')` should work either way. 278 | 279 | Generated Before (in 1.21) 280 | 281 | ```python 282 | class ProtoEnum(object): 283 | @classmethod 284 | def Value(cls, name: str) -> ProtoEnumValue 285 | ``` 286 | 287 | Generated After (in 1.22) 288 | 289 | ```python 290 | ProtoEnum: _ProtoEnum 291 | class _ProtoEnum(google.protobuf.EnumTypeWrapper): 292 | def Value(self, name: str) -> ProtoEnumValue 293 | ``` 294 | 295 | - Remove autogenerated EnumTypeWrapper methods that are redundant to the typeshed parent class. Added testing for these. 296 | 297 | ## 1.21 298 | 299 | - Support for module descriptor. 300 | - Update mangling from `global__` to `message__` 301 | - Fix bug in message typing for nested enums. Split EnumValue from EnumTypeWrapper. Enforces that constructing 302 | an enum value must happen via a NewType wrapper to the int. 303 | 304 | Example: 305 | 306 | ```proto 307 | enum ProtoEnum { 308 | FIRST = 1; 309 | SECOND = 2; 310 | } 311 | 312 | mesage ProtoMsg { 313 | ProtoEnum enum = 1; 314 | } 315 | ``` 316 | 317 | Generated Before (in 1.20): 318 | 319 | ```python 320 | class ProtoEnum(object): 321 | @classmethod 322 | def Value(cls, name: str) -> ProtoEnum 323 | 324 | class ProtoMsg(Message): 325 | def __init__(self, enum: ProtoEnum) -> None 326 | ``` 327 | 328 | Generated After (in 1.21): 329 | 330 | ```python 331 | ProtoEnumValue = NewType('ProtoEnumValue', int) 332 | class ProtoEnum(object): 333 | @classmethod 334 | def Value(cls, name: str) -> ProtoEnumValue 335 | 336 | class ProtoMsg(Message): 337 | def __init__(self, enum: ProtoEnumValue) -> None 338 | ``` 339 | 340 | Migration Guide (with example calling code) 341 | 342 | Before (with 1.20) 343 | 344 | ```python 345 | from msg_pb2 import ProtoEnum, ProtoMsg 346 | 347 | def make_proto_msg(enum: ProtoEnum) -> ProtoMsg: 348 | return ProtoMsg(enum) 349 | make_proto_msg(ProtoMsg.FIRST) 350 | ``` 351 | 352 | After (with 1.21) 353 | 354 | ```python 355 | from msg_pb2 import ProtoEnum, ProtoMsg 356 | 357 | def make_proto_msg(enum: 'msg_pb2.ProtoEnumValue') -> ProtoMsg: 358 | return ProtoMsg(enum) 359 | make_proto_msg(ProtoMsg.FIRST) 360 | ``` 361 | 362 | - Use inline-style rather than comment-style typing in the pyi file 363 | - Remove MergeFrom/CopyFrom from generated code as it is in the Message superclass 364 | 365 | ## 1.20 366 | 367 | - Black code formatting 368 | - Fix message/field name aliasing when field name matches a message/enum name 369 | 370 | ## 1.19 371 | 372 | - Allow omitting required proto2 fields from constructor parameters 373 | - Support and testing for python 3.8 374 | - Support for python-protobuf to 3.11.3 375 | 376 | ## 1.18 377 | 378 | - Use `entry_points:console_scripts` to support long paths to the python interpreter 379 | 380 | ## 1.17 381 | 382 | - Update to newer mypy version - including minor changes to typeshed 383 | 384 | ## 1.16 385 | 386 | - Absolute path to necessary python 387 | - Add forward reference string literal support for enums 388 | - Alias builtin types to avoid collision with field name 389 | 390 | ## 1.15 391 | 392 | - Add `class` to set of python keywords 393 | 394 | ## 1.14 395 | 396 | - Add `Message.DESCRIPTOR` 397 | 398 | ## Older changelogs not available. Check git log if you need them! 399 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright (c) 2017 Dropbox, Inc. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mypy-protobuf: Generate mypy stub files from protobuf specs 2 | 3 | [![CI](https://github.com/nipunn1313/mypy-protobuf/workflows/CI/badge.svg)](https://github.com/nipunn1313/mypy-protobuf/actions?query=branch%3Amain) 4 | [![pypi](https://img.shields.io/pypi/v/mypy-protobuf?logo=Pypi)](https://pypi.org/project/mypy-protobuf/) 5 | [![license](https://img.shields.io/github/license/nipunn1313/mypy-protobuf)](https://github.com/nipunn1313/mypy-protobuf/blob/main/LICENSE) 6 | =========================================================== 7 | 8 | 2.10 is the last version of mypy-protobuf which supports targeting python 2.7. 9 | 10 | Built originally with love at [Dropbox](https://github.com/dropbox) 11 | 12 | See [Changelog](CHANGELOG.md) for recent changes. 13 | 14 | ## Requirements to run mypy-protobuf 15 | 16 | Earlier releases might work, but aren't tested 17 | 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. 21 | 22 | ## Requirements to run typecheckers on stubs generated by mypy-protobuf 23 | 24 | Earlier releases might work, but aren't tested 25 | 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 29 | 30 | ### To run typecheckers on code generated with grpc plugin - you'll additionally need 31 | 32 | Earlier releases might work, but aren't tested 33 | 34 | - [grpcio>=1.66.2](https://pypi.org/project/grpcio/) 35 | - [grpcio-tools>=1.66.2](https://pypi.org/project/grpcio-tools/) 36 | - [grpc-stubs>=1.53.0.5](https://pypi.org/project/grpc-stubs/) 37 | 38 | Other configurations may work, but are not continuously tested currently. 39 | We would be open to expanding this list - file an issue on the issue tracker. 40 | 41 | ## Installation 42 | 43 | The plugin can be installed with 44 | 45 | ``` 46 | pip3 install mypy-protobuf 47 | ``` 48 | 49 | To install unreleased 50 | 51 | ``` 52 | REV=main # or whichever unreleased git rev you'd like 53 | pip3 install git+https://github.com/nipunn1313/mypy-protobuf.git@$REV 54 | 55 | # For older (1.x) versions of mypy protobuf - you may need 56 | pip3 install git+https://github.com/nipunn1313/mypy-protobuf.git@$REV#subdirectory=python 57 | ``` 58 | 59 | In order to run mypy on the generated code, you'll need to install 60 | 61 | ``` 62 | pip3 install mypy>=0.910 types-protobuf>=0.1.14 63 | ``` 64 | 65 | # Usage 66 | 67 | On posix, protoc-gen-mypy is installed to python's executable bin. Assuming that's 68 | on your $PATH, you can run 69 | 70 | ``` 71 | protoc --python_out=output/location --mypy_out=output/location 72 | ``` 73 | 74 | Alternately, you can explicitly provide the path: 75 | 76 | ``` 77 | protoc --plugin=protoc-gen-mypy=path/to/protoc-gen-mypy --python_out=output/location --mypy_out=output/location 78 | ``` 79 | 80 | Check the version number with 81 | 82 | ``` 83 | > protoc-gen-mypy --version 84 | ``` 85 | 86 | ## Implementation 87 | 88 | The implementation of the plugin is in `mypy_protobuf/main.py`, which installs to 89 | an executable protoc-gen-mypy. On windows it installs to `protoc-gen-mypy.exe` 90 | 91 | ## Features 92 | 93 | See [Changelog](CHANGELOG.md) for full listing 94 | 95 | ### Bring comments from .proto files to docstrings in .pyi files 96 | 97 | Comments in the .proto files on messages, fields, enums, enum variants, extensions, services, and methods 98 | will appear as docstrings in .pyi files. Useful in IDEs for showing completions with comments. 99 | 100 | ### Types enum int values more strongly 101 | 102 | Enum int values produce stubs which wrap the int values in NewType 103 | 104 | ```proto 105 | enum MyEnum { 106 | HELLO = 0; 107 | WORLD = 1; 108 | } 109 | ``` 110 | 111 | Will yield an [enum type wrapper](https://github.com/python/typeshed/blob/16ae4c61201cd8b96b8b22cdfb2ab9e89ba5bcf2/stubs/protobuf/google/protobuf/internal/enum_type_wrapper.pyi) whose methods type to `MyEnum.ValueType` (a `NewType(int)` rather than `int`. 112 | This allows mypy to catch bugs where the wrong enum value is being used. 113 | 114 | Calling code may be typed as follows. 115 | 116 | In python >= 3.7 117 | 118 | ```python 119 | # May need [PEP 563](https://www.python.org/dev/peps/pep-0563/) to postpone evaluation of annotations 120 | # from __future__ import annotations # Not needed with python>=3.11 or protobuf>=3.20.0 121 | def f(x: MyEnum.ValueType): 122 | print(x) 123 | f(MyEnum.Value("HELLO")) 124 | ``` 125 | 126 | With protobuf <= 3.20.0, for usages of cast, the type of `x` must be quoted 127 | After protobuf >= 3.20.0 - `ValueType` exists in the python code and quotes aren't needed 128 | until [upstream protobuf](https://github.com/protocolbuffers/protobuf/pull/8182) includes `ValueType` 129 | 130 | ```python 131 | cast('MyEnum.ValueType', x) 132 | ``` 133 | 134 | Similarly, for type aliases with protobuf < 3.20.0, you must either quote the type or hide it behind `TYPE_CHECKING` 135 | 136 | ```python 137 | from typing import Tuple, TYPE_CHECKING 138 | HELLO = Tuple['MyEnum.ValueType', 'MyEnum.ValueType'] 139 | if TYPE_CHECKING: 140 | HELLO = Tuple[MyEnum.ValueType, MyEnum.ValueType] 141 | ``` 142 | 143 | #### Enum int impl details 144 | 145 | mypy-protobuf autogenerates an instance of the EnumTypeWrapper as follows. 146 | 147 | ```python 148 | class _MyEnum: 149 | ValueType = typing.NewType('ValueType', builtins.int) 150 | V: typing_extensions.TypeAlias = ValueType 151 | class _MyEnumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MyEnum.ValueType], builtins.type): 152 | DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor 153 | HELLO: _MyEnum.ValueType # 0 154 | WORLD: _MyEnum.ValueType # 1 155 | class MyEnum(_MyEnum, metaclass=_MyEnumEnumTypeWrapper): 156 | pass 157 | 158 | HELLO: MyEnum.ValueType # 0 159 | WORLD: MyEnum.ValueType # 1 160 | ``` 161 | 162 | `_MyEnumEnumTypeWrapper` extends the EnumTypeWrapper to take/return MyEnum.ValueType rather than int 163 | `MyEnum` is an instance of the `EnumTypeWrapper`. 164 | 165 | - Use `_MyEnum` and of metaclass is an implementation detail to make MyEnum.ValueType a valid type w/o a circular dependency 166 | - `V` is supported as an alias of `ValueType` for backward compatibility 167 | 168 | ### Supports generating type wrappers for fields and maps 169 | 170 | M.proto 171 | 172 | ```proto 173 | message M { 174 | uint32 user_id = 1 [(mypy_protobuf.options).casttype="mymod.UserId"]; 175 | map email_by_uid = 2 [ 176 | (mypy_protobuf.options).keytype="path/to/mymod.UserId", 177 | (mypy_protobuf.options).valuetype="path/to/mymod.Email" 178 | ]; 179 | } 180 | ``` 181 | 182 | mymod.py 183 | 184 | ```python 185 | UserId = NewType("UserId", int) 186 | Email = NewType("Email", Text) 187 | ``` 188 | 189 | ### `py_generic_services` 190 | 191 | If `py_generic_services` is set in your proto file, then mypy-protobuf will 192 | generate service stubs. If you want GRPC stubs instead - use the GRPC instructions. 193 | 194 | ### `readable_stubs` 195 | 196 | If `readable_stubs` is set, mypy-protobuf will generate easier-to-read stubs. The downside 197 | to this approach - is that it's possible to generate stubs which do not pass mypy - particularly 198 | in the case of name collisions. mypy-protobuf defaults to generating stubs with fully qualified 199 | imports and mangled global-level identifiers to defend against name collisions between global 200 | identifiers and field names. 201 | 202 | If you're ok with this risk, try it out! 203 | 204 | ``` 205 | protoc --python_out=output/location --mypy_out=readable_stubs:output/location 206 | ``` 207 | 208 | ### `relax_strict_optional_primitives` 209 | 210 | If you are using proto3, then primitives cannot be represented as NULL on the wire - 211 | only as their zero value. By default mypy-protobuf types message constructors to have 212 | non-nullable primitives (eg `int` instead of `Optional[int]`). python-protobuf itself will 213 | internally convert None -> zero value. If you intentionally want to use this behavior, 214 | set this flag! We recommend avoiding this, as it can lead to developer error - confusing 215 | NULL and 0 as distinct on the wire. 216 | However, it may be helpful when migrating existing proto2 code, where the distinction is meaningful 217 | 218 | ``` 219 | protoc --python_out=output/location --mypy_out=relax_strict_optional_primitives:output/location 220 | ``` 221 | 222 | ### Output suppression 223 | 224 | To suppress output, you can run 225 | 226 | ``` 227 | protoc --python_out=output/location --mypy_out=quiet:output/location 228 | ``` 229 | 230 | ### GRPC 231 | 232 | This plugin provides stubs generation for grpcio generated code. 233 | 234 | ``` 235 | protoc \ 236 | --python_out=output/location \ 237 | --mypy_out=output/location \ 238 | --grpc_out=output/location \ 239 | --mypy_grpc_out=output/location 240 | ``` 241 | 242 | Note that generated code for grpc will work only together with code for python and locations should be the same. 243 | If you need stubs for grpc internal code we suggest using this package https://github.com/shabbyrobe/grpc-stubs 244 | 245 | ### Targeting python2 support 246 | 247 | mypy-protobuf's drops support for targeting python2 with version 3.0. If you still need python2 support - 248 | 249 | ``` 250 | python3 -m pip install mypy_protobuf==2.10 251 | protoc --python_out=output/location --mypy_out=output/location 252 | mypy --target-version=2.7 {files} 253 | ``` 254 | 255 | ## Contributing 256 | 257 | Contributions to the implementation are welcome. Please run tests using `./run_test.sh`. 258 | Ensure code is formatted using black. 259 | 260 | ``` 261 | pip3 install black 262 | black . 263 | ``` 264 | 265 | ## Contributors 266 | 267 | - [@nipunn1313](https://github.com/nipunn1313) 268 | - [@dzbarsky](https://github.com/dzbarsky) 269 | - [@gvanrossum](https://github.com/gvanrossum) 270 | - [@peterlvilim](https://github.com/peterlvilim) 271 | - [@msullivan](https://github.com/msullivan) 272 | - [@bradenaw](https://github.com/bradenaw) 273 | - [@ilevkivskyi](https://github.com/ilevkivskyi) 274 | - [@Ketouem](https://github.com/Ketouem) 275 | - [@nmiculinic](https://github.com/nmiculinic) 276 | - [@onto](https://github.com/onto) 277 | - [@jcppkkk](https://github.com/jcppkkk) 278 | - [@drather19](https://github.com/drather19) 279 | - [@smessmer](https://github.com/smessmer) 280 | - [@pcorpet](https://github.com/pcorpet) 281 | - [@zozoens31](https://github.com/zozoens31) 282 | - [@abhishekrb19](https://github.com/abhishekrb19) 283 | - [@jaens](https://github.com/jaens) 284 | - [@arussellsaw](https://github.com/arussellsaw) 285 | - [@shabbyrobe](https://github.com/shabbyrobe) 286 | - [@reorx](https://github.com/reorx) 287 | - [@zifter](https://github.com/zifter) 288 | - [@juzna](https://github.com/juzna) 289 | - [@mikolajz](https://github.com/mikolajz) 290 | - [@chadrik](https://github.com/chadrik) 291 | - [@EPronovost](https://github.com/EPronovost) 292 | - [@chrislawlor](https://github.com/chrislawlor) 293 | - [@henribru](https://github.com/henribru) 294 | - [@Evgenus](https://github.com/Evgenus) 295 | - [@MHDante](https://github.com/MHDante) 296 | - [@nelfin](https://github.com/nelfin) 297 | - [@alkasm](https://github.com/alkasm) 298 | - [@tarmath](https://github.com/tarmath) 299 | - [@jaredkhan](https://github.com/jaredkhan) 300 | - [@sodul](https://github.com/sodul) 301 | - [@miaachan](https://github.com/miaachan) 302 | - [@Alphadelta14](https://github.com/Alphadelta14) 303 | - [@fergyfresh](https://github.com/fergyfresh) 304 | - [@AlexWaygood](https://github.com/AlexWaygood) 305 | - [@Avasam](https://github.com/Avasam) 306 | - [@artificial-aidan](https://github.com/artificial-aidan) 307 | 308 | ## Licence etc. 309 | 310 | 1. License: Apache 2.0. 311 | 2. Copyright attribution: Copyright (c) 2022 Nipunn Koorapati 312 | -------------------------------------------------------------------------------- /mypy_protobuf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/mypy_protobuf/__init__.py -------------------------------------------------------------------------------- /mypy_protobuf/extensions_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # NO CHECKED-IN PROTOBUF GENCODE 4 | # source: mypy_protobuf/extensions.proto 5 | # Protobuf Python Version: 5.28.2 6 | """Generated protocol buffer code.""" 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import descriptor_pool as _descriptor_pool 9 | from google.protobuf import runtime_version as _runtime_version 10 | from google.protobuf import symbol_database as _symbol_database 11 | from google.protobuf.internal import builder as _builder 12 | _runtime_version.ValidateProtobufRuntimeVersion( 13 | _runtime_version.Domain.PUBLIC, 14 | 5, 15 | 28, 16 | 2, 17 | '', 18 | 'mypy_protobuf/extensions.proto' 19 | ) 20 | # @@protoc_insertion_point(imports) 21 | 22 | _sym_db = _symbol_database.Default() 23 | 24 | 25 | from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 26 | 27 | 28 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emypy_protobuf/extensions.proto\x12\rmypy_protobuf\x1a google/protobuf/descriptor.proto\"D\n\x0c\x46ieldOptions\x12\x10\n\x08\x63\x61sttype\x18\x01 \x01(\t\x12\x0f\n\x07keytype\x18\x02 \x01(\t\x12\x11\n\tvaluetype\x18\x03 \x01(\t:L\n\x07options\x12\x1d.google.protobuf.FieldOptions\x18\x82\t \x01(\x0b\x32\x1b.mypy_protobuf.FieldOptions:4\n\x08\x63\x61sttype\x12\x1d.google.protobuf.FieldOptions\x18\xff\x08 \x01(\tB\x02\x18\x01:3\n\x07keytype\x12\x1d.google.protobuf.FieldOptions\x18\x80\t \x01(\tB\x02\x18\x01:5\n\tvaluetype\x12\x1d.google.protobuf.FieldOptions\x18\x81\t \x01(\tB\x02\x18\x01\x62\x06proto3') 29 | 30 | _globals = globals() 31 | _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) 32 | _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'mypy_protobuf.extensions_pb2', _globals) 33 | if not _descriptor._USE_C_DESCRIPTORS: 34 | DESCRIPTOR._loaded_options = None 35 | _globals['casttype']._loaded_options = None 36 | _globals['casttype']._serialized_options = b'\030\001' 37 | _globals['keytype']._loaded_options = None 38 | _globals['keytype']._serialized_options = b'\030\001' 39 | _globals['valuetype']._loaded_options = None 40 | _globals['valuetype']._serialized_options = b'\030\001' 41 | _globals['_FIELDOPTIONS']._serialized_start=83 42 | _globals['_FIELDOPTIONS']._serialized_end=151 43 | # @@protoc_insertion_point(module_scope) 44 | -------------------------------------------------------------------------------- /mypy_protobuf/extensions_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.descriptor_pb2 9 | import google.protobuf.internal.extension_dict 10 | import google.protobuf.message 11 | import sys 12 | import typing 13 | 14 | if sys.version_info >= (3, 10): 15 | import typing as typing_extensions 16 | else: 17 | import typing_extensions 18 | 19 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 20 | 21 | @typing.final 22 | class FieldOptions(google.protobuf.message.Message): 23 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 24 | 25 | CASTTYPE_FIELD_NUMBER: builtins.int 26 | KEYTYPE_FIELD_NUMBER: builtins.int 27 | VALUETYPE_FIELD_NUMBER: builtins.int 28 | casttype: builtins.str 29 | """Tells mypy-protobuf to use a specific newtype rather than the normal type for this field.""" 30 | keytype: builtins.str 31 | """Tells mypy-protobuf to use a specific type for keys; only makes sense on map fields""" 32 | valuetype: builtins.str 33 | """Tells mypy-protobuf to use a specific type for values; only makes sense on map fields""" 34 | def __init__( 35 | self, 36 | *, 37 | casttype: builtins.str = ..., 38 | keytype: builtins.str = ..., 39 | valuetype: builtins.str = ..., 40 | ) -> None: ... 41 | def ClearField(self, field_name: typing.Literal["casttype", b"casttype", "keytype", b"keytype", "valuetype", b"valuetype"]) -> None: ... 42 | 43 | Global___FieldOptions: typing_extensions.TypeAlias = FieldOptions 44 | 45 | OPTIONS_FIELD_NUMBER: builtins.int 46 | CASTTYPE_FIELD_NUMBER: builtins.int 47 | KEYTYPE_FIELD_NUMBER: builtins.int 48 | VALUETYPE_FIELD_NUMBER: builtins.int 49 | options: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, Global___FieldOptions] 50 | """Custom field options from mypy-protobuf""" 51 | casttype: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, builtins.str] 52 | """Legacy fields. Prefer to use ones within `options` instead.""" 53 | keytype: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, builtins.str] 54 | valuetype: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, builtins.str] 55 | -------------------------------------------------------------------------------- /mypy_protobuf/protoc_gen_mypy.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | %~dp0\python -u %0\..\protoc-gen-mypy 3 | -------------------------------------------------------------------------------- /mypy_requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements to run mypy itself. Mypy executable exists in a separate venv. 2 | mypy==1.13.0 3 | -------------------------------------------------------------------------------- /proto/google/protobuf/duration.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option cc_enable_arenas = true; 37 | option go_package = "github.com/golang/protobuf/ptypes/duration"; 38 | option java_package = "com.google.protobuf"; 39 | option java_outer_classname = "DurationProto"; 40 | option java_multiple_files = true; 41 | option objc_class_prefix = "GPB"; 42 | 43 | // A Duration represents a signed, fixed-length span of time represented 44 | // as a count of seconds and fractions of seconds at nanosecond 45 | // resolution. It is independent of any calendar and concepts like "day" 46 | // or "month". It is related to Timestamp in that the difference between 47 | // two Timestamp values is a Duration and it can be added or subtracted 48 | // from a Timestamp. Range is approximately +-10,000 years. 49 | // 50 | // # Examples 51 | // 52 | // Example 1: Compute Duration from two Timestamps in pseudo code. 53 | // 54 | // Timestamp start = ...; 55 | // Timestamp end = ...; 56 | // Duration duration = ...; 57 | // 58 | // duration.seconds = end.seconds - start.seconds; 59 | // duration.nanos = end.nanos - start.nanos; 60 | // 61 | // if (duration.seconds < 0 && duration.nanos > 0) { 62 | // duration.seconds += 1; 63 | // duration.nanos -= 1000000000; 64 | // } else if (duration.seconds > 0 && duration.nanos < 0) { 65 | // duration.seconds -= 1; 66 | // duration.nanos += 1000000000; 67 | // } 68 | // 69 | // Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. 70 | // 71 | // Timestamp start = ...; 72 | // Duration duration = ...; 73 | // Timestamp end = ...; 74 | // 75 | // end.seconds = start.seconds + duration.seconds; 76 | // end.nanos = start.nanos + duration.nanos; 77 | // 78 | // if (end.nanos < 0) { 79 | // end.seconds -= 1; 80 | // end.nanos += 1000000000; 81 | // } else if (end.nanos >= 1000000000) { 82 | // end.seconds += 1; 83 | // end.nanos -= 1000000000; 84 | // } 85 | // 86 | // Example 3: Compute Duration from datetime.timedelta in Python. 87 | // 88 | // td = datetime.timedelta(days=3, minutes=10) 89 | // duration = Duration() 90 | // duration.FromTimedelta(td) 91 | // 92 | // # JSON Mapping 93 | // 94 | // In JSON format, the Duration type is encoded as a string rather than an 95 | // object, where the string ends in the suffix "s" (indicating seconds) and 96 | // is preceded by the number of seconds, with nanoseconds expressed as 97 | // fractional seconds. For example, 3 seconds with 0 nanoseconds should be 98 | // encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should 99 | // be expressed in JSON format as "3.000000001s", and 3 seconds and 1 100 | // microsecond should be expressed in JSON format as "3.000001s". 101 | // 102 | // 103 | message Duration { 104 | // Signed seconds of the span of time. Must be from -315,576,000,000 105 | // to +315,576,000,000 inclusive. Note: these bounds are computed from: 106 | // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years 107 | int64 seconds = 1; 108 | 109 | // Signed fractions of a second at nanosecond resolution of the span 110 | // of time. Durations less than one second are represented with a 0 111 | // `seconds` field and a positive or negative `nanos` field. For durations 112 | // of one second or more, a non-zero value for the `nanos` field must be 113 | // of the same sign as the `seconds` field. Must be from -999,999,999 114 | // to +999,999,999 inclusive. 115 | int32 nanos = 2; 116 | } 117 | -------------------------------------------------------------------------------- /proto/mypy_protobuf/extensions.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package mypy_protobuf; 4 | 5 | import "google/protobuf/descriptor.proto"; 6 | 7 | message FieldOptions { 8 | // Tells mypy-protobuf to use a specific newtype rather than the normal type for this field. 9 | string casttype = 1; 10 | // Tells mypy-protobuf to use a specific type for keys; only makes sense on map fields 11 | string keytype = 2; 12 | // Tells mypy-protobuf to use a specific type for values; only makes sense on map fields 13 | string valuetype = 3; 14 | 15 | } 16 | 17 | extend google.protobuf.FieldOptions { 18 | // Custom field options from mypy-protobuf 19 | FieldOptions options = 1154; 20 | 21 | // Legacy fields. Prefer to use ones within `options` instead. 22 | string casttype = 1151 [deprecated = true]; 23 | string keytype = 1152 [deprecated = true]; 24 | string valuetype = 1153 [deprecated = true]; 25 | } 26 | -------------------------------------------------------------------------------- /proto/testproto/Capitalized/Capitalized.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package Capitalized; 4 | 5 | message lower { 6 | int64 a = 1; 7 | } 8 | 9 | message Upper { 10 | lower Lower = 1; 11 | } 12 | 13 | message lower2 { 14 | Upper upper = 1; 15 | } 16 | -------------------------------------------------------------------------------- /proto/testproto/comment_special_chars.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package comment_special_chars; 4 | 5 | message Test { 6 | // Ending with " 7 | string a = 1; 8 | // Ending with "" 9 | string b = 2; 10 | // Ending with """ 11 | string c = 3; 12 | // Ending with \ 13 | string d = 4; 14 | // Containing bad escape: \x 15 | string e = 5; 16 | // Containing """" quadruple 17 | string f = 6; 18 | // Containing """"" quintuple 19 | string g = 7; 20 | // Containing """""" sextuple 21 | string h = 8; 22 | // """ Multiple """ triples """ 23 | string i = 9; 24 | // "quotes" can be a problem in comments. 25 | // """Triple quotes""" just as well 26 | string j = 10; 27 | // """""""""""""""""""""""""""""""""""""""""""""""" 28 | // " " 29 | // " Super Duper comments with surrounding edges! " 30 | // " " 31 | // " Pay attention to me!!!! " 32 | // " " 33 | // """""""""""""""""""""""""""""""""""""""""""""""" 34 | string k = 11; 35 | } 36 | -------------------------------------------------------------------------------- /proto/testproto/dot.com/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package test; 3 | 4 | message TestMessage { 5 | string foo = 1; 6 | } 7 | -------------------------------------------------------------------------------- /proto/testproto/grpc/dummy.proto: -------------------------------------------------------------------------------- 1 | // https://github.com/vmagamedov/grpclib/blob/master/tests/dummy.proto 2 | syntax = "proto3"; 3 | 4 | package dummy; 5 | 6 | message DummyRequest { 7 | string value = 1; 8 | } 9 | 10 | message DummyReply { 11 | string value = 1; 12 | } 13 | 14 | // DummyService 15 | service DummyService { 16 | // UnaryUnary 17 | rpc UnaryUnary (DummyRequest) returns (DummyReply) {} 18 | // UnaryStream 19 | rpc UnaryStream (DummyRequest) returns (stream DummyReply) {} 20 | // StreamUnary 21 | rpc StreamUnary (stream DummyRequest) returns (DummyReply) {} 22 | // StreamStream 23 | rpc StreamStream (stream DummyRequest) returns (stream DummyReply) {} 24 | } 25 | -------------------------------------------------------------------------------- /proto/testproto/grpc/import.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "testproto/test.proto"; 4 | import "google/protobuf/empty.proto"; 5 | 6 | package test.grpc; 7 | 8 | // SimpleService 9 | service SimpleService { 10 | // UnaryUnary 11 | rpc UnaryUnary (google.protobuf.Empty) returns (test.Simple1) {} 12 | // UnaryStream 13 | rpc UnaryStream (test.Simple1) returns (google.protobuf.Empty) {} 14 | rpc NoComment (test.Simple1) returns (google.protobuf.Empty) {} 15 | } 16 | -------------------------------------------------------------------------------- /proto/testproto/inner/inner.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package inner; 4 | 5 | import "testproto/test3.proto"; 6 | 7 | message Inner { 8 | test3.OuterEnum a = 1; 9 | } 10 | -------------------------------------------------------------------------------- /proto/testproto/nested/nested.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test.nested_package; 4 | 5 | import "testproto/test3.proto"; 6 | 7 | message Nested { 8 | test3.OuterEnum a = 1; 9 | } 10 | 11 | message AnotherNested { 12 | enum NestedEnum { 13 | INVALID = 0; 14 | ONE = 1; 15 | TWO = 2; 16 | } 17 | 18 | message NestedMessage { 19 | enum NestedEnum2 { 20 | UNDEFINED = 0; 21 | NESTED_ENUM1 = 1; 22 | NESTED_ENUM2 = 2; 23 | } 24 | string s = 1; 25 | bool b = 2; 26 | NestedEnum ne = 3; 27 | NestedEnum2 ne2 = 4; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /proto/testproto/nopackage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | // Intentionally don't set a package - just to make sure we can handle it. 4 | 5 | message NoPackage {} 6 | message NoPackage2 { 7 | NoPackage np = 1; 8 | repeated NoPackage np_rep = 2; 9 | } 10 | -------------------------------------------------------------------------------- /proto/testproto/readme_enum.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test; 4 | 5 | enum MyEnum { 6 | HELLO = 0; 7 | WORLD = 1; 8 | } 9 | -------------------------------------------------------------------------------- /proto/testproto/reexport.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package test3; 4 | 5 | // Should be reexported 6 | import public "testproto/test3.proto"; 7 | import public "google/protobuf/empty.proto"; 8 | // Not reexported 9 | import "testproto/inner/inner.proto"; 10 | -------------------------------------------------------------------------------- /proto/testproto/test.proto: -------------------------------------------------------------------------------- 1 | // Proto 2 test file. 2 | syntax = "proto2"; 3 | 4 | package test; 5 | 6 | import "mypy_protobuf/extensions.proto"; 7 | import "testproto/inner/inner.proto"; 8 | import "testproto/nested/nested.proto"; 9 | import "testproto/nopackage.proto"; 10 | import "testproto/test3.proto"; 11 | 12 | // Outer Enum 13 | enum OuterEnum { 14 | // FOO 15 | FOO = 1; 16 | // BAR 17 | BAR = 2; 18 | } 19 | 20 | // Naming conflicts! 21 | enum NamingConflicts { 22 | Name = 1; 23 | Value = 2; 24 | keys = 3; 25 | values = 4; 26 | items = 5; 27 | // See https://github.com/protocolbuffers/protobuf/issues/8803 28 | // proto itself generates broken code when DESCRIPTOR is there 29 | // DESCRIPTOR = 8; 30 | } 31 | 32 | // Message with one of everything 33 | message Simple1 { 34 | // Inner Enum 35 | enum InnerEnum { 36 | // INNER1 37 | INNER1 = 1; 38 | // INNER2 39 | INNER2 = 2; 40 | } 41 | message InnerMessage {} 42 | 43 | optional string a_string = 1; 44 | repeated string a_repeated_string = 2; 45 | optional bool a_boolean = 3; 46 | optional uint32 a_uint32 = 4; 47 | optional OuterEnum a_enum = 5; 48 | optional test3.OuterEnum a_external_enum = 6; 49 | 50 | optional inner.Inner a_inner = 7; 51 | optional test.nested_package.Nested a_nested = 12; 52 | 53 | optional InnerEnum inner_enum = 8; 54 | repeated InnerEnum rep_inner_enum = 9; 55 | optional InnerMessage inner_message = 10; 56 | repeated InnerMessage rep_inner_message = 11; 57 | 58 | optional NoPackage no_package = 13; 59 | optional test.nested_package.AnotherNested.NestedEnum nested_enum = 14; 60 | optional test.nested_package.AnotherNested.NestedMessage nested_message = 15; 61 | 62 | oneof a_oneof { 63 | string a_oneof_1 = 16; 64 | string a_oneof_2 = 17; 65 | Simple2 outer_message_in_oneof = 18; 66 | OuterEnum outer_enum_in_oneof = 19; 67 | InnerEnum inner_enum_in_oneof = 20; 68 | } 69 | 70 | optional uint32 user_id = 21 [(mypy_protobuf.casttype)="test/test_generated_mypy.UserId"]; 71 | optional string email = 22 [(mypy_protobuf.casttype)="test/test_generated_mypy.Email"]; 72 | map email_by_uid = 23 [ 73 | (mypy_protobuf.keytype)="test/test_generated_mypy.UserId", 74 | (mypy_protobuf.valuetype)="test/test_generated_mypy.Email" 75 | ]; 76 | 77 | extensions 1000 to max; 78 | } 79 | 80 | message Simple2 { 81 | required string a_string = 1; 82 | 83 | extensions 1000 to max; 84 | } 85 | 86 | message Extensions1 { 87 | extend Simple1 { 88 | // ext 89 | optional Extensions1 ext = 1000; 90 | } 91 | 92 | optional string ext1_string = 1; 93 | } 94 | 95 | message Extensions2 { 96 | extend Simple1 { 97 | // foo 98 | optional Extensions2 foo = 2020; 99 | } 100 | 101 | optional bool flag = 1; 102 | } 103 | 104 | message None { 105 | optional int64 valid = 1; 106 | } 107 | 108 | message PythonReservedKeywords { 109 | enum finally { 110 | continue = 1; 111 | valid_in_finally = 2; 112 | } 113 | 114 | message lambda { 115 | optional int64 continue = 1; 116 | optional int64 valid = 2; 117 | } 118 | 119 | required int64 from = 1; 120 | optional Simple2 in = 2; 121 | optional finally is = 3; 122 | optional int64 for = 5; 123 | optional int64 try = 6; 124 | optional int64 def = 7; 125 | optional int64 nonlocal = 8; 126 | optional int64 while = 9; 127 | optional int64 and = 10; 128 | optional int64 del = 11; 129 | optional int64 global = 12; 130 | optional int64 not = 13; 131 | optional int64 with = 14; 132 | optional int64 as = 15; 133 | optional int64 elif = 16; 134 | optional int64 if = 17; 135 | optional int64 or = 18; 136 | optional int64 yield = 19; 137 | optional int64 assert = 20; 138 | optional int64 else = 21; 139 | optional int64 import = 22; 140 | optional int64 pass = 23; 141 | optional int64 break = 24; 142 | optional int64 except = 25; 143 | optional int64 raise = 26; 144 | optional int64 False = 27; 145 | optional int64 True = 29; 146 | optional int64 class = 30; 147 | 148 | // Test unreserved identifiers w/ reserved message names 149 | optional None none = 28; 150 | optional finally valid = 31; 151 | } 152 | 153 | // Do one with just one arg - to make sure it's syntactically correct 154 | message PythonReservedKeywordsSmall { 155 | required int64 from = 1; 156 | } 157 | 158 | // Method name is reserved 159 | service PythonReservedKeywordsService { 160 | // lambda 161 | rpc lambda(Simple1) returns (PythonReservedKeywords.lambda) {} 162 | // valid_method_name1 163 | rpc valid_method_name1(Simple1) returns (None) {} 164 | // valid_method_name2 165 | rpc valid_method_name2(Simple1) returns (PythonReservedKeywords.lambda) {} 166 | } 167 | 168 | // when service name itself is reserved - generated code was found to be invalid 169 | // in protoc 3.17.3 170 | //service global { 171 | // rpc Echo(Simple1) returns (Simple2) {} 172 | //} 173 | 174 | option py_generic_services = true; 175 | service ATestService { 176 | rpc Echo(Simple1) returns (Simple2) {} 177 | } 178 | 179 | message SelfField { 180 | // Field self -> must generate an __init__ method w/ different name 181 | optional int64 self = 1; 182 | } 183 | -------------------------------------------------------------------------------- /proto/testproto/test3.proto: -------------------------------------------------------------------------------- 1 | /* 2 | * Proto 3 test example. 3 | * 4 | * Descriptor documentation should show up in module docstrings. 5 | */ 6 | syntax = "proto3"; 7 | 8 | /* package test3 */ 9 | package test3; 10 | 11 | import "mypy_protobuf/extensions.proto"; 12 | 13 | enum OuterEnum { 14 | UNKNOWN = 0; 15 | FOO3 = 1; 16 | BAR3 = 2; 17 | } 18 | 19 | message OuterMessage3 { 20 | string a_string = 1; 21 | } 22 | 23 | message SimpleProto3 { 24 | enum InnerEnum { 25 | INNER1 = 0; 26 | INNER2 = 1; 27 | } 28 | 29 | string a_string = 1; 30 | repeated string a_repeated_string = 2; 31 | OuterEnum a_outer_enum = 3; 32 | OuterMessage3 outer_message = 4; 33 | InnerEnum inner_enum = 15; 34 | 35 | oneof a_oneof { 36 | string a_oneof_1 = 5; 37 | string a_oneof_2 = 6; 38 | OuterMessage3 outer_message_in_oneof = 12; 39 | OuterEnum outer_enum_in_oneof = 13; 40 | InnerEnum inner_enum_in_oneof = 14; 41 | } 42 | 43 | oneof b_oneof { 44 | string b_oneof_1 = 7; 45 | string b_oneof_2 = 8; 46 | } 47 | 48 | OuterMessage3 bool = 9; 49 | 50 | // Test having fieldname match messagename 51 | OuterEnum OuterEnum = 10; 52 | OuterMessage3 OuterMessage3 = 11; 53 | 54 | // Test generation of map 55 | map map_scalar = 16; 56 | map map_message = 17; 57 | 58 | optional string an_optional_string = 18; 59 | 60 | uint32 user_id = 19 [(mypy_protobuf.options).casttype="test/test_generated_mypy.UserId"]; 61 | string email = 20 [(mypy_protobuf.options).casttype="test/test_generated_mypy.Email"]; 62 | map email_by_uid = 21 [ 63 | (mypy_protobuf.options).keytype="test/test_generated_mypy.UserId", 64 | (mypy_protobuf.options).valuetype="test/test_generated_mypy.Email" 65 | ]; 66 | } 67 | -------------------------------------------------------------------------------- /proto/testproto/test_extensions2.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | import "testproto/test.proto"; 4 | 5 | package test; 6 | 7 | message SeparateFileExtension { 8 | extend Simple2 { optional SeparateFileExtension ext = 1001; } 9 | 10 | optional bool flag = 1; 11 | } -------------------------------------------------------------------------------- /proto/testproto/test_extensions3.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/protobuf/descriptor.proto"; 4 | import "testproto/test3.proto"; 5 | 6 | package test3; 7 | 8 | extend google.protobuf.FieldOptions { 9 | string test_field_extension = 50000; 10 | } 11 | 12 | extend google.protobuf.MessageOptions { 13 | string scalar_option = 51234; 14 | repeated string repeated_scalar_option = 51235; 15 | OuterEnum enum_option = 51236; 16 | repeated OuterEnum repeated_enum_option = 51237; 17 | OuterMessage3 msg_option = 51238; 18 | repeated OuterMessage3 repeated_msg_option = 51239; 19 | } 20 | 21 | message MessageOptionsTestMsg { 22 | option (scalar_option) = "Hello world!"; 23 | option (repeated_scalar_option) = "A"; 24 | option (repeated_scalar_option) = "B"; 25 | option (repeated_scalar_option) = "C"; 26 | 27 | option (enum_option) = FOO3; 28 | option (repeated_enum_option) = FOO3; 29 | option (repeated_enum_option) = BAR3; 30 | 31 | option (msg_option).a_string = "Hello OuterMessage3"; 32 | option (repeated_msg_option) = {a_string: "Hello OuterMessage3 A"}; 33 | option (repeated_msg_option) = {a_string: "Hello OuterMessage3 B"}; 34 | } 35 | -------------------------------------------------------------------------------- /proto/testproto/test_no_generic_services.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | package test; 4 | 5 | message Simple3 { 6 | required string a_string = 1; 7 | } 8 | 9 | service ATestService2 { 10 | rpc Echo(Simple3) returns (Simple3) {} 11 | } 12 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=42", 4 | "wheel", 5 | ] 6 | build-backend = "setuptools.build_meta" 7 | 8 | [tool.black] 9 | extend-exclude = "(_pb2.py$|_pb2_grpc.py$)" 10 | # We don't care about line length for generated code 11 | line-length = 10000 12 | 13 | [tool.isort] 14 | profile = "black" 15 | skip_gitignore = true 16 | extend_skip_glob = ["*_pb2.py"] 17 | 18 | [tool.mypy] 19 | strict = true 20 | show_error_codes = true 21 | 22 | [tool.pyright] 23 | venvPath = "." 24 | venv = "venv_3.8.17" 25 | # verboseOutput = true 26 | extraPaths = ["test/generated"] 27 | include = [ 28 | "mypy_protobuf/", 29 | "test/" 30 | ] 31 | exclude = [ 32 | "**/*_pb2.py", 33 | "**/*_pb2_grpc.py" 34 | ] 35 | -------------------------------------------------------------------------------- /run_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | RED="\033[0;31m" 4 | NC='\033[0m' 5 | 6 | PY_VER_MYPY_PROTOBUF=${PY_VER_MYPY_PROTOBUF:=3.11.4} 7 | PY_VER_MYPY_PROTOBUF_SHORT=$(echo "$PY_VER_MYPY_PROTOBUF" | cut -d. -f1-2) 8 | PY_VER_MYPY=${PY_VER_MYPY:=3.8.17} 9 | PY_VER_UNIT_TESTS="${PY_VER_UNIT_TESTS:=3.8.17}" 10 | 11 | if [ -e "$CUSTOM_TYPESHED_DIR" ]; then 12 | export MYPYPATH=$CUSTOM_TYPESHED_DIR/stubs/protobuf 13 | fi 14 | 15 | # Install protoc 16 | PYTHON_PROTOBUF_VERSION=$(grep "^protobuf==" test_requirements.txt | cut -f3 -d=) 17 | PROTOBUF_VERSION=$(echo "$PYTHON_PROTOBUF_VERSION" | cut -f2-3 -d.) 18 | PROTOC_DIR="protoc_$PROTOBUF_VERSION" 19 | if [[ -z $SKIP_CLEAN ]] || [[ ! -e $PROTOC_DIR ]]; then 20 | if uname -a | grep Darwin; then 21 | # brew install coreutils wget 22 | PLAT=osx 23 | else 24 | PLAT=linux 25 | fi 26 | 27 | PROTOC_FILENAME="protoc-${PROTOBUF_VERSION}-${PLAT}-x86_64.zip" 28 | PROTOC_URL="https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOBUF_VERSION}/$PROTOC_FILENAME" 29 | 30 | rm -rf "$PROTOC_DIR" 31 | wget "$PROTOC_URL" -P "$PROTOC_DIR" 32 | mkdir -p "$PROTOC_DIR/protoc_install" 33 | unzip "$PROTOC_DIR/$PROTOC_FILENAME" -d "$PROTOC_DIR/protoc_install" 34 | fi 35 | PROTOC="$PROTOC_DIR/protoc_install/bin/protoc" 36 | if [[ $($PROTOC --version) != "libprotoc $PROTOBUF_VERSION" ]]; then 37 | echo -e "${RED}Wrong protoc installed?" 38 | exit 1 39 | fi 40 | 41 | PROTOC_ARGS=( --proto_path=proto/ --proto_path="$PROTOC_DIR/protoc_install/include" --experimental_allow_proto3_optional ) 42 | 43 | # Create mypy venv 44 | MYPY_VENV=venv_$PY_VER_MYPY 45 | ( 46 | eval "$(pyenv init --path)" 47 | eval "$(pyenv init -)" 48 | pyenv shell "$PY_VER_MYPY" 49 | 50 | if [[ -z $SKIP_CLEAN ]] || [[ ! -e $MYPY_VENV ]]; then 51 | python3 --version 52 | python3 -m pip --version 53 | python -m pip install virtualenv 54 | python3 -m virtualenv "$MYPY_VENV" 55 | "$MYPY_VENV"/bin/python3 -m pip install -r mypy_requirements.txt 56 | fi 57 | "$MYPY_VENV"/bin/mypy --version 58 | ) 59 | 60 | # Create unit tests venvs 61 | for PY_VER in $PY_VER_UNIT_TESTS; do 62 | ( 63 | UNIT_TESTS_VENV=venv_$PY_VER 64 | eval "$(pyenv init --path)" 65 | eval "$(pyenv init -)" 66 | pyenv shell "$PY_VER" 67 | 68 | if [[ -z $SKIP_CLEAN ]] || [[ ! -e $UNIT_TESTS_VENV ]]; then 69 | python -m pip install virtualenv 70 | python -m virtualenv "$UNIT_TESTS_VENV" 71 | "$UNIT_TESTS_VENV"/bin/python -m pip install -r test_requirements.txt 72 | fi 73 | "$UNIT_TESTS_VENV"/bin/py.test --version 74 | ) 75 | done 76 | 77 | # Create mypy-protobuf venv 78 | MYPY_PROTOBUF_VENV=venv_$PY_VER_MYPY_PROTOBUF 79 | ( 80 | eval "$(pyenv init --path)" 81 | eval "$(pyenv init -)" 82 | pyenv shell "$PY_VER_MYPY_PROTOBUF" 83 | 84 | # Create virtualenv + Install requirements for mypy-protobuf 85 | if [[ -z $SKIP_CLEAN ]] || [[ ! -e $MYPY_PROTOBUF_VENV ]]; then 86 | python -m pip install virtualenv 87 | python -m virtualenv "$MYPY_PROTOBUF_VENV" 88 | "$MYPY_PROTOBUF_VENV"/bin/python -m pip install -e . 89 | fi 90 | ) 91 | 92 | # Run mypy-protobuf 93 | ( 94 | source "$MYPY_PROTOBUF_VENV"/bin/activate 95 | 96 | # Confirm version number 97 | test "$(protoc-gen-mypy -V)" = "mypy-protobuf 3.6.0" 98 | test "$(protoc-gen-mypy --version)" = "mypy-protobuf 3.6.0" 99 | test "$(protoc-gen-mypy_grpc -V)" = "mypy-protobuf 3.6.0" 100 | test "$(protoc-gen-mypy_grpc --version)" = "mypy-protobuf 3.6.0" 101 | 102 | # Run mypy on mypy-protobuf internal code for developers to catch issues 103 | FILES="mypy_protobuf/main.py" 104 | "$MYPY_VENV/bin/mypy" --custom-typeshed-dir="$CUSTOM_TYPESHED_DIR" --python-executable="$MYPY_PROTOBUF_VENV/bin/python3" --python-version="$PY_VER_MYPY_PROTOBUF_SHORT" $FILES 105 | 106 | # Generate protos 107 | python --version 108 | $PROTOC --version 109 | 110 | # CI Check to make sure generated files are committed 111 | SHA_BEFORE=$(find test/generated -name "*.pyi" -print0 | xargs -0 sha1sum) 112 | # Clean out generated/ directory - except for __init__.py 113 | find test/generated -type f -not -name "__init__.py" -delete 114 | 115 | # Compile protoc -> python 116 | find proto -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --python_out=test/generated 117 | 118 | # Compile protoc -> mypy using mypy_protobuf 119 | # Prereq - create the mypy.proto python proto 120 | find proto/mypy_protobuf -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --python_out=. 121 | find proto/mypy_protobuf -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_out=. 122 | 123 | # Sanity check that our flags work 124 | find proto -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_out=quiet:test/generated 125 | find proto -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_out=readable_stubs:test/generated 126 | find proto -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_out=relax_strict_optional_primitives:test/generated 127 | # Overwrite w/ run with mypy-protobuf without flags 128 | find proto -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_out=test/generated 129 | 130 | # Generate grpc protos 131 | find proto/testproto/grpc -name "*.proto" -print0 | xargs -0 "$PROTOC" "${PROTOC_ARGS[@]}" --mypy_grpc_out=test/generated 132 | 133 | if [[ -n $VALIDATE ]] && ! diff <(echo "$SHA_BEFORE") <(find test/generated -name "*.pyi" -print0 | xargs -0 sha1sum); then 134 | echo -e "${RED}Some .pyi files did not match. Please commit those files${NC}" 135 | exit 1 136 | fi 137 | ) 138 | 139 | for PY_VER in $PY_VER_UNIT_TESTS; do 140 | UNIT_TESTS_VENV=venv_$PY_VER 141 | PY_VER_MYPY_TARGET=$(echo "$PY_VER" | cut -d. -f1-2) 142 | 143 | # Generate GRPC protos for mypy / tests 144 | ( 145 | source "$UNIT_TESTS_VENV"/bin/activate 146 | find proto/testproto/grpc -name "*.proto" -print0 | xargs -0 python -m grpc_tools.protoc "${PROTOC_ARGS[@]}" --grpc_python_out=test/generated 147 | ) 148 | 149 | # Run mypy on unit tests / generated output 150 | ( 151 | source "$MYPY_VENV"/bin/activate 152 | export MYPYPATH=$MYPYPATH:test/generated 153 | 154 | # Run mypy 155 | MODULES=( -m test.test_generated_mypy -m test.test_grpc_usage -m test.test_grpc_async_usage ) 156 | mypy --custom-typeshed-dir="$CUSTOM_TYPESHED_DIR" --python-executable="$UNIT_TESTS_VENV"/bin/python --python-version="$PY_VER_MYPY_TARGET" "${MODULES[@]}" 157 | 158 | # Run stubtest. Stubtest does not work with python impl - only cpp impl 159 | API_IMPL="$(python3 -c "import google.protobuf.internal.api_implementation as a ; print(a.Type())")" 160 | if [[ $API_IMPL != "python" ]]; then 161 | PYTHONPATH=test/generated python3 -m mypy.stubtest --custom-typeshed-dir="$CUSTOM_TYPESHED_DIR" --allowlist stubtest_allowlist.txt testproto 162 | fi 163 | 164 | # run mypy on negative-tests (expected mypy failures) 165 | NEGATIVE_MODULES=( -m test_negative.negative "${MODULES[@]}" ) 166 | 167 | MYPY_OUTPUT=$(mktemp -d) 168 | call_mypy() { 169 | # Write output to file. Make variant w/ omitted line numbers for easy diffing / CR 170 | PY_VER_MYPY_TARGET=$(echo "$1" | cut -d. -f1-2) 171 | export MYPYPATH=$MYPYPATH:test/generated 172 | # Use --no-incremental to avoid caching issues: https://github.com/python/mypy/issues/16363 173 | mypy --custom-typeshed-dir="$CUSTOM_TYPESHED_DIR" --python-executable="venv_$1/bin/python" --no-incremental --python-version="$PY_VER_MYPY_TARGET" "${@: 2}" > "$MYPY_OUTPUT/mypy_output" || true 174 | cut -d: -f1,3- "$MYPY_OUTPUT/mypy_output" > "$MYPY_OUTPUT/mypy_output.omit_linenos" 175 | } 176 | 177 | call_mypy "$PY_VER" "${NEGATIVE_MODULES[@]}" 178 | if ! diff "$MYPY_OUTPUT/mypy_output" "test_negative/output.expected.$PY_VER_MYPY_TARGET" || ! diff "$MYPY_OUTPUT/mypy_output.omit_linenos" "test_negative/output.expected.$PY_VER_MYPY_TARGET.omit_linenos"; then 179 | echo -e "${RED}test_negative/output.expected.$PY_VER_MYPY_TARGET didnt match. Copying over for you. Now rerun${NC}" 180 | 181 | # Copy over all the mypy results for the developer. 182 | call_mypy "$PY_VER" "${NEGATIVE_MODULES[@]}" 183 | cp "$MYPY_OUTPUT/mypy_output" test_negative/output.expected.3.8 184 | cp "$MYPY_OUTPUT/mypy_output.omit_linenos" test_negative/output.expected.3.8.omit_linenos 185 | exit 1 186 | fi 187 | ) 188 | 189 | ( 190 | # Run unit tests. 191 | source "$UNIT_TESTS_VENV"/bin/activate 192 | PYTHONPATH=test/generated py.test --ignore=test/generated -v 193 | ) 194 | done 195 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = mypy-protobuf 3 | version = attr: mypy_protobuf.main.__version__ 4 | description = Generate mypy stub files from protobuf specs 5 | keywords = mypy proto dropbox 6 | license = Apache License 2.0 7 | author = Nipunn Koorapati 8 | author_email = nipunn1313@gmail.com 9 | url = https://github.com/nipunn1313/mypy-protobuf 10 | download_url = https://github.com/nipunn1313/mypy-protobuf/releases 11 | 12 | [options] 13 | py_modules = 14 | mypy_protobuf 15 | mypy_protobuf.main 16 | mypy_protobuf.extensions_pb2 17 | install_requires = 18 | protobuf>=4.25.3 19 | types-protobuf>=4.24 20 | python_requires = >=3.8 21 | 22 | [options.entry_points] 23 | console_scripts = 24 | protoc-gen-mypy = mypy_protobuf.main:main 25 | protoc-gen-mypy_grpc = mypy_protobuf.main:grpc 26 | -------------------------------------------------------------------------------- /stubtest_allowlist.txt: -------------------------------------------------------------------------------- 1 | # Generated pb2 __init__ methods diverge because the runtime 2 | # has *args, **kwargs - and we're trying to do better 3 | testproto\..*_pb2\..*\.__init__$ 4 | 5 | # stubtest is confused by these edge cases 6 | testproto.test_pb2.PythonReservedKeywords.* 7 | testproto.test_pb2.None 8 | 9 | # Enum variants - stubtest doesn't like int varying from a NewType 10 | testproto.test_pb2.FOO 11 | testproto.test_pb2.BAR 12 | testproto.test_pb2.Simple1.INNER1 13 | testproto.test_pb2.Simple1.INNER2 14 | testproto.test_pb2.Name 15 | testproto.test_pb2.Value 16 | testproto.test_pb2.items 17 | testproto.test_pb2.keys 18 | testproto.test_pb2.values 19 | testproto.test3_pb2.SimpleProto3.INNER1 20 | testproto.test3_pb2.SimpleProto3.INNER2 21 | testproto.reexport_pb2.SimpleProto3.INNER1 22 | testproto.reexport_pb2.SimpleProto3.INNER2 23 | testproto.reexport_pb2.UNKNOWN 24 | testproto.reexport_pb2.FOO3 25 | testproto.reexport_pb2.BAR3 26 | testproto.test3_pb2.UNKNOWN 27 | testproto.test3_pb2.FOO3 28 | testproto.test3_pb2.BAR3 29 | testproto.nested.nested_pb2.AnotherNested.ONE 30 | testproto.nested.nested_pb2.AnotherNested.TWO 31 | testproto.readme_enum_pb2.HELLO 32 | testproto.readme_enum_pb2.WORLD 33 | testproto.nested.nested_pb2.AnotherNested.NestedMessage.UNDEFINED 34 | testproto.nested.nested_pb2.AnotherNested.NestedMessage.NESTED_ENUM1 35 | testproto.nested.nested_pb2.AnotherNested.NestedMessage.NESTED_ENUM2 36 | testproto.nested.nested_pb2.AnotherNested.INVALID 37 | 38 | # Our enum types and helper types aren't there at runtime (Dynamic EnumTypeWrapper at runtime) 39 | # .*\..*EnumTypeWrapper$ 40 | testproto.test_pb2.Simple1._?InnerEnum(EnumTypeWrapper)? 41 | testproto.test_pb2._?OuterEnum(EnumTypeWrapper)? 42 | testproto.test_pb2._?NamingConflicts(EnumTypeWrapper)? 43 | testproto.test3_pb2.SimpleProto3._?InnerEnum(EnumTypeWrapper)? 44 | testproto.test3_pb2._?OuterEnum(EnumTypeWrapper)? 45 | testproto.reexport_pb2.SimpleProto3._?InnerEnum(EnumTypeWrapper)? 46 | testproto.reexport_pb2._?OuterEnum(EnumTypeWrapper)? 47 | testproto.readme_enum_pb2._?MyEnum(EnumTypeWrapper)? 48 | testproto.nested.nested_pb2.AnotherNested._?NestedEnum(EnumTypeWrapper)? 49 | testproto.nested.nested_pb2.AnotherNested.NestedMessage._?NestedEnum2(EnumTypeWrapper)? 50 | 51 | # Our fake async stubs are not there at runtime (yet) 52 | testproto.grpc.dummy_pb2_grpc.DummyServiceAsyncStub 53 | testproto.grpc.import_pb2_grpc.SimpleServiceAsyncStub 54 | 55 | # Part of an "EXPERIMENTAL API" according to comment. Not documented. 56 | testproto.grpc.dummy_pb2_grpc.DummyService 57 | testproto.grpc.import_pb2_grpc.SimpleService 58 | 59 | # global prefix globals are generated, but aren't used at runtime 60 | # using `__` would be preferable, but Y047 will detect unused names. 61 | .*_pb2\.Global___.* 62 | 63 | # All readonly generated @property fields seem to trip up 64 | # stubtest. It claims they aren't available at runtime, when practically, 65 | # they actually are. 66 | # Eg: 67 | # error: testproto.test_pb2.Simple1.rep_inner_message is not present at runtime 68 | # 69 | testproto.Capitalized.Capitalized_pb2.Upper.Lower 70 | testproto.Capitalized.Capitalized_pb2.lower2.upper 71 | testproto.nopackage_pb2.NoPackage2.np 72 | testproto.nopackage_pb2.NoPackage2.np_rep 73 | testproto.reexport_pb2.SimpleProto3.MapMessageEntry.value 74 | testproto.reexport_pb2.SimpleProto3.OuterMessage3 75 | testproto.reexport_pb2.SimpleProto3.a_repeated_string 76 | testproto.reexport_pb2.SimpleProto3.bool 77 | testproto.reexport_pb2.SimpleProto3.email_by_uid 78 | testproto.reexport_pb2.SimpleProto3.map_message 79 | testproto.reexport_pb2.SimpleProto3.map_scalar 80 | testproto.reexport_pb2.SimpleProto3.outer_message 81 | testproto.reexport_pb2.SimpleProto3.outer_message_in_oneof 82 | testproto.test3_pb2.SimpleProto3.MapMessageEntry.value 83 | testproto.test3_pb2.SimpleProto3.OuterMessage3 84 | testproto.test3_pb2.SimpleProto3.a_repeated_string 85 | testproto.test3_pb2.SimpleProto3.bool 86 | testproto.test3_pb2.SimpleProto3.email_by_uid 87 | testproto.test3_pb2.SimpleProto3.map_message 88 | testproto.test3_pb2.SimpleProto3.map_scalar 89 | testproto.test3_pb2.SimpleProto3.outer_message 90 | testproto.test3_pb2.SimpleProto3.outer_message_in_oneof 91 | testproto.test_pb2.Simple1.a_inner 92 | testproto.test_pb2.Simple1.a_nested 93 | testproto.test_pb2.Simple1.a_repeated_string 94 | testproto.test_pb2.Simple1.email_by_uid 95 | testproto.test_pb2.Simple1.inner_message 96 | testproto.test_pb2.Simple1.nested_message 97 | testproto.test_pb2.Simple1.no_package 98 | testproto.test_pb2.Simple1.outer_message_in_oneof 99 | testproto.test_pb2.Simple1.rep_inner_enum 100 | testproto.test_pb2.Simple1.rep_inner_message 101 | 102 | # All messages now fail with something like this: 103 | # error: testproto.nested.nested_pb2.Nested is inconsistent, metaclass differs 104 | # Stub: at line 55 in file test/generated/testproto/nested/nested_pb2.pyi 105 | # N/A 106 | # Runtime: in file /Users/nipunn/src/mypy-protobuf/test/generated/testproto/nested/nested_pb2.py 107 | # 108 | # 109 | # For now, just allow all these errors until we come up with better silencing mechanisms or 110 | # alternately, better stubs for google._upb 111 | testproto.Capitalized.Capitalized_pb2.Upper 112 | testproto.Capitalized.Capitalized_pb2.lower 113 | testproto.Capitalized.Capitalized_pb2.lower2 114 | testproto.comment_special_chars_pb2.Test 115 | testproto.dot.com.test_pb2.TestMessage 116 | testproto.grpc.dummy_pb2.DummyReply 117 | testproto.grpc.dummy_pb2.DummyRequest 118 | testproto.inner.inner_pb2.Inner 119 | testproto.nested.nested_pb2.AnotherNested 120 | testproto.nested.nested_pb2.AnotherNested.NestedMessage 121 | testproto.nested.nested_pb2.Nested 122 | testproto.nopackage_pb2.NoPackage 123 | testproto.nopackage_pb2.NoPackage2 124 | testproto.reexport_pb2.Empty 125 | testproto.reexport_pb2.OuterMessage3 126 | testproto.reexport_pb2.SimpleProto3 127 | testproto.reexport_pb2.SimpleProto3.EmailByUidEntry 128 | testproto.reexport_pb2.SimpleProto3.MapMessageEntry 129 | testproto.reexport_pb2.SimpleProto3.MapScalarEntry 130 | testproto.test3_pb2.OuterMessage3 131 | testproto.test3_pb2.SimpleProto3 132 | testproto.test3_pb2.SimpleProto3.EmailByUidEntry 133 | testproto.test3_pb2.SimpleProto3.MapMessageEntry 134 | testproto.test3_pb2.SimpleProto3.MapScalarEntry 135 | testproto.test_extensions2_pb2.SeparateFileExtension 136 | testproto.test_extensions3_pb2.MessageOptionsTestMsg 137 | testproto.test_no_generic_services_pb2.Simple3 138 | testproto.test_pb2.Extensions1 139 | testproto.test_pb2.Extensions2 140 | testproto.test_pb2.SelfField 141 | testproto.test_pb2.Simple1 142 | testproto.test_pb2.Simple1.EmailByUidEntry 143 | testproto.test_pb2.Simple1.InnerMessage 144 | testproto.test_pb2.Simple2 145 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/test/__init__.py -------------------------------------------------------------------------------- /test/generated/google/protobuf/duration_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | Protocol Buffers - Google's data interchange format 5 | Copyright 2008 Google Inc. All rights reserved. 6 | https://developers.google.com/protocol-buffers/ 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are 10 | met: 11 | 12 | * Redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | * Redistributions in binary form must reproduce the above 15 | copyright notice, this list of conditions and the following disclaimer 16 | in the documentation and/or other materials provided with the 17 | distribution. 18 | * Neither the name of Google Inc. nor the names of its 19 | contributors may be used to endorse or promote products derived from 20 | this software without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | """ 34 | 35 | import builtins 36 | import google.protobuf.descriptor 37 | import google.protobuf.internal.well_known_types 38 | import google.protobuf.message 39 | import sys 40 | import typing 41 | 42 | if sys.version_info >= (3, 10): 43 | import typing as typing_extensions 44 | else: 45 | import typing_extensions 46 | 47 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 48 | 49 | @typing.final 50 | class Duration(google.protobuf.message.Message, google.protobuf.internal.well_known_types.Duration): 51 | """A Duration represents a signed, fixed-length span of time represented 52 | as a count of seconds and fractions of seconds at nanosecond 53 | resolution. It is independent of any calendar and concepts like "day" 54 | or "month". It is related to Timestamp in that the difference between 55 | two Timestamp values is a Duration and it can be added or subtracted 56 | from a Timestamp. Range is approximately +-10,000 years. 57 | 58 | # Examples 59 | 60 | Example 1: Compute Duration from two Timestamps in pseudo code. 61 | 62 | Timestamp start = ...; 63 | Timestamp end = ...; 64 | Duration duration = ...; 65 | 66 | duration.seconds = end.seconds - start.seconds; 67 | duration.nanos = end.nanos - start.nanos; 68 | 69 | if (duration.seconds < 0 && duration.nanos > 0) { 70 | duration.seconds += 1; 71 | duration.nanos -= 1000000000; 72 | } else if (duration.seconds > 0 && duration.nanos < 0) { 73 | duration.seconds -= 1; 74 | duration.nanos += 1000000000; 75 | } 76 | 77 | Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. 78 | 79 | Timestamp start = ...; 80 | Duration duration = ...; 81 | Timestamp end = ...; 82 | 83 | end.seconds = start.seconds + duration.seconds; 84 | end.nanos = start.nanos + duration.nanos; 85 | 86 | if (end.nanos < 0) { 87 | end.seconds -= 1; 88 | end.nanos += 1000000000; 89 | } else if (end.nanos >= 1000000000) { 90 | end.seconds += 1; 91 | end.nanos -= 1000000000; 92 | } 93 | 94 | Example 3: Compute Duration from datetime.timedelta in Python. 95 | 96 | td = datetime.timedelta(days=3, minutes=10) 97 | duration = Duration() 98 | duration.FromTimedelta(td) 99 | 100 | # JSON Mapping 101 | 102 | In JSON format, the Duration type is encoded as a string rather than an 103 | object, where the string ends in the suffix "s" (indicating seconds) and 104 | is preceded by the number of seconds, with nanoseconds expressed as 105 | fractional seconds. For example, 3 seconds with 0 nanoseconds should be 106 | encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should 107 | be expressed in JSON format as "3.000000001s", and 3 seconds and 1 108 | microsecond should be expressed in JSON format as "3.000001s". 109 | """ 110 | 111 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 112 | 113 | SECONDS_FIELD_NUMBER: builtins.int 114 | NANOS_FIELD_NUMBER: builtins.int 115 | seconds: builtins.int 116 | """Signed seconds of the span of time. Must be from -315,576,000,000 117 | to +315,576,000,000 inclusive. Note: these bounds are computed from: 118 | 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years 119 | """ 120 | nanos: builtins.int 121 | """Signed fractions of a second at nanosecond resolution of the span 122 | of time. Durations less than one second are represented with a 0 123 | `seconds` field and a positive or negative `nanos` field. For durations 124 | of one second or more, a non-zero value for the `nanos` field must be 125 | of the same sign as the `seconds` field. Must be from -999,999,999 126 | to +999,999,999 inclusive. 127 | """ 128 | def __init__( 129 | self, 130 | *, 131 | seconds: builtins.int = ..., 132 | nanos: builtins.int = ..., 133 | ) -> None: ... 134 | def ClearField(self, field_name: typing.Literal["nanos", b"nanos", "seconds", b"seconds"]) -> None: ... 135 | 136 | Global___Duration: typing_extensions.TypeAlias = Duration 137 | -------------------------------------------------------------------------------- /test/generated/mypy_protobuf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/test/generated/mypy_protobuf/__init__.py -------------------------------------------------------------------------------- /test/generated/mypy_protobuf/extensions_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.descriptor_pb2 9 | import google.protobuf.internal.extension_dict 10 | import google.protobuf.message 11 | import sys 12 | import typing 13 | 14 | if sys.version_info >= (3, 10): 15 | import typing as typing_extensions 16 | else: 17 | import typing_extensions 18 | 19 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 20 | 21 | @typing.final 22 | class FieldOptions(google.protobuf.message.Message): 23 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 24 | 25 | CASTTYPE_FIELD_NUMBER: builtins.int 26 | KEYTYPE_FIELD_NUMBER: builtins.int 27 | VALUETYPE_FIELD_NUMBER: builtins.int 28 | casttype: builtins.str 29 | """Tells mypy-protobuf to use a specific newtype rather than the normal type for this field.""" 30 | keytype: builtins.str 31 | """Tells mypy-protobuf to use a specific type for keys; only makes sense on map fields""" 32 | valuetype: builtins.str 33 | """Tells mypy-protobuf to use a specific type for values; only makes sense on map fields""" 34 | def __init__( 35 | self, 36 | *, 37 | casttype: builtins.str = ..., 38 | keytype: builtins.str = ..., 39 | valuetype: builtins.str = ..., 40 | ) -> None: ... 41 | def ClearField(self, field_name: typing.Literal["casttype", b"casttype", "keytype", b"keytype", "valuetype", b"valuetype"]) -> None: ... 42 | 43 | Global___FieldOptions: typing_extensions.TypeAlias = FieldOptions 44 | 45 | OPTIONS_FIELD_NUMBER: builtins.int 46 | CASTTYPE_FIELD_NUMBER: builtins.int 47 | KEYTYPE_FIELD_NUMBER: builtins.int 48 | VALUETYPE_FIELD_NUMBER: builtins.int 49 | options: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, Global___FieldOptions] 50 | """Custom field options from mypy-protobuf""" 51 | casttype: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, builtins.str] 52 | """Legacy fields. Prefer to use ones within `options` instead.""" 53 | keytype: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, builtins.str] 54 | valuetype: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, builtins.str] 55 | -------------------------------------------------------------------------------- /test/generated/testproto/Capitalized/Capitalized_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import sys 10 | import typing 11 | 12 | if sys.version_info >= (3, 10): 13 | import typing as typing_extensions 14 | else: 15 | import typing_extensions 16 | 17 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 18 | 19 | @typing.final 20 | class lower(google.protobuf.message.Message): 21 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 22 | 23 | A_FIELD_NUMBER: builtins.int 24 | a: builtins.int 25 | def __init__( 26 | self, 27 | *, 28 | a: builtins.int = ..., 29 | ) -> None: ... 30 | def ClearField(self, field_name: typing.Literal["a", b"a"]) -> None: ... 31 | 32 | Global___lower: typing_extensions.TypeAlias = lower 33 | 34 | @typing.final 35 | class Upper(google.protobuf.message.Message): 36 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 37 | 38 | LOWER_FIELD_NUMBER: builtins.int 39 | @property 40 | def Lower(self) -> Global___lower: ... 41 | def __init__( 42 | self, 43 | *, 44 | Lower: Global___lower | None = ..., 45 | ) -> None: ... 46 | def HasField(self, field_name: typing.Literal["Lower", b"Lower"]) -> builtins.bool: ... 47 | def ClearField(self, field_name: typing.Literal["Lower", b"Lower"]) -> None: ... 48 | 49 | Global___Upper: typing_extensions.TypeAlias = Upper 50 | 51 | @typing.final 52 | class lower2(google.protobuf.message.Message): 53 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 54 | 55 | UPPER_FIELD_NUMBER: builtins.int 56 | @property 57 | def upper(self) -> Global___Upper: ... 58 | def __init__( 59 | self, 60 | *, 61 | upper: Global___Upper | None = ..., 62 | ) -> None: ... 63 | def HasField(self, field_name: typing.Literal["upper", b"upper"]) -> builtins.bool: ... 64 | def ClearField(self, field_name: typing.Literal["upper", b"upper"]) -> None: ... 65 | 66 | Global___lower2: typing_extensions.TypeAlias = lower2 67 | -------------------------------------------------------------------------------- /test/generated/testproto/Capitalized/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/test/generated/testproto/Capitalized/__init__.py -------------------------------------------------------------------------------- /test/generated/testproto/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/test/generated/testproto/__init__.py -------------------------------------------------------------------------------- /test/generated/testproto/comment_special_chars_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import sys 10 | import typing 11 | 12 | if sys.version_info >= (3, 10): 13 | import typing as typing_extensions 14 | else: 15 | import typing_extensions 16 | 17 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 18 | 19 | @typing.final 20 | class Test(google.protobuf.message.Message): 21 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 22 | 23 | A_FIELD_NUMBER: builtins.int 24 | B_FIELD_NUMBER: builtins.int 25 | C_FIELD_NUMBER: builtins.int 26 | D_FIELD_NUMBER: builtins.int 27 | E_FIELD_NUMBER: builtins.int 28 | F_FIELD_NUMBER: builtins.int 29 | G_FIELD_NUMBER: builtins.int 30 | H_FIELD_NUMBER: builtins.int 31 | I_FIELD_NUMBER: builtins.int 32 | J_FIELD_NUMBER: builtins.int 33 | K_FIELD_NUMBER: builtins.int 34 | a: builtins.str 35 | """Ending with " """ 36 | b: builtins.str 37 | """Ending with "" """ 38 | c: builtins.str 39 | """Ending with \"\"\" """ 40 | d: builtins.str 41 | """Ending with \\ """ 42 | e: builtins.str 43 | """Containing bad escape: \\x""" 44 | f: builtins.str 45 | """Containing \"\"\"" quadruple""" 46 | g: builtins.str 47 | """Containing \"\"\""" quintuple""" 48 | h: builtins.str 49 | """Containing \"\"\"\"\"\" sextuple""" 50 | i: builtins.str 51 | """\"\"\" Multiple \"\"\" triples \"\"\" """ 52 | j: builtins.str 53 | """"quotes" can be a problem in comments. 54 | \"\"\"Triple quotes\"\"\" just as well 55 | """ 56 | k: builtins.str 57 | """\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" 58 | " " 59 | " Super Duper comments with surrounding edges! " 60 | " " 61 | " Pay attention to me!!!! " 62 | " " 63 | \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\" 64 | """ 65 | def __init__( 66 | self, 67 | *, 68 | a: builtins.str = ..., 69 | b: builtins.str = ..., 70 | c: builtins.str = ..., 71 | d: builtins.str = ..., 72 | e: builtins.str = ..., 73 | f: builtins.str = ..., 74 | g: builtins.str = ..., 75 | h: builtins.str = ..., 76 | i: builtins.str = ..., 77 | j: builtins.str = ..., 78 | k: builtins.str = ..., 79 | ) -> None: ... 80 | def ClearField(self, field_name: typing.Literal["a", b"a", "b", b"b", "c", b"c", "d", b"d", "e", b"e", "f", b"f", "g", b"g", "h", b"h", "i", b"i", "j", b"j", "k", b"k"]) -> None: ... 81 | 82 | Global___Test: typing_extensions.TypeAlias = Test 83 | -------------------------------------------------------------------------------- /test/generated/testproto/dot/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/test/generated/testproto/dot/__init__.py -------------------------------------------------------------------------------- /test/generated/testproto/dot/com/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/test/generated/testproto/dot/com/__init__.py -------------------------------------------------------------------------------- /test/generated/testproto/dot/com/test_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import sys 10 | import typing 11 | 12 | if sys.version_info >= (3, 10): 13 | import typing as typing_extensions 14 | else: 15 | import typing_extensions 16 | 17 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 18 | 19 | @typing.final 20 | class TestMessage(google.protobuf.message.Message): 21 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 22 | 23 | FOO_FIELD_NUMBER: builtins.int 24 | foo: builtins.str 25 | def __init__( 26 | self, 27 | *, 28 | foo: builtins.str = ..., 29 | ) -> None: ... 30 | def ClearField(self, field_name: typing.Literal["foo", b"foo"]) -> None: ... 31 | 32 | Global___TestMessage: typing_extensions.TypeAlias = TestMessage 33 | -------------------------------------------------------------------------------- /test/generated/testproto/grpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/test/generated/testproto/grpc/__init__.py -------------------------------------------------------------------------------- /test/generated/testproto/grpc/dummy_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | https://github.com/vmagamedov/grpclib/blob/master/tests/dummy.proto""" 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import sys 10 | import typing 11 | 12 | if sys.version_info >= (3, 10): 13 | import typing as typing_extensions 14 | else: 15 | import typing_extensions 16 | 17 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 18 | 19 | @typing.final 20 | class DummyRequest(google.protobuf.message.Message): 21 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 22 | 23 | VALUE_FIELD_NUMBER: builtins.int 24 | value: builtins.str 25 | def __init__( 26 | self, 27 | *, 28 | value: builtins.str = ..., 29 | ) -> None: ... 30 | def ClearField(self, field_name: typing.Literal["value", b"value"]) -> None: ... 31 | 32 | Global___DummyRequest: typing_extensions.TypeAlias = DummyRequest 33 | 34 | @typing.final 35 | class DummyReply(google.protobuf.message.Message): 36 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 37 | 38 | VALUE_FIELD_NUMBER: builtins.int 39 | value: builtins.str 40 | def __init__( 41 | self, 42 | *, 43 | value: builtins.str = ..., 44 | ) -> None: ... 45 | def ClearField(self, field_name: typing.Literal["value", b"value"]) -> None: ... 46 | 47 | Global___DummyReply: typing_extensions.TypeAlias = DummyReply 48 | -------------------------------------------------------------------------------- /test/generated/testproto/grpc/dummy_pb2_grpc.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | https://github.com/vmagamedov/grpclib/blob/master/tests/dummy.proto""" 5 | 6 | import abc 7 | import collections.abc 8 | import grpc 9 | import grpc.aio 10 | import sys 11 | import testproto.grpc.dummy_pb2 12 | import typing 13 | 14 | if sys.version_info >= (3, 13): 15 | import typing as typing_extensions 16 | else: 17 | import typing_extensions 18 | 19 | _T = typing.TypeVar("_T") 20 | 21 | class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta): ... 22 | 23 | class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore[misc, type-arg] 24 | ... 25 | 26 | GRPC_GENERATED_VERSION: str 27 | GRPC_VERSION: str 28 | _DummyServiceUnaryUnaryType = typing_extensions.TypeVar( 29 | '_DummyServiceUnaryUnaryType', 30 | grpc.UnaryUnaryMultiCallable[ 31 | testproto.grpc.dummy_pb2.DummyRequest, 32 | testproto.grpc.dummy_pb2.DummyReply, 33 | ], 34 | grpc.aio.UnaryUnaryMultiCallable[ 35 | testproto.grpc.dummy_pb2.DummyRequest, 36 | testproto.grpc.dummy_pb2.DummyReply, 37 | ], 38 | default=grpc.UnaryUnaryMultiCallable[ 39 | testproto.grpc.dummy_pb2.DummyRequest, 40 | testproto.grpc.dummy_pb2.DummyReply, 41 | ], 42 | ) 43 | 44 | _DummyServiceUnaryStreamType = typing_extensions.TypeVar( 45 | '_DummyServiceUnaryStreamType', 46 | grpc.UnaryStreamMultiCallable[ 47 | testproto.grpc.dummy_pb2.DummyRequest, 48 | testproto.grpc.dummy_pb2.DummyReply, 49 | ], 50 | grpc.aio.UnaryStreamMultiCallable[ 51 | testproto.grpc.dummy_pb2.DummyRequest, 52 | testproto.grpc.dummy_pb2.DummyReply, 53 | ], 54 | default=grpc.UnaryStreamMultiCallable[ 55 | testproto.grpc.dummy_pb2.DummyRequest, 56 | testproto.grpc.dummy_pb2.DummyReply, 57 | ], 58 | ) 59 | 60 | _DummyServiceStreamUnaryType = typing_extensions.TypeVar( 61 | '_DummyServiceStreamUnaryType', 62 | grpc.StreamUnaryMultiCallable[ 63 | testproto.grpc.dummy_pb2.DummyRequest, 64 | testproto.grpc.dummy_pb2.DummyReply, 65 | ], 66 | grpc.aio.StreamUnaryMultiCallable[ 67 | testproto.grpc.dummy_pb2.DummyRequest, 68 | testproto.grpc.dummy_pb2.DummyReply, 69 | ], 70 | default=grpc.StreamUnaryMultiCallable[ 71 | testproto.grpc.dummy_pb2.DummyRequest, 72 | testproto.grpc.dummy_pb2.DummyReply, 73 | ], 74 | ) 75 | 76 | _DummyServiceStreamStreamType = typing_extensions.TypeVar( 77 | '_DummyServiceStreamStreamType', 78 | grpc.StreamStreamMultiCallable[ 79 | testproto.grpc.dummy_pb2.DummyRequest, 80 | testproto.grpc.dummy_pb2.DummyReply, 81 | ], 82 | grpc.aio.StreamStreamMultiCallable[ 83 | testproto.grpc.dummy_pb2.DummyRequest, 84 | testproto.grpc.dummy_pb2.DummyReply, 85 | ], 86 | default=grpc.StreamStreamMultiCallable[ 87 | testproto.grpc.dummy_pb2.DummyRequest, 88 | testproto.grpc.dummy_pb2.DummyReply, 89 | ], 90 | ) 91 | 92 | class DummyServiceStub(typing.Generic[_DummyServiceUnaryUnaryType, _DummyServiceUnaryStreamType, _DummyServiceStreamUnaryType, _DummyServiceStreamStreamType]): 93 | """DummyService""" 94 | 95 | @typing.overload 96 | def __init__(self: DummyServiceStub[ 97 | grpc.UnaryUnaryMultiCallable[ 98 | testproto.grpc.dummy_pb2.DummyRequest, 99 | testproto.grpc.dummy_pb2.DummyReply, 100 | ], 101 | grpc.UnaryStreamMultiCallable[ 102 | testproto.grpc.dummy_pb2.DummyRequest, 103 | testproto.grpc.dummy_pb2.DummyReply, 104 | ], 105 | grpc.StreamUnaryMultiCallable[ 106 | testproto.grpc.dummy_pb2.DummyRequest, 107 | testproto.grpc.dummy_pb2.DummyReply, 108 | ], 109 | grpc.StreamStreamMultiCallable[ 110 | testproto.grpc.dummy_pb2.DummyRequest, 111 | testproto.grpc.dummy_pb2.DummyReply, 112 | ], 113 | ], channel: grpc.Channel) -> None: ... 114 | 115 | @typing.overload 116 | def __init__(self: DummyServiceStub[ 117 | grpc.aio.UnaryUnaryMultiCallable[ 118 | testproto.grpc.dummy_pb2.DummyRequest, 119 | testproto.grpc.dummy_pb2.DummyReply, 120 | ], 121 | grpc.aio.UnaryStreamMultiCallable[ 122 | testproto.grpc.dummy_pb2.DummyRequest, 123 | testproto.grpc.dummy_pb2.DummyReply, 124 | ], 125 | grpc.aio.StreamUnaryMultiCallable[ 126 | testproto.grpc.dummy_pb2.DummyRequest, 127 | testproto.grpc.dummy_pb2.DummyReply, 128 | ], 129 | grpc.aio.StreamStreamMultiCallable[ 130 | testproto.grpc.dummy_pb2.DummyRequest, 131 | testproto.grpc.dummy_pb2.DummyReply, 132 | ], 133 | ], channel: grpc.aio.Channel) -> None: ... 134 | 135 | UnaryUnary: _DummyServiceUnaryUnaryType 136 | """UnaryUnary""" 137 | 138 | UnaryStream: _DummyServiceUnaryStreamType 139 | """UnaryStream""" 140 | 141 | StreamUnary: _DummyServiceStreamUnaryType 142 | """StreamUnary""" 143 | 144 | StreamStream: _DummyServiceStreamStreamType 145 | """StreamStream""" 146 | 147 | DummyServiceAsyncStub: typing_extensions.TypeAlias = DummyServiceStub[ 148 | grpc.aio.UnaryUnaryMultiCallable[ 149 | testproto.grpc.dummy_pb2.DummyRequest, 150 | testproto.grpc.dummy_pb2.DummyReply, 151 | ], 152 | grpc.aio.UnaryStreamMultiCallable[ 153 | testproto.grpc.dummy_pb2.DummyRequest, 154 | testproto.grpc.dummy_pb2.DummyReply, 155 | ], 156 | grpc.aio.StreamUnaryMultiCallable[ 157 | testproto.grpc.dummy_pb2.DummyRequest, 158 | testproto.grpc.dummy_pb2.DummyReply, 159 | ], 160 | grpc.aio.StreamStreamMultiCallable[ 161 | testproto.grpc.dummy_pb2.DummyRequest, 162 | testproto.grpc.dummy_pb2.DummyReply, 163 | ], 164 | ] 165 | 166 | class DummyServiceServicer(metaclass=abc.ABCMeta): 167 | """DummyService""" 168 | 169 | @abc.abstractmethod 170 | def UnaryUnary( 171 | self, 172 | request: testproto.grpc.dummy_pb2.DummyRequest, 173 | context: _ServicerContext, 174 | ) -> typing.Union[testproto.grpc.dummy_pb2.DummyReply, collections.abc.Awaitable[testproto.grpc.dummy_pb2.DummyReply]]: 175 | """UnaryUnary""" 176 | 177 | @abc.abstractmethod 178 | def UnaryStream( 179 | self, 180 | request: testproto.grpc.dummy_pb2.DummyRequest, 181 | context: _ServicerContext, 182 | ) -> typing.Union[collections.abc.Iterator[testproto.grpc.dummy_pb2.DummyReply], collections.abc.AsyncIterator[testproto.grpc.dummy_pb2.DummyReply]]: 183 | """UnaryStream""" 184 | 185 | @abc.abstractmethod 186 | def StreamUnary( 187 | self, 188 | request_iterator: _MaybeAsyncIterator[testproto.grpc.dummy_pb2.DummyRequest], 189 | context: _ServicerContext, 190 | ) -> typing.Union[testproto.grpc.dummy_pb2.DummyReply, collections.abc.Awaitable[testproto.grpc.dummy_pb2.DummyReply]]: 191 | """StreamUnary""" 192 | 193 | @abc.abstractmethod 194 | def StreamStream( 195 | self, 196 | request_iterator: _MaybeAsyncIterator[testproto.grpc.dummy_pb2.DummyRequest], 197 | context: _ServicerContext, 198 | ) -> typing.Union[collections.abc.Iterator[testproto.grpc.dummy_pb2.DummyReply], collections.abc.AsyncIterator[testproto.grpc.dummy_pb2.DummyReply]]: 199 | """StreamStream""" 200 | 201 | def add_DummyServiceServicer_to_server(servicer: DummyServiceServicer, server: typing.Union[grpc.Server, grpc.aio.Server]) -> None: ... 202 | -------------------------------------------------------------------------------- /test/generated/testproto/grpc/import_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import google.protobuf.descriptor 7 | 8 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 9 | -------------------------------------------------------------------------------- /test/generated/testproto/grpc/import_pb2_grpc.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import abc 7 | import collections.abc 8 | import google.protobuf.empty_pb2 9 | import grpc 10 | import grpc.aio 11 | import sys 12 | import testproto.test_pb2 13 | import typing 14 | 15 | if sys.version_info >= (3, 13): 16 | import typing as typing_extensions 17 | else: 18 | import typing_extensions 19 | 20 | _T = typing.TypeVar("_T") 21 | 22 | class _MaybeAsyncIterator(collections.abc.AsyncIterator[_T], collections.abc.Iterator[_T], metaclass=abc.ABCMeta): ... 23 | 24 | class _ServicerContext(grpc.ServicerContext, grpc.aio.ServicerContext): # type: ignore[misc, type-arg] 25 | ... 26 | 27 | GRPC_GENERATED_VERSION: str 28 | GRPC_VERSION: str 29 | _SimpleServiceUnaryUnaryType = typing_extensions.TypeVar( 30 | '_SimpleServiceUnaryUnaryType', 31 | grpc.UnaryUnaryMultiCallable[ 32 | google.protobuf.empty_pb2.Empty, 33 | testproto.test_pb2.Simple1, 34 | ], 35 | grpc.aio.UnaryUnaryMultiCallable[ 36 | google.protobuf.empty_pb2.Empty, 37 | testproto.test_pb2.Simple1, 38 | ], 39 | default=grpc.UnaryUnaryMultiCallable[ 40 | google.protobuf.empty_pb2.Empty, 41 | testproto.test_pb2.Simple1, 42 | ], 43 | ) 44 | 45 | _SimpleServiceUnaryStreamType = typing_extensions.TypeVar( 46 | '_SimpleServiceUnaryStreamType', 47 | grpc.UnaryUnaryMultiCallable[ 48 | testproto.test_pb2.Simple1, 49 | google.protobuf.empty_pb2.Empty, 50 | ], 51 | grpc.aio.UnaryUnaryMultiCallable[ 52 | testproto.test_pb2.Simple1, 53 | google.protobuf.empty_pb2.Empty, 54 | ], 55 | default=grpc.UnaryUnaryMultiCallable[ 56 | testproto.test_pb2.Simple1, 57 | google.protobuf.empty_pb2.Empty, 58 | ], 59 | ) 60 | 61 | _SimpleServiceNoCommentType = typing_extensions.TypeVar( 62 | '_SimpleServiceNoCommentType', 63 | grpc.UnaryUnaryMultiCallable[ 64 | testproto.test_pb2.Simple1, 65 | google.protobuf.empty_pb2.Empty, 66 | ], 67 | grpc.aio.UnaryUnaryMultiCallable[ 68 | testproto.test_pb2.Simple1, 69 | google.protobuf.empty_pb2.Empty, 70 | ], 71 | default=grpc.UnaryUnaryMultiCallable[ 72 | testproto.test_pb2.Simple1, 73 | google.protobuf.empty_pb2.Empty, 74 | ], 75 | ) 76 | 77 | class SimpleServiceStub(typing.Generic[_SimpleServiceUnaryUnaryType, _SimpleServiceUnaryStreamType, _SimpleServiceNoCommentType]): 78 | """SimpleService""" 79 | 80 | @typing.overload 81 | def __init__(self: SimpleServiceStub[ 82 | grpc.UnaryUnaryMultiCallable[ 83 | google.protobuf.empty_pb2.Empty, 84 | testproto.test_pb2.Simple1, 85 | ], 86 | grpc.UnaryUnaryMultiCallable[ 87 | testproto.test_pb2.Simple1, 88 | google.protobuf.empty_pb2.Empty, 89 | ], 90 | grpc.UnaryUnaryMultiCallable[ 91 | testproto.test_pb2.Simple1, 92 | google.protobuf.empty_pb2.Empty, 93 | ], 94 | ], channel: grpc.Channel) -> None: ... 95 | 96 | @typing.overload 97 | def __init__(self: SimpleServiceStub[ 98 | grpc.aio.UnaryUnaryMultiCallable[ 99 | google.protobuf.empty_pb2.Empty, 100 | testproto.test_pb2.Simple1, 101 | ], 102 | grpc.aio.UnaryUnaryMultiCallable[ 103 | testproto.test_pb2.Simple1, 104 | google.protobuf.empty_pb2.Empty, 105 | ], 106 | grpc.aio.UnaryUnaryMultiCallable[ 107 | testproto.test_pb2.Simple1, 108 | google.protobuf.empty_pb2.Empty, 109 | ], 110 | ], channel: grpc.aio.Channel) -> None: ... 111 | 112 | UnaryUnary: _SimpleServiceUnaryUnaryType 113 | """UnaryUnary""" 114 | 115 | UnaryStream: _SimpleServiceUnaryStreamType 116 | """UnaryStream""" 117 | 118 | NoComment: _SimpleServiceNoCommentType 119 | 120 | SimpleServiceAsyncStub: typing_extensions.TypeAlias = SimpleServiceStub[ 121 | grpc.aio.UnaryUnaryMultiCallable[ 122 | google.protobuf.empty_pb2.Empty, 123 | testproto.test_pb2.Simple1, 124 | ], 125 | grpc.aio.UnaryUnaryMultiCallable[ 126 | testproto.test_pb2.Simple1, 127 | google.protobuf.empty_pb2.Empty, 128 | ], 129 | grpc.aio.UnaryUnaryMultiCallable[ 130 | testproto.test_pb2.Simple1, 131 | google.protobuf.empty_pb2.Empty, 132 | ], 133 | ] 134 | 135 | class SimpleServiceServicer(metaclass=abc.ABCMeta): 136 | """SimpleService""" 137 | 138 | @abc.abstractmethod 139 | def UnaryUnary( 140 | self, 141 | request: google.protobuf.empty_pb2.Empty, 142 | context: _ServicerContext, 143 | ) -> typing.Union[testproto.test_pb2.Simple1, collections.abc.Awaitable[testproto.test_pb2.Simple1]]: 144 | """UnaryUnary""" 145 | 146 | @abc.abstractmethod 147 | def UnaryStream( 148 | self, 149 | request: testproto.test_pb2.Simple1, 150 | context: _ServicerContext, 151 | ) -> typing.Union[google.protobuf.empty_pb2.Empty, collections.abc.Awaitable[google.protobuf.empty_pb2.Empty]]: 152 | """UnaryStream""" 153 | 154 | @abc.abstractmethod 155 | def NoComment( 156 | self, 157 | request: testproto.test_pb2.Simple1, 158 | context: _ServicerContext, 159 | ) -> typing.Union[google.protobuf.empty_pb2.Empty, collections.abc.Awaitable[google.protobuf.empty_pb2.Empty]]: ... 160 | 161 | def add_SimpleServiceServicer_to_server(servicer: SimpleServiceServicer, server: typing.Union[grpc.Server, grpc.aio.Server]) -> None: ... 162 | -------------------------------------------------------------------------------- /test/generated/testproto/inner/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/test/generated/testproto/inner/__init__.py -------------------------------------------------------------------------------- /test/generated/testproto/inner/inner_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import sys 10 | import testproto.test3_pb2 11 | import typing 12 | 13 | if sys.version_info >= (3, 10): 14 | import typing as typing_extensions 15 | else: 16 | import typing_extensions 17 | 18 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 19 | 20 | @typing.final 21 | class Inner(google.protobuf.message.Message): 22 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 23 | 24 | A_FIELD_NUMBER: builtins.int 25 | a: testproto.test3_pb2.OuterEnum.ValueType 26 | def __init__( 27 | self, 28 | *, 29 | a: testproto.test3_pb2.OuterEnum.ValueType = ..., 30 | ) -> None: ... 31 | def ClearField(self, field_name: typing.Literal["a", b"a"]) -> None: ... 32 | 33 | Global___Inner: typing_extensions.TypeAlias = Inner 34 | -------------------------------------------------------------------------------- /test/generated/testproto/nested/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/test/generated/testproto/nested/__init__.py -------------------------------------------------------------------------------- /test/generated/testproto/nested/nested_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.enum_type_wrapper 9 | import google.protobuf.message 10 | import sys 11 | import testproto.test3_pb2 12 | import typing 13 | 14 | if sys.version_info >= (3, 10): 15 | import typing as typing_extensions 16 | else: 17 | import typing_extensions 18 | 19 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 20 | 21 | @typing.final 22 | class Nested(google.protobuf.message.Message): 23 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 24 | 25 | A_FIELD_NUMBER: builtins.int 26 | a: testproto.test3_pb2.OuterEnum.ValueType 27 | def __init__( 28 | self, 29 | *, 30 | a: testproto.test3_pb2.OuterEnum.ValueType = ..., 31 | ) -> None: ... 32 | def ClearField(self, field_name: typing.Literal["a", b"a"]) -> None: ... 33 | 34 | Global___Nested: typing_extensions.TypeAlias = Nested 35 | 36 | @typing.final 37 | class AnotherNested(google.protobuf.message.Message): 38 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 39 | 40 | class _NestedEnum: 41 | ValueType = typing.NewType("ValueType", builtins.int) 42 | V: typing_extensions.TypeAlias = ValueType 43 | 44 | class _NestedEnumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[AnotherNested._NestedEnum.ValueType], builtins.type): 45 | DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor 46 | INVALID: AnotherNested._NestedEnum.ValueType # 0 47 | ONE: AnotherNested._NestedEnum.ValueType # 1 48 | TWO: AnotherNested._NestedEnum.ValueType # 2 49 | 50 | class NestedEnum(_NestedEnum, metaclass=_NestedEnumEnumTypeWrapper): ... 51 | INVALID: AnotherNested.NestedEnum.ValueType # 0 52 | ONE: AnotherNested.NestedEnum.ValueType # 1 53 | TWO: AnotherNested.NestedEnum.ValueType # 2 54 | 55 | @typing.final 56 | class NestedMessage(google.protobuf.message.Message): 57 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 58 | 59 | class _NestedEnum2: 60 | ValueType = typing.NewType("ValueType", builtins.int) 61 | V: typing_extensions.TypeAlias = ValueType 62 | 63 | class _NestedEnum2EnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[AnotherNested.NestedMessage._NestedEnum2.ValueType], builtins.type): 64 | DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor 65 | UNDEFINED: AnotherNested.NestedMessage._NestedEnum2.ValueType # 0 66 | NESTED_ENUM1: AnotherNested.NestedMessage._NestedEnum2.ValueType # 1 67 | NESTED_ENUM2: AnotherNested.NestedMessage._NestedEnum2.ValueType # 2 68 | 69 | class NestedEnum2(_NestedEnum2, metaclass=_NestedEnum2EnumTypeWrapper): ... 70 | UNDEFINED: AnotherNested.NestedMessage.NestedEnum2.ValueType # 0 71 | NESTED_ENUM1: AnotherNested.NestedMessage.NestedEnum2.ValueType # 1 72 | NESTED_ENUM2: AnotherNested.NestedMessage.NestedEnum2.ValueType # 2 73 | 74 | S_FIELD_NUMBER: builtins.int 75 | B_FIELD_NUMBER: builtins.int 76 | NE_FIELD_NUMBER: builtins.int 77 | NE2_FIELD_NUMBER: builtins.int 78 | s: builtins.str 79 | b: builtins.bool 80 | ne: Global___AnotherNested.NestedEnum.ValueType 81 | ne2: Global___AnotherNested.NestedMessage.NestedEnum2.ValueType 82 | def __init__( 83 | self, 84 | *, 85 | s: builtins.str = ..., 86 | b: builtins.bool = ..., 87 | ne: Global___AnotherNested.NestedEnum.ValueType = ..., 88 | ne2: Global___AnotherNested.NestedMessage.NestedEnum2.ValueType = ..., 89 | ) -> None: ... 90 | def ClearField(self, field_name: typing.Literal["b", b"b", "ne", b"ne", "ne2", b"ne2", "s", b"s"]) -> None: ... 91 | 92 | def __init__( 93 | self, 94 | ) -> None: ... 95 | 96 | Global___AnotherNested: typing_extensions.TypeAlias = AnotherNested 97 | -------------------------------------------------------------------------------- /test/generated/testproto/nopackage_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import collections.abc 8 | import google.protobuf.descriptor 9 | import google.protobuf.internal.containers 10 | import google.protobuf.message 11 | import sys 12 | import typing 13 | 14 | if sys.version_info >= (3, 10): 15 | import typing as typing_extensions 16 | else: 17 | import typing_extensions 18 | 19 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 20 | 21 | @typing.final 22 | class NoPackage(google.protobuf.message.Message): 23 | """Intentionally don't set a package - just to make sure we can handle it.""" 24 | 25 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 26 | 27 | def __init__( 28 | self, 29 | ) -> None: ... 30 | 31 | Global___NoPackage: typing_extensions.TypeAlias = NoPackage 32 | 33 | @typing.final 34 | class NoPackage2(google.protobuf.message.Message): 35 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 36 | 37 | NP_FIELD_NUMBER: builtins.int 38 | NP_REP_FIELD_NUMBER: builtins.int 39 | @property 40 | def np(self) -> Global___NoPackage: ... 41 | @property 42 | def np_rep(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[Global___NoPackage]: ... 43 | def __init__( 44 | self, 45 | *, 46 | np: Global___NoPackage | None = ..., 47 | np_rep: collections.abc.Iterable[Global___NoPackage] | None = ..., 48 | ) -> None: ... 49 | def HasField(self, field_name: typing.Literal["np", b"np"]) -> builtins.bool: ... 50 | def ClearField(self, field_name: typing.Literal["np", b"np", "np_rep", b"np_rep"]) -> None: ... 51 | 52 | Global___NoPackage2: typing_extensions.TypeAlias = NoPackage2 53 | -------------------------------------------------------------------------------- /test/generated/testproto/readme_enum_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.enum_type_wrapper 9 | import sys 10 | import typing 11 | 12 | if sys.version_info >= (3, 10): 13 | import typing as typing_extensions 14 | else: 15 | import typing_extensions 16 | 17 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 18 | 19 | class _MyEnum: 20 | ValueType = typing.NewType("ValueType", builtins.int) 21 | V: typing_extensions.TypeAlias = ValueType 22 | 23 | class _MyEnumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MyEnum.ValueType], builtins.type): 24 | DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor 25 | HELLO: _MyEnum.ValueType # 0 26 | WORLD: _MyEnum.ValueType # 1 27 | 28 | class MyEnum(_MyEnum, metaclass=_MyEnumEnumTypeWrapper): ... 29 | 30 | HELLO: MyEnum.ValueType # 0 31 | WORLD: MyEnum.ValueType # 1 32 | Global___MyEnum: typing_extensions.TypeAlias = MyEnum 33 | -------------------------------------------------------------------------------- /test/generated/testproto/reexport_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import google.protobuf.descriptor 7 | from google.protobuf.empty_pb2 import ( 8 | Empty as Empty, 9 | ) 10 | from testproto.test3_pb2 import ( 11 | BAR3 as BAR3, 12 | FOO3 as FOO3, 13 | OuterEnum as OuterEnum, 14 | OuterMessage3 as OuterMessage3, 15 | SimpleProto3 as SimpleProto3, 16 | UNKNOWN as UNKNOWN, 17 | ) 18 | 19 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 20 | -------------------------------------------------------------------------------- /test/generated/testproto/test3_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | package test3""" 5 | 6 | import builtins 7 | import collections.abc 8 | import google.protobuf.descriptor 9 | import google.protobuf.internal.containers 10 | import google.protobuf.internal.enum_type_wrapper 11 | import google.protobuf.message 12 | import sys 13 | import test.test_generated_mypy 14 | import typing 15 | 16 | if sys.version_info >= (3, 10): 17 | import typing as typing_extensions 18 | else: 19 | import typing_extensions 20 | 21 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 22 | 23 | class _OuterEnum: 24 | ValueType = typing.NewType("ValueType", builtins.int) 25 | V: typing_extensions.TypeAlias = ValueType 26 | 27 | class _OuterEnumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_OuterEnum.ValueType], builtins.type): 28 | DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor 29 | UNKNOWN: _OuterEnum.ValueType # 0 30 | FOO3: _OuterEnum.ValueType # 1 31 | BAR3: _OuterEnum.ValueType # 2 32 | 33 | class OuterEnum(_OuterEnum, metaclass=_OuterEnumEnumTypeWrapper): ... 34 | 35 | UNKNOWN: OuterEnum.ValueType # 0 36 | FOO3: OuterEnum.ValueType # 1 37 | BAR3: OuterEnum.ValueType # 2 38 | Global___OuterEnum: typing_extensions.TypeAlias = OuterEnum 39 | 40 | @typing.final 41 | class OuterMessage3(google.protobuf.message.Message): 42 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 43 | 44 | A_STRING_FIELD_NUMBER: builtins.int 45 | a_string: builtins.str 46 | def __init__( 47 | self, 48 | *, 49 | a_string: builtins.str = ..., 50 | ) -> None: ... 51 | def ClearField(self, field_name: typing.Literal["a_string", b"a_string"]) -> None: ... 52 | 53 | Global___OuterMessage3: typing_extensions.TypeAlias = OuterMessage3 54 | 55 | @typing.final 56 | class SimpleProto3(google.protobuf.message.Message): 57 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 58 | 59 | class _InnerEnum: 60 | ValueType = typing.NewType("ValueType", builtins.int) 61 | V: typing_extensions.TypeAlias = ValueType 62 | 63 | class _InnerEnumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[SimpleProto3._InnerEnum.ValueType], builtins.type): 64 | DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor 65 | INNER1: SimpleProto3._InnerEnum.ValueType # 0 66 | INNER2: SimpleProto3._InnerEnum.ValueType # 1 67 | 68 | class InnerEnum(_InnerEnum, metaclass=_InnerEnumEnumTypeWrapper): ... 69 | INNER1: SimpleProto3.InnerEnum.ValueType # 0 70 | INNER2: SimpleProto3.InnerEnum.ValueType # 1 71 | 72 | @typing.final 73 | class MapScalarEntry(google.protobuf.message.Message): 74 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 75 | 76 | KEY_FIELD_NUMBER: builtins.int 77 | VALUE_FIELD_NUMBER: builtins.int 78 | key: builtins.int 79 | value: builtins.str 80 | def __init__( 81 | self, 82 | *, 83 | key: builtins.int = ..., 84 | value: builtins.str = ..., 85 | ) -> None: ... 86 | def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... 87 | 88 | @typing.final 89 | class MapMessageEntry(google.protobuf.message.Message): 90 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 91 | 92 | KEY_FIELD_NUMBER: builtins.int 93 | VALUE_FIELD_NUMBER: builtins.int 94 | key: builtins.int 95 | @property 96 | def value(self) -> Global___OuterMessage3: ... 97 | def __init__( 98 | self, 99 | *, 100 | key: builtins.int = ..., 101 | value: Global___OuterMessage3 | None = ..., 102 | ) -> None: ... 103 | def HasField(self, field_name: typing.Literal["value", b"value"]) -> builtins.bool: ... 104 | def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... 105 | 106 | @typing.final 107 | class EmailByUidEntry(google.protobuf.message.Message): 108 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 109 | 110 | KEY_FIELD_NUMBER: builtins.int 111 | VALUE_FIELD_NUMBER: builtins.int 112 | key: builtins.int 113 | value: builtins.str 114 | def __init__( 115 | self, 116 | *, 117 | key: builtins.int = ..., 118 | value: builtins.str = ..., 119 | ) -> None: ... 120 | def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... 121 | 122 | A_STRING_FIELD_NUMBER: builtins.int 123 | A_REPEATED_STRING_FIELD_NUMBER: builtins.int 124 | A_OUTER_ENUM_FIELD_NUMBER: builtins.int 125 | OUTER_MESSAGE_FIELD_NUMBER: builtins.int 126 | INNER_ENUM_FIELD_NUMBER: builtins.int 127 | A_ONEOF_1_FIELD_NUMBER: builtins.int 128 | A_ONEOF_2_FIELD_NUMBER: builtins.int 129 | OUTER_MESSAGE_IN_ONEOF_FIELD_NUMBER: builtins.int 130 | OUTER_ENUM_IN_ONEOF_FIELD_NUMBER: builtins.int 131 | INNER_ENUM_IN_ONEOF_FIELD_NUMBER: builtins.int 132 | B_ONEOF_1_FIELD_NUMBER: builtins.int 133 | B_ONEOF_2_FIELD_NUMBER: builtins.int 134 | BOOL_FIELD_NUMBER: builtins.int 135 | OUTERENUM_FIELD_NUMBER: builtins.int 136 | OUTERMESSAGE3_FIELD_NUMBER: builtins.int 137 | MAP_SCALAR_FIELD_NUMBER: builtins.int 138 | MAP_MESSAGE_FIELD_NUMBER: builtins.int 139 | AN_OPTIONAL_STRING_FIELD_NUMBER: builtins.int 140 | USER_ID_FIELD_NUMBER: builtins.int 141 | EMAIL_FIELD_NUMBER: builtins.int 142 | EMAIL_BY_UID_FIELD_NUMBER: builtins.int 143 | a_string: builtins.str 144 | a_outer_enum: Global___OuterEnum.ValueType 145 | inner_enum: Global___SimpleProto3.InnerEnum.ValueType 146 | a_oneof_1: builtins.str 147 | a_oneof_2: builtins.str 148 | outer_enum_in_oneof: Global___OuterEnum.ValueType 149 | inner_enum_in_oneof: Global___SimpleProto3.InnerEnum.ValueType 150 | b_oneof_1: builtins.str 151 | b_oneof_2: builtins.str 152 | OuterEnum: Global___OuterEnum.ValueType 153 | """Test having fieldname match messagename""" 154 | an_optional_string: builtins.str 155 | user_id: test.test_generated_mypy.UserId 156 | email: test.test_generated_mypy.Email 157 | @property 158 | def a_repeated_string(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... 159 | @property 160 | def outer_message(self) -> Global___OuterMessage3: ... 161 | @property 162 | def outer_message_in_oneof(self) -> Global___OuterMessage3: ... 163 | @property 164 | def bool(self) -> Global___OuterMessage3: ... 165 | @property 166 | def OuterMessage3(self) -> Global___OuterMessage3: ... 167 | @property 168 | def map_scalar(self) -> google.protobuf.internal.containers.ScalarMap[builtins.int, builtins.str]: 169 | """Test generation of map""" 170 | 171 | @property 172 | def map_message(self) -> google.protobuf.internal.containers.MessageMap[builtins.int, Global___OuterMessage3]: ... 173 | @property 174 | def email_by_uid(self) -> google.protobuf.internal.containers.ScalarMap[test.test_generated_mypy.UserId, test.test_generated_mypy.Email]: ... 175 | def __init__( 176 | self, 177 | *, 178 | a_string: builtins.str = ..., 179 | a_repeated_string: collections.abc.Iterable[builtins.str] | None = ..., 180 | a_outer_enum: Global___OuterEnum.ValueType = ..., 181 | outer_message: Global___OuterMessage3 | None = ..., 182 | inner_enum: Global___SimpleProto3.InnerEnum.ValueType = ..., 183 | a_oneof_1: builtins.str = ..., 184 | a_oneof_2: builtins.str = ..., 185 | outer_message_in_oneof: Global___OuterMessage3 | None = ..., 186 | outer_enum_in_oneof: Global___OuterEnum.ValueType = ..., 187 | inner_enum_in_oneof: Global___SimpleProto3.InnerEnum.ValueType = ..., 188 | b_oneof_1: builtins.str = ..., 189 | b_oneof_2: builtins.str = ..., 190 | bool: Global___OuterMessage3 | None = ..., 191 | OuterEnum: Global___OuterEnum.ValueType = ..., 192 | OuterMessage3: Global___OuterMessage3 | None = ..., 193 | map_scalar: collections.abc.Mapping[builtins.int, builtins.str] | None = ..., 194 | map_message: collections.abc.Mapping[builtins.int, Global___OuterMessage3] | None = ..., 195 | an_optional_string: builtins.str | None = ..., 196 | user_id: test.test_generated_mypy.UserId = ..., 197 | email: test.test_generated_mypy.Email = ..., 198 | email_by_uid: collections.abc.Mapping[test.test_generated_mypy.UserId, test.test_generated_mypy.Email] | None = ..., 199 | ) -> None: ... 200 | def HasField(self, field_name: typing.Literal["OuterMessage3", b"OuterMessage3", "_an_optional_string", b"_an_optional_string", "a_oneof", b"a_oneof", "a_oneof_1", b"a_oneof_1", "a_oneof_2", b"a_oneof_2", "an_optional_string", b"an_optional_string", "b_oneof", b"b_oneof", "b_oneof_1", b"b_oneof_1", "b_oneof_2", b"b_oneof_2", "bool", b"bool", "inner_enum_in_oneof", b"inner_enum_in_oneof", "outer_enum_in_oneof", b"outer_enum_in_oneof", "outer_message", b"outer_message", "outer_message_in_oneof", b"outer_message_in_oneof"]) -> builtins.bool: ... 201 | def ClearField(self, field_name: typing.Literal["OuterEnum", b"OuterEnum", "OuterMessage3", b"OuterMessage3", "_an_optional_string", b"_an_optional_string", "a_oneof", b"a_oneof", "a_oneof_1", b"a_oneof_1", "a_oneof_2", b"a_oneof_2", "a_outer_enum", b"a_outer_enum", "a_repeated_string", b"a_repeated_string", "a_string", b"a_string", "an_optional_string", b"an_optional_string", "b_oneof", b"b_oneof", "b_oneof_1", b"b_oneof_1", "b_oneof_2", b"b_oneof_2", "bool", b"bool", "email", b"email", "email_by_uid", b"email_by_uid", "inner_enum", b"inner_enum", "inner_enum_in_oneof", b"inner_enum_in_oneof", "map_message", b"map_message", "map_scalar", b"map_scalar", "outer_enum_in_oneof", b"outer_enum_in_oneof", "outer_message", b"outer_message", "outer_message_in_oneof", b"outer_message_in_oneof", "user_id", b"user_id"]) -> None: ... 202 | @typing.overload 203 | def WhichOneof(self, oneof_group: typing.Literal["_an_optional_string", b"_an_optional_string"]) -> typing.Literal["an_optional_string"] | None: ... 204 | @typing.overload 205 | def WhichOneof(self, oneof_group: typing.Literal["a_oneof", b"a_oneof"]) -> typing.Literal["a_oneof_1", "a_oneof_2", "outer_message_in_oneof", "outer_enum_in_oneof", "inner_enum_in_oneof"] | None: ... 206 | @typing.overload 207 | def WhichOneof(self, oneof_group: typing.Literal["b_oneof", b"b_oneof"]) -> typing.Literal["b_oneof_1", "b_oneof_2"] | None: ... 208 | 209 | Global___SimpleProto3: typing_extensions.TypeAlias = SimpleProto3 210 | -------------------------------------------------------------------------------- /test/generated/testproto/test_extensions2_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.internal.extension_dict 9 | import google.protobuf.message 10 | import sys 11 | import testproto.test_pb2 12 | import typing 13 | 14 | if sys.version_info >= (3, 10): 15 | import typing as typing_extensions 16 | else: 17 | import typing_extensions 18 | 19 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 20 | 21 | @typing.final 22 | class SeparateFileExtension(google.protobuf.message.Message): 23 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 24 | 25 | FLAG_FIELD_NUMBER: builtins.int 26 | flag: builtins.bool 27 | EXT_FIELD_NUMBER: builtins.int 28 | ext: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[testproto.test_pb2.Simple2, Global___SeparateFileExtension] 29 | def __init__( 30 | self, 31 | *, 32 | flag: builtins.bool | None = ..., 33 | ) -> None: ... 34 | def HasField(self, field_name: typing.Literal["flag", b"flag"]) -> builtins.bool: ... 35 | def ClearField(self, field_name: typing.Literal["flag", b"flag"]) -> None: ... 36 | 37 | Global___SeparateFileExtension: typing_extensions.TypeAlias = SeparateFileExtension 38 | -------------------------------------------------------------------------------- /test/generated/testproto/test_extensions3_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.descriptor_pb2 9 | import google.protobuf.internal.containers 10 | import google.protobuf.internal.extension_dict 11 | import google.protobuf.message 12 | import sys 13 | import testproto.test3_pb2 14 | import typing 15 | 16 | if sys.version_info >= (3, 10): 17 | import typing as typing_extensions 18 | else: 19 | import typing_extensions 20 | 21 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 22 | 23 | @typing.final 24 | class MessageOptionsTestMsg(google.protobuf.message.Message): 25 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 26 | 27 | def __init__( 28 | self, 29 | ) -> None: ... 30 | 31 | Global___MessageOptionsTestMsg: typing_extensions.TypeAlias = MessageOptionsTestMsg 32 | 33 | TEST_FIELD_EXTENSION_FIELD_NUMBER: builtins.int 34 | SCALAR_OPTION_FIELD_NUMBER: builtins.int 35 | REPEATED_SCALAR_OPTION_FIELD_NUMBER: builtins.int 36 | ENUM_OPTION_FIELD_NUMBER: builtins.int 37 | REPEATED_ENUM_OPTION_FIELD_NUMBER: builtins.int 38 | MSG_OPTION_FIELD_NUMBER: builtins.int 39 | REPEATED_MSG_OPTION_FIELD_NUMBER: builtins.int 40 | test_field_extension: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, builtins.str] 41 | scalar_option: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MessageOptions, builtins.str] 42 | repeated_scalar_option: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MessageOptions, google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]] 43 | enum_option: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MessageOptions, testproto.test3_pb2.OuterEnum.ValueType] 44 | repeated_enum_option: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MessageOptions, google.protobuf.internal.containers.RepeatedScalarFieldContainer[testproto.test3_pb2.OuterEnum.ValueType]] 45 | msg_option: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MessageOptions, testproto.test3_pb2.OuterMessage3] 46 | repeated_msg_option: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MessageOptions, google.protobuf.internal.containers.RepeatedCompositeFieldContainer[testproto.test3_pb2.OuterMessage3]] 47 | -------------------------------------------------------------------------------- /test/generated/testproto/test_no_generic_services_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | 6 | import builtins 7 | import google.protobuf.descriptor 8 | import google.protobuf.message 9 | import sys 10 | import typing 11 | 12 | if sys.version_info >= (3, 10): 13 | import typing as typing_extensions 14 | else: 15 | import typing_extensions 16 | 17 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 18 | 19 | @typing.final 20 | class Simple3(google.protobuf.message.Message): 21 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 22 | 23 | A_STRING_FIELD_NUMBER: builtins.int 24 | a_string: builtins.str 25 | def __init__( 26 | self, 27 | *, 28 | a_string: builtins.str | None = ..., 29 | ) -> None: ... 30 | def HasField(self, field_name: typing.Literal["a_string", b"a_string"]) -> builtins.bool: ... 31 | def ClearField(self, field_name: typing.Literal["a_string", b"a_string"]) -> None: ... 32 | 33 | Global___Simple3: typing_extensions.TypeAlias = Simple3 34 | -------------------------------------------------------------------------------- /test/test_generated_mypy.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is a file which should mypy with success. See test_negative for 3 | a file that should have failures. 4 | 5 | This file contains a test to ensure that the test_negative failures 6 | are the expected failures 7 | 8 | These tests can be set up and run by the run_test.sh script 9 | """ 10 | 11 | import glob 12 | import os 13 | from typing import Any, Generator, NewType, Tuple, Type 14 | 15 | import pytest 16 | import testproto.test_pb2 as test_pb2 17 | from google.protobuf.descriptor import FieldDescriptor 18 | from google.protobuf.internal import api_implementation 19 | from google.protobuf.internal.containers import ScalarMap 20 | from google.protobuf.message import Message 21 | from testproto.Capitalized.Capitalized_pb2 import Upper, lower, lower2 22 | from testproto.reexport_pb2 import FOO3 as ReexportedFOO3 23 | from testproto.reexport_pb2 import SimpleProto3 as ReexportedSimpleProto3 24 | from testproto.test3_pb2 import BAR3, FOO3, OuterMessage3, SimpleProto3 25 | from testproto.test_extensions2_pb2 import SeparateFileExtension 26 | from testproto.test_extensions3_pb2 import ( 27 | SCALAR_OPTION_FIELD_NUMBER, 28 | MessageOptionsTestMsg, 29 | enum_option, 30 | msg_option, 31 | repeated_enum_option, 32 | repeated_msg_option, 33 | repeated_scalar_option, 34 | scalar_option, 35 | ) 36 | from testproto.test_pb2 import DESCRIPTOR, FOO, Extensions1, Extensions2 37 | from testproto.test_pb2 import Name as NamingConflicts_Name 38 | from testproto.test_pb2 import ( 39 | NamingConflicts, 40 | OuterEnum, 41 | PythonReservedKeywords, 42 | Simple1, 43 | Simple2, 44 | ) 45 | from typing_extensions import assert_type 46 | 47 | # C++ python API implementation has some semantic differences from pure python 48 | # We mainly focus on testing the C++ impl - but just have some flags here 49 | # to flag off tests that only work on cpp impl 50 | CPP_IMPL = api_implementation.Type() == "cpp" 51 | 52 | UserId = NewType("UserId", int) 53 | 54 | 55 | class Email(str): 56 | pass 57 | 58 | 59 | def _is_summary(line: str) -> bool: 60 | """Checks if the line is the summary line 'Found X errors in Y files (checked Z source files)'""" 61 | return line.startswith("Found ") and line.endswith("source files)\n") 62 | 63 | 64 | def test_generate_mypy_matches() -> None: 65 | proto_files = glob.glob("proto/**/*.proto", recursive=True) 66 | assert len(proto_files) == 17 # Just a sanity check that all the files show up 67 | 68 | pyi_files = glob.glob("test/generated/**/*.pyi", recursive=True) 69 | assert len(pyi_files) == 19 # Should be higher - because grpc files generate extra pyis 70 | 71 | failure_check_results = [] 72 | for fn in proto_files: 73 | assert fn.endswith(".proto") 74 | fn_split = fn.split(os.sep) # Eg. [proto, testproto, dot.com, test.proto] 75 | assert fn_split[-1].endswith(".proto") 76 | last = fn_split[-1][: -len(".proto")] + "_pb2.pyi" # Eg [test_pb2.proto] 77 | components = fn_split[1:-1] # Eg. [testproto, dot.com] 78 | components = [c.replace(".", os.sep) for c in components] # Eg. [testproto, dot/com] 79 | components.append(last) # Eg. [testproto, dot/com, test_pb2.proto] 80 | 81 | output = os.path.join("test", "generated", *components) 82 | 83 | assert os.path.exists(output) 84 | failure_check_results.append(output) 85 | if "grpc" in components: 86 | grpc_output = output[:-4] + "_grpc.pyi" 87 | assert os.path.exists(grpc_output) 88 | failure_check_results.append(grpc_output) 89 | 90 | # Make sure we checked everything 91 | assert len(failure_check_results) == len(pyi_files) 92 | 93 | 94 | def test_generate_negative_matches() -> None: 95 | """Confirm that the test_negative expected file matches an error for each line""" 96 | 97 | def grab_errors(filename: str) -> Generator[Tuple[str, int], None, None]: 98 | for line in open(filename).readlines(): 99 | if _is_summary(line): 100 | continue 101 | parts = line.split(":") 102 | yield parts[0], int(parts[1]) 103 | 104 | def grab_expectations(filename: str, marker: str) -> Generator[Tuple[str, int], None, None]: 105 | for idx, line in enumerate(open(filename).readlines()): 106 | if "#" in line and marker in line: 107 | yield filename, idx + 1 108 | 109 | errors_38 = set(grab_errors("test_negative/output.expected.3.8")) 110 | 111 | expected_errors_38 = set(grab_expectations("test_negative/negative.py", "E:3.8")) 112 | 113 | assert errors_38 == expected_errors_38 114 | 115 | # Some sanity checks to make sure we don't mess this up. Please update as necessary. 116 | assert len(errors_38) == 77 117 | 118 | 119 | def test_func() -> None: 120 | s = Simple1(a_string="hello") 121 | 122 | s = Simple1() 123 | s.a_string = "Hello" 124 | s.a_repeated_string.append("Hello") 125 | 126 | s2 = Simple1.FromString(s.SerializeToString()) 127 | assert s2.a_string == "Hello" 128 | assert s2.A_STRING_FIELD_NUMBER == 1 129 | assert s2.USER_ID_FIELD_NUMBER == 21 130 | 131 | s3 = Simple1() 132 | s3.ParseFromString(s.SerializeToString()) 133 | assert s3.a_string == "Hello" 134 | 135 | s4 = Simple1() 136 | s4.CopyFrom(s) 137 | assert s4.a_string == "Hello" 138 | 139 | ll = lower2() 140 | ll.upper.Lower.a = 2 141 | assert ll == lower2(upper=Upper(Lower=lower(a=2))) 142 | 143 | 144 | def test_enum() -> None: 145 | e = FOO 146 | e = OuterEnum.Value("BAR") 147 | assert OuterEnum.Value("BAR") == 2 148 | assert OuterEnum.Name(e) == "BAR" 149 | assert OuterEnum.keys() == ["FOO", "BAR"] 150 | assert OuterEnum.values() == [1, 2] 151 | assert OuterEnum.items() == [("FOO", 1), ("BAR", 2)] 152 | 153 | # Make sure we can assure typing with a couple of techniques 154 | e2: test_pb2.OuterEnum.ValueType = OuterEnum.Value("BAR") 155 | assert OuterEnum.Name(e2) == "BAR" 156 | e3: OuterEnum.ValueType = OuterEnum.Value("BAR") 157 | assert OuterEnum.Name(e3) == "BAR" 158 | e4: int = OuterEnum.Value("BAR") 159 | print(e4) 160 | assert OuterEnum.Name(e2) == "BAR" 161 | 162 | # Legacy .V type 163 | e5: OuterEnum.V = OuterEnum.Value("BAR") 164 | print(e5) 165 | assert OuterEnum.Name(e3) == "BAR" 166 | 167 | # Protobuf itself allows both str and bytes here. 168 | if CPP_IMPL: 169 | assert OuterEnum.Value("BAR") == OuterEnum.Value(b"BAR") 170 | 171 | 172 | def test_enum_naming_conflicts() -> None: 173 | assert NamingConflicts.Name(NamingConflicts_Name) == "Name" 174 | assert NamingConflicts.Value("Name") == 1 175 | assert NamingConflicts_Name == 1 176 | 177 | 178 | def test_has_field_proto2() -> None: 179 | """For HasField which is typed with Literal""" 180 | s = Simple1() 181 | s.a_string = "Hello" 182 | 183 | # Proto2 tests 184 | assert s.HasField("a_string") 185 | assert not s.HasField("a_inner") 186 | assert not s.HasField("a_enum") 187 | assert not s.HasField("a_oneof") 188 | 189 | # Erase the types to verify that incorrect inputs fail at runtime 190 | # Each test here should be duplicated in test_negative to ensure mypy fails it too 191 | if CPP_IMPL: 192 | s_untyped: Any = s 193 | with pytest.raises(ValueError, match="Protocol message Simple1 has no field garbage."): 194 | s_untyped.HasField("garbage") 195 | with pytest.raises( 196 | ValueError, 197 | match='Protocol message Simple1 has no singular "a_repeated_string" field', 198 | ): 199 | s_untyped.HasField("a_repeated_string") 200 | with pytest.raises(TypeError, match="bad argument type for built-in operation"): 201 | s_untyped.HasField(b"a_string") 202 | 203 | none_err = "bad argument type for built-in operation" 204 | with pytest.raises(TypeError, match=none_err): 205 | s_untyped.HasField(None) 206 | 207 | 208 | def test_has_field_proto3() -> None: 209 | s = SimpleProto3() 210 | assert not s.HasField("outer_message") 211 | assert not s.HasField("a_oneof") 212 | 213 | assert not s.HasField("an_optional_string") 214 | # synthetic oneof from optional field, see https://github.com/protocolbuffers/protobuf/blob/v3.12.0/docs/implementing_proto3_presence.md#updating-a-code-generator 215 | assert not s.HasField("_an_optional_string") 216 | 217 | s = SimpleProto3(an_optional_string=None) 218 | assert not s.HasField("an_optional_string") 219 | # synthetic oneof from optional field 220 | assert not s.HasField("_an_optional_string") 221 | 222 | s = SimpleProto3(an_optional_string="") 223 | assert s.HasField("an_optional_string") 224 | # synthetic oneof from optional field 225 | assert s.HasField("_an_optional_string") 226 | 227 | # Erase the types to verify that incorrect inputs fail at runtime 228 | # Each test here should be duplicated in test_negative to ensure mypy fails it too 229 | if CPP_IMPL: 230 | s_untyped: Any = s 231 | with pytest.raises(ValueError, match="Protocol message SimpleProto3 has no field garbage."): 232 | s_untyped.HasField("garbage") 233 | with pytest.raises( 234 | ValueError, 235 | match='Can\'t test non-optional, non-submessage field "SimpleProto3.a_string" for presence in proto3.', 236 | ): 237 | s_untyped.HasField("a_string") 238 | with pytest.raises( 239 | ValueError, 240 | match='Can\'t test non-optional, non-submessage field "SimpleProto3.a_outer_enum" for presence in proto3.', 241 | ): 242 | s_untyped.HasField("a_outer_enum") 243 | with pytest.raises( 244 | ValueError, 245 | match='Protocol message SimpleProto3 has no singular "a_repeated_string" field', 246 | ): 247 | s_untyped.HasField("a_repeated_string") 248 | with pytest.raises(TypeError, match="bad argument type for built-in operation"): 249 | s_untyped.HasField(b"outer_message") 250 | 251 | none_err = "bad argument type for built-in operation" 252 | with pytest.raises(TypeError, match=none_err): 253 | s_untyped.HasField(None) 254 | 255 | 256 | def test_clear_field_proto2() -> None: 257 | """For ClearField which is typed with Literal""" 258 | s = Simple1() 259 | s.a_string = "Hello" 260 | 261 | # Proto2 tests 262 | s.ClearField("a_string") 263 | s.ClearField("a_inner") 264 | s.ClearField("a_repeated_string") 265 | s.ClearField("a_oneof") 266 | if CPP_IMPL: 267 | s.ClearField(b"a_string") 268 | 269 | # Erase the types to verify that incorrect inputs fail at runtime 270 | # Each test here should be duplicated in test_negative to ensure mypy fails it too 271 | s_untyped: Any = s 272 | with pytest.raises(ValueError, match='Protocol message has no "garbage" field.'): 273 | s_untyped.ClearField("garbage") 274 | 275 | 276 | def test_clear_field_proto3() -> None: 277 | """For ClearField which is typed with Literal""" 278 | s = SimpleProto3() 279 | s.a_string = "Hello" 280 | 281 | # Proto2 tests 282 | s.ClearField("a_string") 283 | s.ClearField("a_outer_enum") 284 | s.ClearField("outer_message") 285 | s.ClearField("a_repeated_string") 286 | s.ClearField("a_oneof") 287 | if CPP_IMPL: 288 | s.ClearField(b"a_string") 289 | s.ClearField("an_optional_string") 290 | # synthetic oneof from optional field 291 | s.ClearField("_an_optional_string") 292 | 293 | # Erase the types to verify that incorrect inputs fail at runtime 294 | # Each test here should be duplicated in test_negative to ensure mypy fails it too 295 | s_untyped: Any = s 296 | if CPP_IMPL: 297 | with pytest.raises(ValueError, match='Protocol message has no "garbage" field.'): 298 | s_untyped.ClearField("garbage") 299 | 300 | 301 | def test_which_oneof_proto2() -> None: 302 | s = Simple1() 303 | 304 | assert s.WhichOneof("a_oneof") is None 305 | s.a_oneof_1 = "hello" 306 | assert s.WhichOneof("a_oneof") == "a_oneof_1" 307 | if CPP_IMPL: 308 | assert s.WhichOneof(b"a_oneof") == "a_oneof_1" 309 | assert type(s.WhichOneof("a_oneof")) == str 310 | field = s.WhichOneof("a_oneof") 311 | assert field is not None 312 | assert s.HasField(field) 313 | 314 | # Erase the types to verify that incorrect inputs fail at runtime 315 | # Each test here should be duplicated in test_negative to ensure mypy fails it too 316 | s_untyped: Any = s 317 | with pytest.raises(ValueError, match='Protocol message Simple1 has no "garbage" field.'): 318 | s_untyped.WhichOneof("garbage") 319 | 320 | 321 | def test_which_oneof_proto3() -> None: 322 | s = SimpleProto3() 323 | 324 | assert s.WhichOneof("a_oneof") is None 325 | s.a_oneof_1 = "hello" 326 | s.b_oneof_1 = "world" 327 | assert s.WhichOneof("a_oneof") == "a_oneof_1" 328 | if CPP_IMPL: 329 | assert s.WhichOneof(b"a_oneof") == "a_oneof_1" 330 | assert type(s.WhichOneof("a_oneof")) == str 331 | 332 | field_a = s.WhichOneof("a_oneof") 333 | assert field_a is not None 334 | assert s.HasField(field_a) 335 | 336 | field_b = s.WhichOneof("b_oneof") 337 | assert field_b is not None 338 | assert s.HasField(field_b) 339 | 340 | # synthetic oneof from optional field 341 | assert s.WhichOneof("_an_optional_string") is None 342 | s.an_optional_string = "foo" 343 | field_aos = s.WhichOneof("_an_optional_string") 344 | assert field_aos is not None 345 | assert s.HasField(field_aos) 346 | 347 | # Erase the types to verify that incorrect inputs fail at runtime 348 | # Each test here should be duplicated in test_negative to ensure mypy fails it too 349 | s_untyped: Any = s 350 | with pytest.raises(ValueError, match='Protocol message SimpleProto3 has no "garbage" field.'): 351 | s_untyped.WhichOneof("garbage") 352 | 353 | 354 | def test_extensions_proto2() -> None: 355 | s1 = Simple1() 356 | s2 = Simple2() 357 | 358 | assert isinstance(Extensions1.ext, FieldDescriptor) 359 | assert test_pb2.Extensions1.EXT_FIELD_NUMBER == 1000 360 | assert isinstance(Extensions2.foo, FieldDescriptor) 361 | assert isinstance(SeparateFileExtension.ext, FieldDescriptor) 362 | 363 | assert s1.HasExtension(Extensions1.ext) is False 364 | s1.ClearExtension(Extensions1.ext) 365 | 366 | e1 = s1.Extensions[Extensions1.ext] 367 | e1.ext1_string = "first extension" 368 | assert isinstance(e1, Extensions1) 369 | 370 | e2 = s1.Extensions[Extensions2.foo] 371 | e2.flag = True 372 | assert isinstance(e2, Extensions2) 373 | 374 | e3 = s2.Extensions[SeparateFileExtension.ext] 375 | e3.flag = True 376 | assert isinstance(e3, SeparateFileExtension) 377 | 378 | del s1.Extensions[Extensions2.foo] 379 | 380 | # Using __iter__, x is a FieldDescriptor but the type of the message that 381 | # s1.Extensions[x] yields is unknown (it could be any of the extension messages). 382 | # Hence, s1.Extensions[x] is typed as Any. 383 | for x in s1.Extensions: 384 | assert isinstance(x, FieldDescriptor) 385 | assert x.is_extension 386 | y = s1.Extensions[x] 387 | assert y.ext1_string == "first extension" 388 | 389 | assert Extensions1.ext in s1.Extensions 390 | 391 | assert len(s2.Extensions) == 1 392 | 393 | 394 | def test_extensions_proto3() -> None: 395 | assert MessageOptionsTestMsg.DESCRIPTOR.GetOptions().Extensions[scalar_option] == "Hello world!" 396 | assert SCALAR_OPTION_FIELD_NUMBER == 51234 397 | assert MessageOptionsTestMsg.DESCRIPTOR.GetOptions().Extensions[repeated_scalar_option] == ["A", "B", "C"] 398 | assert MessageOptionsTestMsg.DESCRIPTOR.GetOptions().Extensions[enum_option] == FOO3 399 | assert MessageOptionsTestMsg.DESCRIPTOR.GetOptions().Extensions[repeated_enum_option] == [FOO3, BAR3] 400 | assert MessageOptionsTestMsg.DESCRIPTOR.GetOptions().Extensions[msg_option] == OuterMessage3(a_string="Hello OuterMessage3") 401 | assert list(MessageOptionsTestMsg.DESCRIPTOR.GetOptions().Extensions[repeated_msg_option]) == [ 402 | OuterMessage3(a_string="Hello OuterMessage3 A"), 403 | OuterMessage3(a_string="Hello OuterMessage3 B"), 404 | ] 405 | 406 | 407 | def test_constructor_proto2() -> None: 408 | x = Simple2() # It's OK to omit a required field from the constructor. 409 | assert not x.HasField("a_string") 410 | 411 | x = Simple2(a_string=None) # It's OK to pass None for a required field. 412 | assert not x.HasField("a_string") 413 | 414 | x = Simple2(a_string="") 415 | assert x.HasField("a_string") 416 | 417 | x = Simple2(a_string="hello") 418 | assert x.HasField("a_string") 419 | 420 | 421 | def test_constructor_proto3() -> None: 422 | x = SimpleProto3() 423 | assert x.a_string == "" 424 | assert not x.HasField("an_optional_string") 425 | 426 | # an_optional_string has optional keyword so None is allowed 427 | x = SimpleProto3(an_optional_string=None) 428 | assert not x.HasField("an_optional_string") 429 | # Field access still returns the 0-value, even though 430 | # the field is not present. 431 | assert x.an_optional_string == "" 432 | 433 | x = SimpleProto3(a_string="", an_optional_string="") 434 | assert x.a_string == "" 435 | assert x.HasField("an_optional_string") 436 | assert x.an_optional_string == "" 437 | 438 | x = SimpleProto3(a_string="hello", an_optional_string="hello") 439 | assert x.a_string == "hello" 440 | assert x.HasField("an_optional_string") 441 | assert x.an_optional_string == "hello" 442 | 443 | 444 | def test_message_descriptor_proto2() -> None: 445 | assert Simple1().DESCRIPTOR.full_name == "test.Simple1" 446 | assert Simple1.DESCRIPTOR.full_name == "test.Simple1" 447 | 448 | 449 | def test_message_descriptor_proto3() -> None: 450 | assert SimpleProto3().DESCRIPTOR.full_name == "test3.SimpleProto3" 451 | assert SimpleProto3.DESCRIPTOR.full_name == "test3.SimpleProto3" 452 | 453 | 454 | def test_reexport_identical() -> None: 455 | assert SimpleProto3 is ReexportedSimpleProto3 456 | assert FOO3 is ReexportedFOO3 457 | 458 | 459 | def test_enum_descriptor() -> None: 460 | assert OuterEnum.DESCRIPTOR.name == "OuterEnum" 461 | 462 | 463 | def test_module_descriptor() -> None: 464 | assert DESCRIPTOR.name == "testproto/test.proto" 465 | 466 | 467 | def test_mapping_type() -> None: 468 | s = SimpleProto3() 469 | s.map_scalar[5] = "abcd" 470 | assert s.map_scalar[5] == "abcd" 471 | 472 | s.map_message[5].a_string = "hi" 473 | assert s.map_message[5] == OuterMessage3(a_string="hi") 474 | 475 | assert s.map_message.get_or_create(6) == OuterMessage3() 476 | assert s.map_message[6] == OuterMessage3() 477 | assert s.map_message.get_or_create(6) == OuterMessage3() 478 | 479 | SimpleProto3(map_scalar={5: "abcd"}, map_message={5: OuterMessage3(a_string="hi")}) 480 | 481 | 482 | def test_casttype() -> None: 483 | s = Simple1() 484 | assert_type(s.user_id, UserId) 485 | assert_type(s.email, Email) 486 | assert_type(s.email_by_uid, ScalarMap[UserId, Email]) 487 | 488 | s.user_id = UserId(33) 489 | assert s.user_id == 33 490 | s.email = Email("abcd@gmail.com") 491 | assert s.email == "abcd@gmail.com" 492 | s.email_by_uid[UserId(33)] = Email("abcd@gmail.com") 493 | 494 | s2 = SimpleProto3() 495 | assert_type(s2.user_id, UserId) 496 | assert_type(s2.email, Email) 497 | assert_type(s2.email_by_uid, ScalarMap[UserId, Email]) 498 | 499 | s2.user_id = UserId(33) 500 | assert s2.user_id == 33 501 | s2.email = Email("abcd@gmail.com") 502 | assert s2.email == "abcd@gmail.com" 503 | s2.email_by_uid[UserId(33)] = Email("abcd@gmail.com") 504 | 505 | 506 | def test_reserved_keywords() -> None: 507 | with pytest.raises(AttributeError, match="module.*has no attribute 'asdf'"): 508 | getattr(test_pb2, "asdf") # noqa: B009 509 | 510 | # Confirm that "None" is a Message 511 | none_cls: Type[test_pb2._r_None] = getattr(test_pb2, "None") 512 | none_instance = none_cls(valid=5) 513 | assert isinstance(none_instance, Message) 514 | 515 | # Confirm that messages and enums w/ reserved names type properly 516 | prk = PythonReservedKeywords(none=none_instance, valid=PythonReservedKeywords.valid_in_finally) 517 | assert prk.none.valid == 5 518 | assert prk.valid == PythonReservedKeywords.valid_in_finally 519 | -------------------------------------------------------------------------------- /test/test_grpc_async_usage.py: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | import grpc.aio 4 | import pytest 5 | from testproto.grpc import dummy_pb2, dummy_pb2_grpc 6 | 7 | ADDRESS = "localhost:22223" 8 | 9 | 10 | class Servicer(dummy_pb2_grpc.DummyServiceServicer): 11 | async def UnaryUnary( 12 | self, 13 | request: dummy_pb2.DummyRequest, 14 | context: grpc.aio.ServicerContext, 15 | ) -> dummy_pb2.DummyReply: 16 | return dummy_pb2.DummyReply(value=request.value[::-1]) 17 | 18 | async def UnaryStream( 19 | self, 20 | request: dummy_pb2.DummyRequest, 21 | context: grpc.aio.ServicerContext, 22 | ) -> typing.AsyncIterator[dummy_pb2.DummyReply]: 23 | for char in request.value: 24 | yield dummy_pb2.DummyReply(value=char) 25 | 26 | async def StreamUnary( 27 | self, 28 | request_iterator: typing.AsyncIterator[dummy_pb2.DummyRequest], 29 | context: grpc.aio.ServicerContext, 30 | ) -> dummy_pb2.DummyReply: 31 | values = [data.value async for data in request_iterator] 32 | return dummy_pb2.DummyReply(value="".join(values)) 33 | 34 | async def StreamStream( 35 | self, 36 | request_iterator: typing.AsyncIterator[dummy_pb2.DummyRequest], 37 | context: grpc.aio.ServicerContext, 38 | ) -> typing.AsyncIterator[dummy_pb2.DummyReply]: 39 | async for data in request_iterator: 40 | yield dummy_pb2.DummyReply(value=data.value.upper()) 41 | 42 | 43 | def make_server() -> grpc.aio.Server: 44 | server = grpc.aio.server() 45 | servicer = Servicer() 46 | server.add_insecure_port(ADDRESS) 47 | dummy_pb2_grpc.add_DummyServiceServicer_to_server(servicer, server) 48 | return server 49 | 50 | 51 | @pytest.mark.asyncio 52 | async def test_grpc() -> None: 53 | server = make_server() 54 | await server.start() 55 | async with grpc.aio.insecure_channel(ADDRESS) as channel: 56 | client = dummy_pb2_grpc.DummyServiceStub(channel) 57 | request = dummy_pb2.DummyRequest(value="cprg") 58 | result1 = await client.UnaryUnary(request) 59 | result2 = client.UnaryStream(dummy_pb2.DummyRequest(value=result1.value)) 60 | result2_list = [r async for r in result2] 61 | assert len(result2_list) == 4 62 | result3 = client.StreamStream(dummy_pb2.DummyRequest(value=part.value) for part in result2_list) 63 | result3_list = [r async for r in result3] 64 | assert len(result3_list) == 4 65 | result4 = await client.StreamUnary(dummy_pb2.DummyRequest(value=part.value) for part in result3_list) 66 | assert result4.value == "GRPC" 67 | 68 | await server.stop(None) 69 | -------------------------------------------------------------------------------- /test/test_grpc_usage.py: -------------------------------------------------------------------------------- 1 | import typing 2 | from concurrent import futures 3 | 4 | import grpc 5 | from testproto.grpc import dummy_pb2, dummy_pb2_grpc 6 | 7 | ADDRESS = "localhost:22222" 8 | 9 | 10 | class Servicer(dummy_pb2_grpc.DummyServiceServicer): 11 | def UnaryUnary( 12 | self, 13 | request: dummy_pb2.DummyRequest, 14 | context: grpc.ServicerContext, 15 | ) -> dummy_pb2.DummyReply: 16 | return dummy_pb2.DummyReply(value=request.value[::-1]) 17 | 18 | def UnaryStream( 19 | self, 20 | request: dummy_pb2.DummyRequest, 21 | context: grpc.ServicerContext, 22 | ) -> typing.Iterator[dummy_pb2.DummyReply]: 23 | for char in request.value: 24 | yield dummy_pb2.DummyReply(value=char) 25 | 26 | def StreamUnary( 27 | self, 28 | request_iterator: typing.Iterator[dummy_pb2.DummyRequest], 29 | context: grpc.ServicerContext, 30 | ) -> dummy_pb2.DummyReply: 31 | return dummy_pb2.DummyReply(value="".join(data.value for data in request_iterator)) 32 | 33 | def StreamStream( 34 | self, 35 | request_iterator: typing.Iterator[dummy_pb2.DummyRequest], 36 | context: grpc.ServicerContext, 37 | ) -> typing.Iterator[dummy_pb2.DummyReply]: 38 | for data in request_iterator: 39 | yield dummy_pb2.DummyReply(value=data.value.upper()) 40 | 41 | 42 | def make_server() -> grpc.Server: 43 | server = grpc.server(futures.ThreadPoolExecutor()) 44 | servicer = Servicer() 45 | server.add_insecure_port(ADDRESS) 46 | dummy_pb2_grpc.add_DummyServiceServicer_to_server(servicer, server) 47 | return server 48 | 49 | 50 | def test_grpc() -> None: 51 | server = make_server() 52 | server.start() 53 | channel = grpc.insecure_channel(ADDRESS) 54 | client = dummy_pb2_grpc.DummyServiceStub(channel) 55 | request = dummy_pb2.DummyRequest(value="cprg") 56 | result1 = client.UnaryUnary(request) 57 | result2 = client.UnaryStream(dummy_pb2.DummyRequest(value=result1.value)) 58 | result2_list = list(result2) 59 | assert len(result2_list) == 4 60 | result3 = client.StreamStream(dummy_pb2.DummyRequest(value=part.value) for part in result2_list) 61 | result3_list = list(result3) 62 | assert len(result3_list) == 4 63 | result4 = client.StreamUnary(dummy_pb2.DummyRequest(value=part.value) for part in result3_list) 64 | assert result4.value == "GRPC" 65 | 66 | # test future() in MultiCallable 67 | future_test: grpc.CallFuture[dummy_pb2.DummyReply] = client.UnaryUnary.future(request) 68 | result5 = future_test.result() 69 | print(result5) 70 | assert result5.value == "grpc" 71 | 72 | # test params on __call__ in MultiCallable 73 | result6: dummy_pb2.DummyReply = client.UnaryUnary(request, timeout=4, metadata=(("test", "metadata"), ("cheems", "many"))) 74 | 75 | assert result6.value == "grpc" 76 | 77 | server.stop(None) 78 | -------------------------------------------------------------------------------- /test_negative/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nipunn1313/mypy-protobuf/afdf55f7fbbe334ac1ed2791ef4ece3ad6724dab/test_negative/__init__.py -------------------------------------------------------------------------------- /test_negative/negative.py: -------------------------------------------------------------------------------- 1 | """ 2 | This code is intended to have mypy failures which we will ensure 3 | show up in the output. 4 | """ 5 | 6 | from test.test_generated_mypy import Email, UserId 7 | from typing import Generator, Iterator, List 8 | 9 | import grpc 10 | from testproto.dot.com.test_pb2 import TestMessage 11 | from testproto.grpc.dummy_pb2 import DummyReply, DummyRequest 12 | from testproto.grpc.dummy_pb2_grpc import DummyServiceServicer, DummyServiceStub 13 | from testproto.test3_pb2 import OuterEnum, OuterMessage3, SimpleProto3 14 | from testproto.test_extensions2_pb2 import SeparateFileExtension 15 | from testproto.test_pb2 import ( 16 | DESCRIPTOR, 17 | FOO, 18 | Extensions1, 19 | Extensions2, 20 | PythonReservedKeywords, 21 | Simple1, 22 | Simple2, 23 | ) 24 | 25 | s = Simple1() 26 | s.a_string = "Hello" 27 | 28 | s2 = Simple1.FromString(s.SerializeToStringg()) # E:3.8 29 | 30 | s3 = Simple1() 31 | s3.ParseFromString(s) # E:3.8 32 | 33 | s4 = Simple1() 34 | s4.CopyFrom(s.SerializeToString()) # E:3.8 35 | 36 | s5 = Simple1() 37 | l: List[int] = [] 38 | l.extend(s5.a_repeated_string) # E:3.8 39 | 40 | tm = TestMessage(foo=55) # E:3.8 41 | 42 | e = FOO 43 | e = 3 # E:3.8 44 | 45 | # Proto2 46 | s.HasField("garbage") # E:3.8 47 | s.HasField("a_repeated_string") # E:3.8 48 | 49 | # Proto3 50 | s6 = SimpleProto3() 51 | s6.HasField("garbage") # E:3.8 52 | s6.HasField("a_string") # E:3.8 53 | s6.HasField("outer_enum") # E:3.8 54 | s6.HasField("a_repeated_string") # E:3.8 55 | 56 | # Proto2 57 | s.ClearField("garbage") # E:3.8 58 | 59 | # Proto3 60 | s6.ClearField("garbage") # E:3.8 61 | 62 | # Proto2 WhichOneof 63 | s.WhichOneof("garbage") # E:3.8 64 | a = 5 65 | a = s.WhichOneof("a_oneof") # E:3.8 66 | b = s.WhichOneof("a_oneof") 67 | assert b is not None 68 | s.HasField(b) # allowed 69 | simple2 = Simple2(a_string="abc") 70 | simple2.HasField(b) # E:3.8 71 | 72 | # WhichOneof should return optional str 73 | var_of_type_str: str = "" 74 | var_of_type_str = s.WhichOneof("a_oneof") # E:3.8 75 | 76 | # Proto3 WhichOneof 77 | s6.WhichOneof("garbage") # E:3.8 78 | a3 = 5 79 | a3 = s6.WhichOneof("a_oneof") # E:3.8 80 | b3 = s6.WhichOneof("a_oneof") 81 | assert b3 is not None 82 | s6.HasField(b3) # allowed 83 | simple2.HasField(b3) # E:3.8 - it's a text but not one of the literals 84 | 85 | # Proto2 Extensions 86 | an_int = 5 87 | an_int = Extensions1.ext # E:3.8 88 | _ = s.Extensions[Extensions1.bad] # E:3.8 89 | e1 = s.Extensions[Extensions1.ext] 90 | e1.foo = 4 # E:3.8 91 | e1 = s.Extensions[Extensions2.foo] # E:3.8 92 | # The following 5 extension lines will error once we undo some compat 93 | # changes to typeshed a few months after the 1.24 release 94 | # See https://github.com/python/typeshed/pull/4833 95 | _ = s.Extensions["foo"] # E:3.8 96 | _ = s.Extensions[SeparateFileExtension.ext] # E:3.8 97 | _ = SeparateFileExtension.ext in s.Extensions # E:3.8 98 | del s.Extensions[SeparateFileExtension.ext] # E:3.8 99 | s.HasExtension(SeparateFileExtension.ext) # E:3.8 100 | simple2.ClearExtension(Extensions1.ext) # E:3.8 101 | 102 | 103 | for x in s.Extensions: 104 | x = 4 # E:3.8 105 | 106 | # Overload WhichOneof 107 | c = s6.WhichOneof("a_oneof") 108 | c = s6.WhichOneof("b_oneof") # E:3.8 109 | 110 | # Message DESCRIPTOR should detect invalid access via instance or class: 111 | SimpleProto3.DESCRIPTOR.Garbage() # E:3.8 112 | SimpleProto3().DESCRIPTOR.Garbage() # E:3.8 113 | 114 | # Enum DESCRIPTOR should detect invalid access: 115 | OuterEnum.DESCRIPTOR.Garbage() # E:3.8 116 | 117 | # Module DESCRIPTOR should detect invalid access: 118 | DESCRIPTOR.Garbage() # E:3.8 119 | 120 | # Enum value type should be an EnumValueType convertible to int 121 | enum = OuterEnum.V(5) 122 | enum = s6.a_outer_enum 123 | as_int = 5 124 | as_int = s6.a_outer_enum 125 | s6.a_outer_enum.FOO # E:3.8 126 | 127 | # Name/Value/items should inherit value type from _EnumTypeWrapper 128 | OuterEnum.Name(OuterEnum.V(5)) 129 | OuterEnum.Name(5) # E:3.8 130 | OuterEnum.Name(OuterEnum.Value("BAR")) 131 | OuterEnum.Name(SimpleProto3.InnerEnum.Value("BAR")) # E:3.8 132 | OuterEnum.Name(OuterEnum.values()[0]) 133 | OuterEnum.Name(SimpleProto3.InnerEnum.values()[0]) # E:3.8 134 | OuterEnum.Name(OuterEnum.items()[0][1]) 135 | OuterEnum.Name(SimpleProto3.InnerEnum.items()[0][1]) # E:3.8 136 | 137 | # Map field does not have get_or_create when mapping to a scalar type 138 | s7 = SimpleProto3() 139 | s7.map_scalar.get_or_create(0) # E:3.8 140 | # Incorrect key type should error 141 | s7.map_scalar.get("abcd") # E:3.8 142 | s7.map_message.get("abcd") # E:3.8 143 | # Incorrect value type should error 144 | map_val = 5 145 | map_val = s7.map_scalar.get(0) # E:3.8 146 | map_val = s7.map_message.get(0) # E:3.8 147 | # Incorrect constructor type should error 148 | s7 = SimpleProto3(map_scalar={"abcd": 5}, map_message={"abcd": "abcd"}) # E:3.8 149 | 150 | # Castable types are typed as their cast, not the base type 151 | s8 = Simple1() 152 | s8.user_id = 55 # E:3.8 153 | s8.email = "abcd@gmail.com" # E:3.8 154 | s8.email_by_uid[55] = "abcd@gmail.com" # E:3.8 155 | s8.email_by_uid[UserId(55)] = "abcd@gmail.com" # E:3.8 156 | s8.email_by_uid[55] = Email("abcd@gmail.com") # E:3.8 157 | s8 = Simple1( 158 | user_id=55, # E:3.8 159 | email="abcd@gmail.com", # E:3.8 160 | email_by_uid={55: "abcd@gmail.com"}, # E:3.8 161 | ) 162 | 163 | # Should not reexport inner.proto, since it doesn't have public tag. 164 | from testproto.reexport_pb2 import Inner # E:3.8 # noqa: F401,E402 165 | 166 | # In proto2 - you can pass in None for primitive, but not in proto3 167 | Simple2(a_string=None) 168 | OuterMessage3(a_string=None) # E:3.8 169 | 170 | # Repeated scalar fields are not assignable only extendable 171 | s9 = Simple1() 172 | s10 = Simple1() 173 | s9.a_repeated_string = s10.a_repeated_string # E:3.8 174 | s9.rep_inner_enum = s10.rep_inner_enum # E:3.8 175 | 176 | # Some reserved keywored testing 177 | # Confirm that messages and enums w/ reserved names type properly 178 | PythonReservedKeywords().none.valid 179 | PythonReservedKeywords().none.invalid # E:3.8 180 | # Enum should be int, even on enum with invalid name 181 | assert PythonReservedKeywords().valid == PythonReservedKeywords.valid_in_finally 182 | a_string = "hi" 183 | a_string = PythonReservedKeywords().valid # E:3.8 184 | 185 | stub0 = DummyServiceStub() # E:3.8 186 | channel = grpc.insecure_channel("127.0.0.1:8080") 187 | stub1 = DummyServiceStub(channel) 188 | request1 = DummyRequest() 189 | 190 | response1 = stub1.UnaryUnary(request1) 191 | value = response1.value 192 | value2 = response1.not_exists # E:3.8 193 | for _result1 in stub1.UnaryUnary(request1): # E:3.8 194 | pass 195 | 196 | for result2 in stub1.UnaryStream(request1): 197 | value = result2.value 198 | value2 = result2.not_exists # E:3.8 199 | response2 = stub1.UnaryStream(request1) 200 | value = response2.value # E:3.8 201 | 202 | 203 | def iter_requests() -> Generator[DummyRequest, None, None]: 204 | yield request1 205 | 206 | 207 | response3 = stub1.StreamUnary(request1) # E:3.8 208 | response4 = stub1.StreamUnary(iter_requests()) 209 | for _result3 in stub1.StreamUnary(request1): # E:3.8 210 | pass 211 | 212 | for _result4 in stub1.StreamStream(request1): # E:3.8 213 | pass 214 | for result5 in stub1.StreamStream(iter_requests()): 215 | value = result5.value 216 | value2 = result5.not_exists # E:3.8 217 | 218 | 219 | class GoodServicer(DummyServiceServicer): 220 | def UnaryUnary( 221 | self, 222 | request: DummyRequest, 223 | context: grpc.ServicerContext, 224 | ) -> DummyReply: 225 | return DummyReply() 226 | 227 | def UnaryStream( 228 | self, 229 | request: DummyRequest, 230 | context: grpc.ServicerContext, 231 | ) -> Iterator[DummyReply]: 232 | yield DummyReply() 233 | 234 | def StreamUnary( 235 | self, 236 | request: Iterator[DummyRequest], 237 | context: grpc.ServicerContext, 238 | ) -> DummyReply: 239 | for _data in request: 240 | pass 241 | return DummyReply() 242 | 243 | def StreamStream( 244 | self, 245 | request: Iterator[DummyRequest], 246 | context: grpc.ServicerContext, 247 | ) -> Iterator[DummyReply]: 248 | for _data in request: 249 | yield DummyReply() 250 | 251 | 252 | class BadServicer(DummyServiceServicer): 253 | def UnaryUnary( # E:3.8 254 | self, 255 | request: Iterator[DummyRequest], # E:3.8 256 | context: grpc.ServicerContext, 257 | ) -> Iterator[DummyReply]: 258 | for _data in request: 259 | yield DummyReply() 260 | 261 | def UnaryStream( # E:3.8 262 | self, 263 | request: Iterator[DummyRequest], # E:3.8 264 | context: grpc.ServicerContext, 265 | ) -> DummyReply: 266 | for _data in request: 267 | pass 268 | return DummyReply() 269 | 270 | def StreamUnary( # E:3.8 271 | self, 272 | request: DummyRequest, # E:3.8 273 | context: grpc.ServicerContext, 274 | ) -> Iterator[DummyReply]: 275 | yield DummyReply() 276 | -------------------------------------------------------------------------------- /test_negative/output.expected.3.8: -------------------------------------------------------------------------------- 1 | test_negative/negative.py:28: error: "Simple1" has no attribute "SerializeToStringg" [attr-defined] 2 | test_negative/negative.py:31: error: Argument 1 to "ParseFromString" of "Message" has incompatible type "Simple1"; expected "bytes" [arg-type] 3 | test_negative/negative.py:34: error: Argument 1 to "CopyFrom" of "Message" has incompatible type "bytes"; expected "Simple1" [arg-type] 4 | test_negative/negative.py:38: error: Argument 1 to "extend" of "list" has incompatible type "RepeatedScalarFieldContainer[str]"; expected "Iterable[int]" [arg-type] 5 | test_negative/negative.py:40: error: Argument "foo" to "TestMessage" has incompatible type "int"; expected "str" [arg-type] 6 | test_negative/negative.py:43: error: Incompatible types in assignment (expression has type "int", variable has type "ValueType") [assignment] 7 | test_negative/negative.py:46: error: Argument 1 to "HasField" of "Simple1" has incompatible type "Literal['garbage']"; expected "Literal['a_boolean', b'a_boolean', 'a_enum', b'a_enum', 'a_external_enum', b'a_external_enum', 'a_inner', b'a_inner', 'a_nested', b'a_nested', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'a_string', b'a_string', 'a_uint32', b'a_uint32', 'email', b'email', 'inner_enum', b'inner_enum', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'inner_message', b'inner_message', 'nested_enum', b'nested_enum', 'nested_message', b'nested_message', 'no_package', b'no_package', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message_in_oneof', b'outer_message_in_oneof', 'user_id', b'user_id']" [arg-type] 8 | test_negative/negative.py:47: error: Argument 1 to "HasField" of "Simple1" has incompatible type "Literal['a_repeated_string']"; expected "Literal['a_boolean', b'a_boolean', 'a_enum', b'a_enum', 'a_external_enum', b'a_external_enum', 'a_inner', b'a_inner', 'a_nested', b'a_nested', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'a_string', b'a_string', 'a_uint32', b'a_uint32', 'email', b'email', 'inner_enum', b'inner_enum', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'inner_message', b'inner_message', 'nested_enum', b'nested_enum', 'nested_message', b'nested_message', 'no_package', b'no_package', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message_in_oneof', b'outer_message_in_oneof', 'user_id', b'user_id']" [arg-type] 9 | test_negative/negative.py:51: error: Argument 1 to "HasField" of "SimpleProto3" has incompatible type "Literal['garbage']"; expected "Literal['OuterMessage3', b'OuterMessage3', '_an_optional_string', b'_an_optional_string', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'an_optional_string', b'an_optional_string', 'b_oneof', b'b_oneof', 'b_oneof_1', b'b_oneof_1', 'b_oneof_2', b'b_oneof_2', 'bool', b'bool', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message', b'outer_message', 'outer_message_in_oneof', b'outer_message_in_oneof']" [arg-type] 10 | test_negative/negative.py:52: error: Argument 1 to "HasField" of "SimpleProto3" has incompatible type "Literal['a_string']"; expected "Literal['OuterMessage3', b'OuterMessage3', '_an_optional_string', b'_an_optional_string', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'an_optional_string', b'an_optional_string', 'b_oneof', b'b_oneof', 'b_oneof_1', b'b_oneof_1', 'b_oneof_2', b'b_oneof_2', 'bool', b'bool', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message', b'outer_message', 'outer_message_in_oneof', b'outer_message_in_oneof']" [arg-type] 11 | test_negative/negative.py:53: error: Argument 1 to "HasField" of "SimpleProto3" has incompatible type "Literal['outer_enum']"; expected "Literal['OuterMessage3', b'OuterMessage3', '_an_optional_string', b'_an_optional_string', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'an_optional_string', b'an_optional_string', 'b_oneof', b'b_oneof', 'b_oneof_1', b'b_oneof_1', 'b_oneof_2', b'b_oneof_2', 'bool', b'bool', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message', b'outer_message', 'outer_message_in_oneof', b'outer_message_in_oneof']" [arg-type] 12 | test_negative/negative.py:54: error: Argument 1 to "HasField" of "SimpleProto3" has incompatible type "Literal['a_repeated_string']"; expected "Literal['OuterMessage3', b'OuterMessage3', '_an_optional_string', b'_an_optional_string', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'an_optional_string', b'an_optional_string', 'b_oneof', b'b_oneof', 'b_oneof_1', b'b_oneof_1', 'b_oneof_2', b'b_oneof_2', 'bool', b'bool', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message', b'outer_message', 'outer_message_in_oneof', b'outer_message_in_oneof']" [arg-type] 13 | test_negative/negative.py:57: error: Argument 1 to "ClearField" of "Simple1" has incompatible type "Literal['garbage']"; expected "Literal['a_boolean', b'a_boolean', 'a_enum', b'a_enum', 'a_external_enum', b'a_external_enum', 'a_inner', b'a_inner', 'a_nested', b'a_nested', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'a_repeated_string', b'a_repeated_string', 'a_string', b'a_string', 'a_uint32', b'a_uint32', 'email', b'email', 'email_by_uid', b'email_by_uid', 'inner_enum', b'inner_enum', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'inner_message', b'inner_message', 'nested_enum', b'nested_enum', 'nested_message', b'nested_message', 'no_package', b'no_package', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message_in_oneof', b'outer_message_in_oneof', 'rep_inner_enum', b'rep_inner_enum', 'rep_inner_message', b'rep_inner_message', 'user_id', b'user_id']" [arg-type] 14 | test_negative/negative.py:60: error: Argument 1 to "ClearField" of "SimpleProto3" has incompatible type "Literal['garbage']"; expected "Literal['OuterEnum', b'OuterEnum', 'OuterMessage3', b'OuterMessage3', '_an_optional_string', b'_an_optional_string', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'a_outer_enum', b'a_outer_enum', 'a_repeated_string', b'a_repeated_string', 'a_string', b'a_string', 'an_optional_string', b'an_optional_string', 'b_oneof', b'b_oneof', 'b_oneof_1', b'b_oneof_1', 'b_oneof_2', b'b_oneof_2', 'bool', b'bool', 'email', b'email', 'email_by_uid', b'email_by_uid', 'inner_enum', b'inner_enum', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'map_message', b'map_message', 'map_scalar', b'map_scalar', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message', b'outer_message', 'outer_message_in_oneof', b'outer_message_in_oneof', 'user_id', b'user_id']" [arg-type] 15 | test_negative/negative.py:63: error: Argument 1 to "WhichOneof" of "Simple1" has incompatible type "Literal['garbage']"; expected "Literal['a_oneof', b'a_oneof']" [arg-type] 16 | test_negative/negative.py:65: error: Incompatible types in assignment (expression has type "Optional[Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']]", variable has type "int") [assignment] 17 | test_negative/negative.py:70: error: Argument 1 to "HasField" of "Simple2" has incompatible type "Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']"; expected "Literal['a_string', b'a_string']" [arg-type] 18 | test_negative/negative.py:74: error: Incompatible types in assignment (expression has type "Optional[Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']]", variable has type "str") [assignment] 19 | test_negative/negative.py:77: error: No overload variant of "WhichOneof" of "SimpleProto3" matches argument type "str" [call-overload] 20 | test_negative/negative.py:77: note: Possible overload variants: 21 | test_negative/negative.py:77: note: def WhichOneof(self, oneof_group: Literal['_an_optional_string', b'_an_optional_string']) -> Optional[Literal['an_optional_string']] 22 | test_negative/negative.py:77: note: def WhichOneof(self, oneof_group: Literal['a_oneof', b'a_oneof']) -> Optional[Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']] 23 | test_negative/negative.py:77: note: def WhichOneof(self, oneof_group: Literal['b_oneof', b'b_oneof']) -> Optional[Literal['b_oneof_1', 'b_oneof_2']] 24 | test_negative/negative.py:79: error: Incompatible types in assignment (expression has type "Optional[Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']]", variable has type "int") [assignment] 25 | test_negative/negative.py:83: error: Argument 1 to "HasField" of "Simple2" has incompatible type "Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']"; expected "Literal['a_string', b'a_string']" [arg-type] 26 | test_negative/negative.py:87: error: Incompatible types in assignment (expression has type "_ExtensionFieldDescriptor[Simple1, Extensions1]", variable has type "int") [assignment] 27 | test_negative/negative.py:88: error: "Type[Extensions1]" has no attribute "bad" [attr-defined] 28 | test_negative/negative.py:90: error: "Extensions1" has no attribute "foo" [attr-defined] 29 | test_negative/negative.py:91: error: Incompatible types in assignment (expression has type "Extensions2", variable has type "Extensions1") [assignment] 30 | test_negative/negative.py:95: error: Invalid index type "str" for "_ExtensionDict[Simple1]"; expected type "_ExtensionFieldDescriptor[Simple1, Never]" [index] 31 | test_negative/negative.py:96: error: Invalid index type "_ExtensionFieldDescriptor[Simple2, SeparateFileExtension]" for "_ExtensionDict[Simple1]"; expected type "_ExtensionFieldDescriptor[Simple1, SeparateFileExtension]" [index] 32 | test_negative/negative.py:97: error: Unsupported operand types for in ("_ExtensionFieldDescriptor[Simple2, SeparateFileExtension]" and "_ExtensionDict[Simple1]") [operator] 33 | test_negative/negative.py:98: error: Argument 1 to "__delitem__" of "_ExtensionDict" has incompatible type "_ExtensionFieldDescriptor[Simple2, SeparateFileExtension]"; expected "_ExtensionFieldDescriptor[Simple1, SeparateFileExtension]" [arg-type] 34 | test_negative/negative.py:99: error: Argument 1 to "HasExtension" of "Message" has incompatible type "_ExtensionFieldDescriptor[Simple2, SeparateFileExtension]"; expected "_ExtensionFieldDescriptor[Simple1, Any]" [arg-type] 35 | test_negative/negative.py:100: error: Argument 1 to "ClearExtension" of "Message" has incompatible type "_ExtensionFieldDescriptor[Simple1, Extensions1]"; expected "_ExtensionFieldDescriptor[Simple2, Any]" [arg-type] 36 | test_negative/negative.py:104: error: Incompatible types in assignment (expression has type "int", variable has type "_ExtensionFieldDescriptor[Simple1, Any]") [assignment] 37 | test_negative/negative.py:108: error: Incompatible types in assignment (expression has type "Optional[Literal['b_oneof_1', 'b_oneof_2']]", variable has type "Optional[Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']]") [assignment] 38 | test_negative/negative.py:111: error: "Descriptor" has no attribute "Garbage" [attr-defined] 39 | test_negative/negative.py:112: error: "Descriptor" has no attribute "Garbage" [attr-defined] 40 | test_negative/negative.py:115: error: "EnumDescriptor" has no attribute "Garbage" [attr-defined] 41 | test_negative/negative.py:118: error: "FileDescriptor" has no attribute "Garbage" [attr-defined] 42 | test_negative/negative.py:125: error: "ValueType" has no attribute "FOO" [attr-defined] 43 | test_negative/negative.py:129: error: Argument 1 to "Name" of "_EnumTypeWrapper" has incompatible type "int"; expected "ValueType" [arg-type] 44 | test_negative/negative.py:131: error: Argument 1 to "Name" of "_EnumTypeWrapper" has incompatible type "testproto.test3_pb2.SimpleProto3._InnerEnum.ValueType"; expected "testproto.test3_pb2._OuterEnum.ValueType" [arg-type] 45 | test_negative/negative.py:133: error: Argument 1 to "Name" of "_EnumTypeWrapper" has incompatible type "testproto.test3_pb2.SimpleProto3._InnerEnum.ValueType"; expected "testproto.test3_pb2._OuterEnum.ValueType" [arg-type] 46 | test_negative/negative.py:135: error: Argument 1 to "Name" of "_EnumTypeWrapper" has incompatible type "testproto.test3_pb2.SimpleProto3._InnerEnum.ValueType"; expected "testproto.test3_pb2._OuterEnum.ValueType" [arg-type] 47 | test_negative/negative.py:139: error: "ScalarMap[int, str]" has no attribute "get_or_create" [attr-defined] 48 | test_negative/negative.py:141: error: No overload variant of "get" of "ScalarMap" matches argument type "str" [call-overload] 49 | test_negative/negative.py:141: note: Possible overload variants: 50 | test_negative/negative.py:141: note: def get(self, key: int, default: None = ...) -> Optional[str] 51 | test_negative/negative.py:141: note: def [_T] get(self, key: int, default: Union[str, _T]) -> Union[str, _T] 52 | test_negative/negative.py:142: error: No overload variant of "get" of "MessageMap" matches argument type "str" [call-overload] 53 | test_negative/negative.py:142: note: Possible overload variants: 54 | test_negative/negative.py:142: note: def get(self, key: int, default: None = ...) -> Optional[OuterMessage3] 55 | test_negative/negative.py:142: note: def [_T] get(self, key: int, default: Union[OuterMessage3, _T]) -> Union[OuterMessage3, _T] 56 | test_negative/negative.py:145: error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "int") [assignment] 57 | test_negative/negative.py:146: error: Incompatible types in assignment (expression has type "Optional[OuterMessage3]", variable has type "int") [assignment] 58 | test_negative/negative.py:148: error: Dict entry 0 has incompatible type "str": "int"; expected "int": "str" [dict-item] 59 | test_negative/negative.py:148: error: Dict entry 0 has incompatible type "str": "str"; expected "int": "OuterMessage3" [dict-item] 60 | test_negative/negative.py:152: error: Incompatible types in assignment (expression has type "int", variable has type "UserId") [assignment] 61 | test_negative/negative.py:153: error: Incompatible types in assignment (expression has type "str", variable has type "Email") [assignment] 62 | test_negative/negative.py:154: error: Invalid index type "int" for "ScalarMap[UserId, Email]"; expected type "UserId" [index] 63 | test_negative/negative.py:154: error: Incompatible types in assignment (expression has type "str", target has type "Email") [assignment] 64 | test_negative/negative.py:155: error: Incompatible types in assignment (expression has type "str", target has type "Email") [assignment] 65 | test_negative/negative.py:156: error: Invalid index type "int" for "ScalarMap[UserId, Email]"; expected type "UserId" [index] 66 | test_negative/negative.py:158: error: Argument "user_id" to "Simple1" has incompatible type "int"; expected "Optional[UserId]" [arg-type] 67 | test_negative/negative.py:159: error: Argument "email" to "Simple1" has incompatible type "str"; expected "Optional[Email]" [arg-type] 68 | test_negative/negative.py:160: error: Dict entry 0 has incompatible type "int": "str"; expected "UserId": "Email" [dict-item] 69 | test_negative/negative.py:164: error: Module "testproto.reexport_pb2" has no attribute "Inner" [attr-defined] 70 | test_negative/negative.py:168: error: Argument "a_string" to "OuterMessage3" has incompatible type "None"; expected "str" [arg-type] 71 | test_negative/negative.py:173: error: Property "a_repeated_string" defined in "Simple1" is read-only [misc] 72 | test_negative/negative.py:174: error: Property "rep_inner_enum" defined in "Simple1" is read-only [misc] 73 | test_negative/negative.py:179: error: "_r_None" has no attribute "invalid"; maybe "valid"? [attr-defined] 74 | test_negative/negative.py:183: error: Incompatible types in assignment (expression has type "ValueType", variable has type "str") [assignment] 75 | test_negative/negative.py:185: error: All overload variants of "DummyServiceStub" require at least one argument [call-overload] 76 | test_negative/negative.py:185: note: Possible overload variants: 77 | test_negative/negative.py:185: note: def [_DummyServiceUnaryUnaryType: (UnaryUnaryMultiCallable[DummyRequest, DummyReply], UnaryUnaryMultiCallable[DummyRequest, DummyReply]), _DummyServiceUnaryStreamType: (UnaryStreamMultiCallable[DummyRequest, DummyReply], UnaryStreamMultiCallable[DummyRequest, DummyReply]), _DummyServiceStreamUnaryType: (StreamUnaryMultiCallable[DummyRequest, DummyReply], StreamUnaryMultiCallable[DummyRequest, DummyReply]), _DummyServiceStreamStreamType: (StreamStreamMultiCallable[DummyRequest, DummyReply], StreamStreamMultiCallable[DummyRequest, DummyReply])] __init__(self, channel: Channel) -> DummyServiceStub[UnaryUnaryMultiCallable[DummyRequest, DummyReply], UnaryStreamMultiCallable[DummyRequest, DummyReply], StreamUnaryMultiCallable[DummyRequest, DummyReply], StreamStreamMultiCallable[DummyRequest, DummyReply]] 78 | test_negative/negative.py:192: error: "DummyReply" has no attribute "not_exists" [attr-defined] 79 | test_negative/negative.py:193: error: "DummyReply" has no attribute "__iter__" (not iterable) [attr-defined] 80 | test_negative/negative.py:198: error: "DummyReply" has no attribute "not_exists" [attr-defined] 81 | test_negative/negative.py:200: error: "CallIterator[DummyReply]" has no attribute "value" [attr-defined] 82 | test_negative/negative.py:207: error: Argument 1 to "__call__" of "StreamUnaryMultiCallable" has incompatible type "DummyRequest"; expected "Iterator[DummyRequest]" [arg-type] 83 | test_negative/negative.py:209: error: "DummyReply" has no attribute "__iter__" (not iterable) [attr-defined] 84 | test_negative/negative.py:209: error: Argument 1 to "__call__" of "StreamUnaryMultiCallable" has incompatible type "DummyRequest"; expected "Iterator[DummyRequest]" [arg-type] 85 | test_negative/negative.py:212: error: Argument 1 to "__call__" of "StreamStreamMultiCallable" has incompatible type "DummyRequest"; expected "Iterator[DummyRequest]" [arg-type] 86 | test_negative/negative.py:216: error: "DummyReply" has no attribute "not_exists" [attr-defined] 87 | test_negative/negative.py:253: error: Return type "Iterator[DummyReply]" of "UnaryUnary" incompatible with return type "Union[DummyReply, Awaitable[DummyReply]]" in supertype "DummyServiceServicer" [override] 88 | test_negative/negative.py:255: error: Argument 1 of "UnaryUnary" is incompatible with supertype "DummyServiceServicer"; supertype defines the argument type as "DummyRequest" [override] 89 | test_negative/negative.py:255: note: This violates the Liskov substitution principle 90 | test_negative/negative.py:255: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides 91 | test_negative/negative.py:261: error: Return type "DummyReply" of "UnaryStream" incompatible with return type "Union[Iterator[DummyReply], AsyncIterator[DummyReply]]" in supertype "DummyServiceServicer" [override] 92 | test_negative/negative.py:263: error: Argument 1 of "UnaryStream" is incompatible with supertype "DummyServiceServicer"; supertype defines the argument type as "DummyRequest" [override] 93 | test_negative/negative.py:263: note: This violates the Liskov substitution principle 94 | test_negative/negative.py:263: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides 95 | test_negative/negative.py:270: error: Return type "Iterator[DummyReply]" of "StreamUnary" incompatible with return type "Union[DummyReply, Awaitable[DummyReply]]" in supertype "DummyServiceServicer" [override] 96 | test_negative/negative.py:272: error: Argument 1 of "StreamUnary" is incompatible with supertype "DummyServiceServicer"; supertype defines the argument type as "_MaybeAsyncIterator[DummyRequest]" [override] 97 | test_negative/negative.py:272: note: This violates the Liskov substitution principle 98 | test_negative/negative.py:272: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides 99 | Found 80 errors in 1 file (checked 4 source files) 100 | -------------------------------------------------------------------------------- /test_negative/output.expected.3.8.omit_linenos: -------------------------------------------------------------------------------- 1 | test_negative/negative.py: error: "Simple1" has no attribute "SerializeToStringg" [attr-defined] 2 | test_negative/negative.py: error: Argument 1 to "ParseFromString" of "Message" has incompatible type "Simple1"; expected "bytes" [arg-type] 3 | test_negative/negative.py: error: Argument 1 to "CopyFrom" of "Message" has incompatible type "bytes"; expected "Simple1" [arg-type] 4 | test_negative/negative.py: error: Argument 1 to "extend" of "list" has incompatible type "RepeatedScalarFieldContainer[str]"; expected "Iterable[int]" [arg-type] 5 | test_negative/negative.py: error: Argument "foo" to "TestMessage" has incompatible type "int"; expected "str" [arg-type] 6 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "int", variable has type "ValueType") [assignment] 7 | test_negative/negative.py: error: Argument 1 to "HasField" of "Simple1" has incompatible type "Literal['garbage']"; expected "Literal['a_boolean', b'a_boolean', 'a_enum', b'a_enum', 'a_external_enum', b'a_external_enum', 'a_inner', b'a_inner', 'a_nested', b'a_nested', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'a_string', b'a_string', 'a_uint32', b'a_uint32', 'email', b'email', 'inner_enum', b'inner_enum', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'inner_message', b'inner_message', 'nested_enum', b'nested_enum', 'nested_message', b'nested_message', 'no_package', b'no_package', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message_in_oneof', b'outer_message_in_oneof', 'user_id', b'user_id']" [arg-type] 8 | test_negative/negative.py: error: Argument 1 to "HasField" of "Simple1" has incompatible type "Literal['a_repeated_string']"; expected "Literal['a_boolean', b'a_boolean', 'a_enum', b'a_enum', 'a_external_enum', b'a_external_enum', 'a_inner', b'a_inner', 'a_nested', b'a_nested', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'a_string', b'a_string', 'a_uint32', b'a_uint32', 'email', b'email', 'inner_enum', b'inner_enum', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'inner_message', b'inner_message', 'nested_enum', b'nested_enum', 'nested_message', b'nested_message', 'no_package', b'no_package', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message_in_oneof', b'outer_message_in_oneof', 'user_id', b'user_id']" [arg-type] 9 | test_negative/negative.py: error: Argument 1 to "HasField" of "SimpleProto3" has incompatible type "Literal['garbage']"; expected "Literal['OuterMessage3', b'OuterMessage3', '_an_optional_string', b'_an_optional_string', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'an_optional_string', b'an_optional_string', 'b_oneof', b'b_oneof', 'b_oneof_1', b'b_oneof_1', 'b_oneof_2', b'b_oneof_2', 'bool', b'bool', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message', b'outer_message', 'outer_message_in_oneof', b'outer_message_in_oneof']" [arg-type] 10 | test_negative/negative.py: error: Argument 1 to "HasField" of "SimpleProto3" has incompatible type "Literal['a_string']"; expected "Literal['OuterMessage3', b'OuterMessage3', '_an_optional_string', b'_an_optional_string', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'an_optional_string', b'an_optional_string', 'b_oneof', b'b_oneof', 'b_oneof_1', b'b_oneof_1', 'b_oneof_2', b'b_oneof_2', 'bool', b'bool', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message', b'outer_message', 'outer_message_in_oneof', b'outer_message_in_oneof']" [arg-type] 11 | test_negative/negative.py: error: Argument 1 to "HasField" of "SimpleProto3" has incompatible type "Literal['outer_enum']"; expected "Literal['OuterMessage3', b'OuterMessage3', '_an_optional_string', b'_an_optional_string', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'an_optional_string', b'an_optional_string', 'b_oneof', b'b_oneof', 'b_oneof_1', b'b_oneof_1', 'b_oneof_2', b'b_oneof_2', 'bool', b'bool', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message', b'outer_message', 'outer_message_in_oneof', b'outer_message_in_oneof']" [arg-type] 12 | test_negative/negative.py: error: Argument 1 to "HasField" of "SimpleProto3" has incompatible type "Literal['a_repeated_string']"; expected "Literal['OuterMessage3', b'OuterMessage3', '_an_optional_string', b'_an_optional_string', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'an_optional_string', b'an_optional_string', 'b_oneof', b'b_oneof', 'b_oneof_1', b'b_oneof_1', 'b_oneof_2', b'b_oneof_2', 'bool', b'bool', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message', b'outer_message', 'outer_message_in_oneof', b'outer_message_in_oneof']" [arg-type] 13 | test_negative/negative.py: error: Argument 1 to "ClearField" of "Simple1" has incompatible type "Literal['garbage']"; expected "Literal['a_boolean', b'a_boolean', 'a_enum', b'a_enum', 'a_external_enum', b'a_external_enum', 'a_inner', b'a_inner', 'a_nested', b'a_nested', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'a_repeated_string', b'a_repeated_string', 'a_string', b'a_string', 'a_uint32', b'a_uint32', 'email', b'email', 'email_by_uid', b'email_by_uid', 'inner_enum', b'inner_enum', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'inner_message', b'inner_message', 'nested_enum', b'nested_enum', 'nested_message', b'nested_message', 'no_package', b'no_package', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message_in_oneof', b'outer_message_in_oneof', 'rep_inner_enum', b'rep_inner_enum', 'rep_inner_message', b'rep_inner_message', 'user_id', b'user_id']" [arg-type] 14 | test_negative/negative.py: error: Argument 1 to "ClearField" of "SimpleProto3" has incompatible type "Literal['garbage']"; expected "Literal['OuterEnum', b'OuterEnum', 'OuterMessage3', b'OuterMessage3', '_an_optional_string', b'_an_optional_string', 'a_oneof', b'a_oneof', 'a_oneof_1', b'a_oneof_1', 'a_oneof_2', b'a_oneof_2', 'a_outer_enum', b'a_outer_enum', 'a_repeated_string', b'a_repeated_string', 'a_string', b'a_string', 'an_optional_string', b'an_optional_string', 'b_oneof', b'b_oneof', 'b_oneof_1', b'b_oneof_1', 'b_oneof_2', b'b_oneof_2', 'bool', b'bool', 'email', b'email', 'email_by_uid', b'email_by_uid', 'inner_enum', b'inner_enum', 'inner_enum_in_oneof', b'inner_enum_in_oneof', 'map_message', b'map_message', 'map_scalar', b'map_scalar', 'outer_enum_in_oneof', b'outer_enum_in_oneof', 'outer_message', b'outer_message', 'outer_message_in_oneof', b'outer_message_in_oneof', 'user_id', b'user_id']" [arg-type] 15 | test_negative/negative.py: error: Argument 1 to "WhichOneof" of "Simple1" has incompatible type "Literal['garbage']"; expected "Literal['a_oneof', b'a_oneof']" [arg-type] 16 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "Optional[Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']]", variable has type "int") [assignment] 17 | test_negative/negative.py: error: Argument 1 to "HasField" of "Simple2" has incompatible type "Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']"; expected "Literal['a_string', b'a_string']" [arg-type] 18 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "Optional[Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']]", variable has type "str") [assignment] 19 | test_negative/negative.py: error: No overload variant of "WhichOneof" of "SimpleProto3" matches argument type "str" [call-overload] 20 | test_negative/negative.py: note: Possible overload variants: 21 | test_negative/negative.py: note: def WhichOneof(self, oneof_group: Literal['_an_optional_string', b'_an_optional_string']) -> Optional[Literal['an_optional_string']] 22 | test_negative/negative.py: note: def WhichOneof(self, oneof_group: Literal['a_oneof', b'a_oneof']) -> Optional[Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']] 23 | test_negative/negative.py: note: def WhichOneof(self, oneof_group: Literal['b_oneof', b'b_oneof']) -> Optional[Literal['b_oneof_1', 'b_oneof_2']] 24 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "Optional[Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']]", variable has type "int") [assignment] 25 | test_negative/negative.py: error: Argument 1 to "HasField" of "Simple2" has incompatible type "Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']"; expected "Literal['a_string', b'a_string']" [arg-type] 26 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "_ExtensionFieldDescriptor[Simple1, Extensions1]", variable has type "int") [assignment] 27 | test_negative/negative.py: error: "Type[Extensions1]" has no attribute "bad" [attr-defined] 28 | test_negative/negative.py: error: "Extensions1" has no attribute "foo" [attr-defined] 29 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "Extensions2", variable has type "Extensions1") [assignment] 30 | test_negative/negative.py: error: Invalid index type "str" for "_ExtensionDict[Simple1]"; expected type "_ExtensionFieldDescriptor[Simple1, Never]" [index] 31 | test_negative/negative.py: error: Invalid index type "_ExtensionFieldDescriptor[Simple2, SeparateFileExtension]" for "_ExtensionDict[Simple1]"; expected type "_ExtensionFieldDescriptor[Simple1, SeparateFileExtension]" [index] 32 | test_negative/negative.py: error: Unsupported operand types for in ("_ExtensionFieldDescriptor[Simple2, SeparateFileExtension]" and "_ExtensionDict[Simple1]") [operator] 33 | test_negative/negative.py: error: Argument 1 to "__delitem__" of "_ExtensionDict" has incompatible type "_ExtensionFieldDescriptor[Simple2, SeparateFileExtension]"; expected "_ExtensionFieldDescriptor[Simple1, SeparateFileExtension]" [arg-type] 34 | test_negative/negative.py: error: Argument 1 to "HasExtension" of "Message" has incompatible type "_ExtensionFieldDescriptor[Simple2, SeparateFileExtension]"; expected "_ExtensionFieldDescriptor[Simple1, Any]" [arg-type] 35 | test_negative/negative.py: error: Argument 1 to "ClearExtension" of "Message" has incompatible type "_ExtensionFieldDescriptor[Simple1, Extensions1]"; expected "_ExtensionFieldDescriptor[Simple2, Any]" [arg-type] 36 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "int", variable has type "_ExtensionFieldDescriptor[Simple1, Any]") [assignment] 37 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "Optional[Literal['b_oneof_1', 'b_oneof_2']]", variable has type "Optional[Literal['a_oneof_1', 'a_oneof_2', 'outer_message_in_oneof', 'outer_enum_in_oneof', 'inner_enum_in_oneof']]") [assignment] 38 | test_negative/negative.py: error: "Descriptor" has no attribute "Garbage" [attr-defined] 39 | test_negative/negative.py: error: "Descriptor" has no attribute "Garbage" [attr-defined] 40 | test_negative/negative.py: error: "EnumDescriptor" has no attribute "Garbage" [attr-defined] 41 | test_negative/negative.py: error: "FileDescriptor" has no attribute "Garbage" [attr-defined] 42 | test_negative/negative.py: error: "ValueType" has no attribute "FOO" [attr-defined] 43 | test_negative/negative.py: error: Argument 1 to "Name" of "_EnumTypeWrapper" has incompatible type "int"; expected "ValueType" [arg-type] 44 | test_negative/negative.py: error: Argument 1 to "Name" of "_EnumTypeWrapper" has incompatible type "testproto.test3_pb2.SimpleProto3._InnerEnum.ValueType"; expected "testproto.test3_pb2._OuterEnum.ValueType" [arg-type] 45 | test_negative/negative.py: error: Argument 1 to "Name" of "_EnumTypeWrapper" has incompatible type "testproto.test3_pb2.SimpleProto3._InnerEnum.ValueType"; expected "testproto.test3_pb2._OuterEnum.ValueType" [arg-type] 46 | test_negative/negative.py: error: Argument 1 to "Name" of "_EnumTypeWrapper" has incompatible type "testproto.test3_pb2.SimpleProto3._InnerEnum.ValueType"; expected "testproto.test3_pb2._OuterEnum.ValueType" [arg-type] 47 | test_negative/negative.py: error: "ScalarMap[int, str]" has no attribute "get_or_create" [attr-defined] 48 | test_negative/negative.py: error: No overload variant of "get" of "ScalarMap" matches argument type "str" [call-overload] 49 | test_negative/negative.py: note: Possible overload variants: 50 | test_negative/negative.py: note: def get(self, key: int, default: None = ...) -> Optional[str] 51 | test_negative/negative.py: note: def [_T] get(self, key: int, default: Union[str, _T]) -> Union[str, _T] 52 | test_negative/negative.py: error: No overload variant of "get" of "MessageMap" matches argument type "str" [call-overload] 53 | test_negative/negative.py: note: Possible overload variants: 54 | test_negative/negative.py: note: def get(self, key: int, default: None = ...) -> Optional[OuterMessage3] 55 | test_negative/negative.py: note: def [_T] get(self, key: int, default: Union[OuterMessage3, _T]) -> Union[OuterMessage3, _T] 56 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "int") [assignment] 57 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "Optional[OuterMessage3]", variable has type "int") [assignment] 58 | test_negative/negative.py: error: Dict entry 0 has incompatible type "str": "int"; expected "int": "str" [dict-item] 59 | test_negative/negative.py: error: Dict entry 0 has incompatible type "str": "str"; expected "int": "OuterMessage3" [dict-item] 60 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "int", variable has type "UserId") [assignment] 61 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "str", variable has type "Email") [assignment] 62 | test_negative/negative.py: error: Invalid index type "int" for "ScalarMap[UserId, Email]"; expected type "UserId" [index] 63 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "str", target has type "Email") [assignment] 64 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "str", target has type "Email") [assignment] 65 | test_negative/negative.py: error: Invalid index type "int" for "ScalarMap[UserId, Email]"; expected type "UserId" [index] 66 | test_negative/negative.py: error: Argument "user_id" to "Simple1" has incompatible type "int"; expected "Optional[UserId]" [arg-type] 67 | test_negative/negative.py: error: Argument "email" to "Simple1" has incompatible type "str"; expected "Optional[Email]" [arg-type] 68 | test_negative/negative.py: error: Dict entry 0 has incompatible type "int": "str"; expected "UserId": "Email" [dict-item] 69 | test_negative/negative.py: error: Module "testproto.reexport_pb2" has no attribute "Inner" [attr-defined] 70 | test_negative/negative.py: error: Argument "a_string" to "OuterMessage3" has incompatible type "None"; expected "str" [arg-type] 71 | test_negative/negative.py: error: Property "a_repeated_string" defined in "Simple1" is read-only [misc] 72 | test_negative/negative.py: error: Property "rep_inner_enum" defined in "Simple1" is read-only [misc] 73 | test_negative/negative.py: error: "_r_None" has no attribute "invalid"; maybe "valid"? [attr-defined] 74 | test_negative/negative.py: error: Incompatible types in assignment (expression has type "ValueType", variable has type "str") [assignment] 75 | test_negative/negative.py: error: All overload variants of "DummyServiceStub" require at least one argument [call-overload] 76 | test_negative/negative.py: note: Possible overload variants: 77 | test_negative/negative.py: note: def [_DummyServiceUnaryUnaryType: (UnaryUnaryMultiCallable[DummyRequest, DummyReply], UnaryUnaryMultiCallable[DummyRequest, DummyReply]), _DummyServiceUnaryStreamType: (UnaryStreamMultiCallable[DummyRequest, DummyReply], UnaryStreamMultiCallable[DummyRequest, DummyReply]), _DummyServiceStreamUnaryType: (StreamUnaryMultiCallable[DummyRequest, DummyReply], StreamUnaryMultiCallable[DummyRequest, DummyReply]), _DummyServiceStreamStreamType: (StreamStreamMultiCallable[DummyRequest, DummyReply], StreamStreamMultiCallable[DummyRequest, DummyReply])] __init__(self, channel: Channel) -> DummyServiceStub[UnaryUnaryMultiCallable[DummyRequest, DummyReply], UnaryStreamMultiCallable[DummyRequest, DummyReply], StreamUnaryMultiCallable[DummyRequest, DummyReply], StreamStreamMultiCallable[DummyRequest, DummyReply]] 78 | test_negative/negative.py: error: "DummyReply" has no attribute "not_exists" [attr-defined] 79 | test_negative/negative.py: error: "DummyReply" has no attribute "__iter__" (not iterable) [attr-defined] 80 | test_negative/negative.py: error: "DummyReply" has no attribute "not_exists" [attr-defined] 81 | test_negative/negative.py: error: "CallIterator[DummyReply]" has no attribute "value" [attr-defined] 82 | test_negative/negative.py: error: Argument 1 to "__call__" of "StreamUnaryMultiCallable" has incompatible type "DummyRequest"; expected "Iterator[DummyRequest]" [arg-type] 83 | test_negative/negative.py: error: "DummyReply" has no attribute "__iter__" (not iterable) [attr-defined] 84 | test_negative/negative.py: error: Argument 1 to "__call__" of "StreamUnaryMultiCallable" has incompatible type "DummyRequest"; expected "Iterator[DummyRequest]" [arg-type] 85 | test_negative/negative.py: error: Argument 1 to "__call__" of "StreamStreamMultiCallable" has incompatible type "DummyRequest"; expected "Iterator[DummyRequest]" [arg-type] 86 | test_negative/negative.py: error: "DummyReply" has no attribute "not_exists" [attr-defined] 87 | test_negative/negative.py: error: Return type "Iterator[DummyReply]" of "UnaryUnary" incompatible with return type "Union[DummyReply, Awaitable[DummyReply]]" in supertype "DummyServiceServicer" [override] 88 | test_negative/negative.py: error: Argument 1 of "UnaryUnary" is incompatible with supertype "DummyServiceServicer"; supertype defines the argument type as "DummyRequest" [override] 89 | test_negative/negative.py: note: This violates the Liskov substitution principle 90 | test_negative/negative.py: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides 91 | test_negative/negative.py: error: Return type "DummyReply" of "UnaryStream" incompatible with return type "Union[Iterator[DummyReply], AsyncIterator[DummyReply]]" in supertype "DummyServiceServicer" [override] 92 | test_negative/negative.py: error: Argument 1 of "UnaryStream" is incompatible with supertype "DummyServiceServicer"; supertype defines the argument type as "DummyRequest" [override] 93 | test_negative/negative.py: note: This violates the Liskov substitution principle 94 | test_negative/negative.py: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides 95 | test_negative/negative.py: error: Return type "Iterator[DummyReply]" of "StreamUnary" incompatible with return type "Union[DummyReply, Awaitable[DummyReply]]" in supertype "DummyServiceServicer" [override] 96 | test_negative/negative.py: error: Argument 1 of "StreamUnary" is incompatible with supertype "DummyServiceServicer"; supertype defines the argument type as "_MaybeAsyncIterator[DummyRequest]" [override] 97 | test_negative/negative.py: note: This violates the Liskov substitution principle 98 | test_negative/negative.py: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides 99 | Found 80 errors in 1 file (checked 4 source files) 100 | -------------------------------------------------------------------------------- /test_requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements to run unit tests. Tests import from 2 | # generated code. 3 | protobuf==5.28.3 4 | pytest==8.3.3 5 | pytest-asyncio==0.24.0 6 | grpc-stubs==1.53.0.5 7 | grpcio-tools==1.67.0 8 | types-protobuf==5.28.3.20241030 9 | --------------------------------------------------------------------------------