• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 22285099215

22 Feb 2026 08:52PM UTC coverage: 75.854% (-17.1%) from 92.936%
22285099215

Pull #23121

github

web-flow
Merge c7299df9c into ba8359840
Pull Request #23121: fix issue with optional fields in dependency validator

28 of 29 new or added lines in 2 files covered. (96.55%)

11174 existing lines in 400 files now uncovered.

53694 of 70786 relevant lines covered (75.85%)

1.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.3
/src/python/pants/backend/codegen/protobuf/python/python_protobuf_subsystem.py
1
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from dataclasses import dataclass
2✔
5

6
from pants.backend.codegen.protobuf.python.additional_fields import ProtobufPythonResolveField
2✔
7
from pants.backend.codegen.protobuf.target_types import (
2✔
8
    ProtobufDependenciesField,
9
    ProtobufGrpcToggleField,
10
)
11
from pants.backend.codegen.utils import find_python_runtime_library_or_raise_error
2✔
12
from pants.backend.python.dependency_inference.module_mapper import (
2✔
13
    PythonModuleOwnersRequest,
14
    map_module_to_address,
15
)
16
from pants.backend.python.dependency_inference.subsystem import (
2✔
17
    AmbiguityResolution,
18
    PythonInferSubsystem,
19
)
20
from pants.backend.python.subsystems.python_tool_base import PythonToolRequirementsBase
2✔
21
from pants.backend.python.subsystems.setup import PythonSetup
2✔
22
from pants.engine.rules import collect_rules, implicitly, rule
2✔
23
from pants.engine.target import FieldSet, InferDependenciesRequest, InferredDependencies
2✔
24
from pants.engine.unions import UnionRule
2✔
25
from pants.option.option_types import BoolOption
2✔
26
from pants.option.subsystem import Subsystem
2✔
27
from pants.source.source_root import SourceRootRequest, get_source_root
2✔
28
from pants.util.docutil import doc_url
2✔
29
from pants.util.strutil import help_text, softwrap
2✔
30

31

32
class PythonProtobufSubsystem(Subsystem):
2✔
33
    options_scope = "python-protobuf"
2✔
34
    help = help_text(
2✔
35
        f"""
36
        Options related to the Protobuf Python backend.
37

38
        See {doc_url("docs/python/integrations/protobuf-and-grpc")}.
39
        """
40
    )
41

42
    grpcio_plugin = BoolOption(
2✔
43
        default=True,
44
        help=softwrap(
45
            """
46
            Use the official `grpcio` plugin (https://pypi.org/project/grpcio/) to generate grpc
47
            service stubs.
48
            """
49
        ),
50
    )
51

52
    grpclib_plugin = BoolOption(
2✔
53
        default=False,
54
        help=softwrap(
55
            """
56
            Use the alternative `grpclib` plugin (https://github.com/vmagamedov/grpclib) to
57
            generate grpc service stubs.
58
            """
59
        ),
60
    )
61

62
    generate_type_stubs = BoolOption(
2✔
63
        default=False,
64
        mutually_exclusive_group="typestubs",
65
        help=softwrap(
66
            """
67
            If True, then configure `protoc` to also generate `.pyi` type stubs for each generated
68
            Python file. This option will work wih any recent version of `protoc` and should
69
            be preferred over the `--python-protobuf-mypy-plugin` option.
70
            """
71
        ),
72
    )
73

74
    mypy_plugin = BoolOption(
2✔
75
        default=False,
76
        mutually_exclusive_group="typestubs",
77
        help=softwrap(
78
            """
79
            Use the `mypy-protobuf` plugin (https://github.com/dropbox/mypy-protobuf) to also
80
            generate `.pyi` type stubs.
81

82
            Please prefer the `--python-protobuf-generate-type-stubs` option over this option
83
            since recent versions of `protoc` have the ability to directly generate type stubs.
84
            """
85
        ),
86
    )
87

88
    infer_runtime_dependency = BoolOption(
2✔
89
        default=True,
90
        help=softwrap(
91
            """
92
            If True, will add a dependency on a `python_requirement` target exposing the
93
            `protobuf` module (usually from the `protobuf` requirement). If the `protobuf_source`
94
            target sets `grpc=True`, will also add a dependency on the `python_requirement`
95
            target exposing the `grpcio` module.
96

97
            If `[python].enable_resolves` is set, Pants will only infer dependencies on
98
            `python_requirement` targets that use the same resolve as the particular
99
            `protobuf_source` / `protobuf_sources` target uses, which is set via its
100
            `python_resolve` field.
101

102
            Unless this option is disabled, Pants will error if no relevant target is found or
103
            if more than one is found which causes ambiguity.
104
            """
105
        ),
106
        advanced=True,
107
    )
108

109

110
class PythonProtobufMypyPlugin(PythonToolRequirementsBase):
2✔
111
    options_scope = "mypy-protobuf"
2✔
112
    help_short = "Configuration of the mypy-protobuf type stub generation plugin."
2✔
113

114
    default_requirements = ["mypy-protobuf>=3.4.0,<4"]
2✔
115

116
    register_interpreter_constraints = True
2✔
117

118
    default_lockfile_resource = ("pants.backend.codegen.protobuf.python", "mypy_protobuf.lock")
2✔
119

120

121
class PythonProtobufGrpclibPlugin(PythonToolRequirementsBase):
2✔
122
    options_scope = "python-grpclib-protobuf"
2✔
123
    help_short = "Configuration of the grpclib plugin."
2✔
124

125
    default_requirements = ["grpclib[protobuf]>=0.4,<1"]
2✔
126

127
    register_interpreter_constraints = True
2✔
128

129
    default_lockfile_resource = ("pants.backend.codegen.protobuf.python", "grpclib.lock")
2✔
130

131

132
@dataclass(frozen=True)
2✔
133
class PythonProtobufDependenciesInferenceFieldSet(FieldSet):
2✔
134
    required_fields = (
2✔
135
        ProtobufDependenciesField,
136
        ProtobufPythonResolveField,
137
        ProtobufGrpcToggleField,
138
    )
139

140
    dependencies: ProtobufDependenciesField
141
    python_resolve: ProtobufPythonResolveField
142
    grpc_toggle: ProtobufGrpcToggleField
143

144

145
class InferPythonProtobufDependencies(InferDependenciesRequest):
2✔
146
    infer_from = PythonProtobufDependenciesInferenceFieldSet
2✔
147

148

149
@rule
2✔
150
async def infer_dependencies(
2✔
151
    request: InferPythonProtobufDependencies,
152
    python_protobuf: PythonProtobufSubsystem,
153
    python_setup: PythonSetup,
154
    python_infer_subsystem: PythonInferSubsystem,
155
) -> InferredDependencies:
156
    if not python_protobuf.infer_runtime_dependency:
2✔
157
        return InferredDependencies([])
1✔
158

159
    resolve = request.field_set.python_resolve.normalized_value(python_setup)
1✔
160

161
    locality = None
1✔
162
    if python_infer_subsystem.ambiguity_resolution == AmbiguityResolution.by_source_root:
1✔
UNCOV
163
        source_root = await get_source_root(
×
164
            SourceRootRequest.for_address(request.field_set.address)
165
        )
UNCOV
166
        locality = source_root.path
×
167

168
    result = []
1✔
169
    addresses_for_protobuf = await map_module_to_address(
1✔
170
        PythonModuleOwnersRequest(
171
            "google.protobuf",
172
            resolve=resolve,
173
            locality=locality,
174
        ),
175
        **implicitly(),
176
    )
177

178
    result.append(
1✔
179
        find_python_runtime_library_or_raise_error(
180
            addresses_for_protobuf,
181
            request.field_set.address,
182
            "google.protobuf",
183
            resolve=resolve,
184
            resolves_enabled=python_setup.enable_resolves,
185
            recommended_requirement_name="protobuf",
186
            recommended_requirement_url="https://pypi.org/project/protobuf/",
187
            disable_inference_option=f"[{python_protobuf.options_scope}].infer_runtime_dependency",
188
        )
189
    )
190

191
    if request.field_set.grpc_toggle.value:
1✔
UNCOV
192
        if python_protobuf.grpcio_plugin:
×
UNCOV
193
            addresses_for_grpc = await map_module_to_address(
×
194
                PythonModuleOwnersRequest(
195
                    "grpc",
196
                    resolve=resolve,
197
                    locality=locality,
198
                ),
199
                **implicitly(),
200
            )
201

UNCOV
202
            result.append(
×
203
                find_python_runtime_library_or_raise_error(
204
                    addresses_for_grpc,
205
                    request.field_set.address,
206
                    # Note that the library is called `grpcio`, but the module is `grpc`.
207
                    "grpc",
208
                    resolve=resolve,
209
                    resolves_enabled=python_setup.enable_resolves,
210
                    recommended_requirement_name="grpcio",
211
                    recommended_requirement_url="https://pypi.org/project/grpcio/",
212
                    disable_inference_option=f"[{python_protobuf.options_scope}].infer_runtime_dependency",
213
                )
214
            )
215

UNCOV
216
        if python_protobuf.grpclib_plugin:
×
UNCOV
217
            addresses_for_grpclib = await map_module_to_address(
×
218
                PythonModuleOwnersRequest(
219
                    "grpclib",
220
                    resolve=resolve,
221
                    locality=locality,
222
                ),
223
                **implicitly(),
224
            )
225

UNCOV
226
            result.append(
×
227
                find_python_runtime_library_or_raise_error(
228
                    addresses_for_grpclib,
229
                    request.field_set.address,
230
                    "grpclib",
231
                    resolve=resolve,
232
                    resolves_enabled=python_setup.enable_resolves,
233
                    recommended_requirement_name="grpclib[protobuf]",
234
                    recommended_requirement_url="https://pypi.org/project/grpclib/",
235
                    disable_inference_option=f"[{python_protobuf.options_scope}].infer_runtime_dependency",
236
                )
237
            )
238

239
    return InferredDependencies(result)
1✔
240

241

242
def rules():
2✔
243
    return [
2✔
244
        *collect_rules(),
245
        UnionRule(InferDependenciesRequest, InferPythonProtobufDependencies),
246
    ]
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc