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

pantsbuild / pants / 21803785359

08 Feb 2026 07:13PM UTC coverage: 43.3% (-37.0%) from 80.277%
21803785359

Pull #23085

github

web-flow
Merge 7c1cd926d into 40389cc58
Pull Request #23085: A helper method for indexing paths by source root

2 of 6 new or added lines in 1 file covered. (33.33%)

17114 existing lines in 539 files now uncovered.

26075 of 60219 relevant lines covered (43.3%)

0.43 hits per line

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

53.16
/src/python/pants/engine/internals/selectors.py
1
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
1✔
5

6
import itertools
1✔
7
from collections.abc import Coroutine, Generator, Iterable
1✔
8
from dataclasses import dataclass
1✔
9
from typing import Any, TypeVar, cast, overload
1✔
10

11
from pants.engine.internals.native_engine import PyGeneratorResponseCall
1✔
12
from pants.util.strutil import softwrap
1✔
13

14
_Output = TypeVar("_Output")
1✔
15

16

17
@dataclass(frozen=True)
1✔
18
class AwaitableConstraints:
1✔
19
    rule_id: str
1✔
20
    output_type: type
1✔
21
    # The number of explicit positional arguments passed to a call-by-name awaitable.
22
    explicit_args_arity: int
1✔
23
    input_types: tuple[type, ...]
1✔
24

25
    def __repr__(self) -> str:
1✔
UNCOV
26
        inputs = ", ".join(f"{t.__name__}" for t in self.input_types)
×
UNCOV
27
        return f"{self.rule_id}({inputs}) -> {self.output_type.__name__}"
×
28

29
    def __str__(self) -> str:
1✔
UNCOV
30
        return repr(self)
×
31

32

33
class Call(PyGeneratorResponseCall):
1✔
34
    def __await__(
1✔
35
        self,
36
    ) -> Generator[Any, None, Any]:
37
        result = yield self
1✔
38
        return result
1✔
39

40
    def __repr__(self) -> str:
1✔
41
        return f"Call({self.rule_id}(...) -> {self.output_type.__name__})"
×
42

43

44
@dataclass(frozen=True)
1✔
45
class _Concurrently:
1✔
46
    calls: tuple[Coroutine, ...]
1✔
47

48
    def __await__(self) -> Generator[tuple[Coroutine, ...], None, tuple]:
1✔
UNCOV
49
        result = yield self.calls
×
UNCOV
50
        return cast(tuple, result)
×
51

52

53
# These type variables are used to parametrize from 1 to 10 args when used in a tuple-style
54
# concurrently() call.
55

56
_Out0 = TypeVar("_Out0")
1✔
57
_Out1 = TypeVar("_Out1")
1✔
58
_Out2 = TypeVar("_Out2")
1✔
59
_Out3 = TypeVar("_Out3")
1✔
60
_Out4 = TypeVar("_Out4")
1✔
61
_Out5 = TypeVar("_Out5")
1✔
62
_Out6 = TypeVar("_Out6")
1✔
63
_Out7 = TypeVar("_Out7")
1✔
64
_Out8 = TypeVar("_Out8")
1✔
65
_Out9 = TypeVar("_Out9")
1✔
66

67

68
@overload
69
async def Concurrently(
70
    __gets: Iterable[Coroutine[Any, Any, _Output]],
71
) -> tuple[_Output, ...]: ...
72

73

74
@overload
75
async def Concurrently(
76
    __get0: Coroutine[Any, Any, _Output],
77
    __get1: Coroutine[Any, Any, _Output],
78
    __get2: Coroutine[Any, Any, _Output],
79
    __get3: Coroutine[Any, Any, _Output],
80
    __get4: Coroutine[Any, Any, _Output],
81
    __get5: Coroutine[Any, Any, _Output],
82
    __get6: Coroutine[Any, Any, _Output],
83
    __get7: Coroutine[Any, Any, _Output],
84
    __get8: Coroutine[Any, Any, _Output],
85
    __get9: Coroutine[Any, Any, _Output],
86
    __get10: Coroutine[Any, Any, _Output],
87
    *__gets: Coroutine[Any, Any, _Output],
88
) -> tuple[_Output, ...]: ...
89

90

91
@overload
92
async def Concurrently(
93
    __get0: Coroutine[Any, Any, _Out0],
94
    __get1: Coroutine[Any, Any, _Out1],
95
    __get2: Coroutine[Any, Any, _Out2],
96
    __get3: Coroutine[Any, Any, _Out3],
97
    __get4: Coroutine[Any, Any, _Out4],
98
    __get5: Coroutine[Any, Any, _Out5],
99
    __get6: Coroutine[Any, Any, _Out6],
100
    __get7: Coroutine[Any, Any, _Out7],
101
    __get8: Coroutine[Any, Any, _Out8],
102
    __get9: Coroutine[Any, Any, _Out9],
103
) -> tuple[_Out0, _Out1, _Out2, _Out3, _Out4, _Out5, _Out6, _Out7, _Out8, _Out9]: ...
104

105

106
@overload
107
async def Concurrently(
108
    __get0: Coroutine[Any, Any, _Out0],
109
    __get1: Coroutine[Any, Any, _Out1],
110
    __get2: Coroutine[Any, Any, _Out2],
111
    __get3: Coroutine[Any, Any, _Out3],
112
    __get4: Coroutine[Any, Any, _Out4],
113
    __get5: Coroutine[Any, Any, _Out5],
114
    __get6: Coroutine[Any, Any, _Out6],
115
    __get7: Coroutine[Any, Any, _Out7],
116
    __get8: Coroutine[Any, Any, _Out8],
117
) -> tuple[_Out0, _Out1, _Out2, _Out3, _Out4, _Out5, _Out6, _Out7, _Out8]: ...
118

119

120
@overload
121
async def Concurrently(
122
    __get0: Coroutine[Any, Any, _Out0],
123
    __get1: Coroutine[Any, Any, _Out1],
124
    __get2: Coroutine[Any, Any, _Out2],
125
    __get3: Coroutine[Any, Any, _Out3],
126
    __get4: Coroutine[Any, Any, _Out4],
127
    __get5: Coroutine[Any, Any, _Out5],
128
    __get6: Coroutine[Any, Any, _Out6],
129
    __get7: Coroutine[Any, Any, _Out7],
130
) -> tuple[_Out0, _Out1, _Out2, _Out3, _Out4, _Out5, _Out6, _Out7]: ...
131

132

133
@overload
134
async def Concurrently(
135
    __get0: Coroutine[Any, Any, _Out0],
136
    __get1: Coroutine[Any, Any, _Out1],
137
    __get2: Coroutine[Any, Any, _Out2],
138
    __get3: Coroutine[Any, Any, _Out3],
139
    __get4: Coroutine[Any, Any, _Out4],
140
    __get5: Coroutine[Any, Any, _Out5],
141
    __get6: Coroutine[Any, Any, _Out6],
142
) -> tuple[_Out0, _Out1, _Out2, _Out3, _Out4, _Out5, _Out6]: ...
143

144

145
@overload
146
async def Concurrently(
147
    __get0: Coroutine[Any, Any, _Out0],
148
    __get1: Coroutine[Any, Any, _Out1],
149
    __get2: Coroutine[Any, Any, _Out2],
150
    __get3: Coroutine[Any, Any, _Out3],
151
    __get4: Coroutine[Any, Any, _Out4],
152
    __get5: Coroutine[Any, Any, _Out5],
153
) -> tuple[_Out0, _Out1, _Out2, _Out3, _Out4, _Out5]: ...
154

155

156
@overload
157
async def Concurrently(
158
    __get0: Coroutine[Any, Any, _Out0],
159
    __get1: Coroutine[Any, Any, _Out1],
160
    __get2: Coroutine[Any, Any, _Out2],
161
    __get3: Coroutine[Any, Any, _Out3],
162
    __get4: Coroutine[Any, Any, _Out4],
163
) -> tuple[_Out0, _Out1, _Out2, _Out3, _Out4]: ...
164

165

166
@overload
167
async def Concurrently(
168
    __get0: Coroutine[Any, Any, _Out0],
169
    __get1: Coroutine[Any, Any, _Out1],
170
    __get2: Coroutine[Any, Any, _Out2],
171
    __get3: Coroutine[Any, Any, _Out3],
172
) -> tuple[_Out0, _Out1, _Out2, _Out3]: ...
173

174

175
@overload
176
async def Concurrently(
177
    __get0: Coroutine[Any, Any, _Out0],
178
    __get1: Coroutine[Any, Any, _Out1],
179
    __get2: Coroutine[Any, Any, _Out2],
180
) -> tuple[_Out0, _Out1, _Out2]: ...
181

182

183
@overload
184
async def Concurrently(
185
    __get0: Coroutine[Any, Any, _Out0],
186
    __get1: Coroutine[Any, Any, _Out1],
187
) -> tuple[_Out0, _Out1]: ...
188

189

190
async def Concurrently(
1✔
191
    __arg0: (Iterable[Coroutine[Any, Any, _Output]] | Coroutine[Any, Any, _Out0]),
192
    __arg1: Coroutine[Any, Any, _Out1] | None = None,
193
    __arg2: Coroutine[Any, Any, _Out2] | None = None,
194
    __arg3: Coroutine[Any, Any, _Out3] | None = None,
195
    __arg4: Coroutine[Any, Any, _Out4] | None = None,
196
    __arg5: Coroutine[Any, Any, _Out5] | None = None,
197
    __arg6: Coroutine[Any, Any, _Out6] | None = None,
198
    __arg7: Coroutine[Any, Any, _Out7] | None = None,
199
    __arg8: Coroutine[Any, Any, _Out8] | None = None,
200
    __arg9: Coroutine[Any, Any, _Out9] | None = None,
201
    *__args: Coroutine[Any, Any, _Output],
202
) -> (
203
    tuple[_Output, ...]
204
    | tuple[_Out0, _Out1, _Out2, _Out3, _Out4, _Out5, _Out6, _Out7, _Out8, _Out9]
205
    | tuple[_Out0, _Out1, _Out2, _Out3, _Out4, _Out5, _Out6, _Out7, _Out8]
206
    | tuple[_Out0, _Out1, _Out2, _Out3, _Out4, _Out5, _Out6, _Out7]
207
    | tuple[_Out0, _Out1, _Out2, _Out3, _Out4, _Out5, _Out6]
208
    | tuple[_Out0, _Out1, _Out2, _Out3, _Out4, _Out5]
209
    | tuple[_Out0, _Out1, _Out2, _Out3, _Out4]
210
    | tuple[_Out0, _Out1, _Out2, _Out3]
211
    | tuple[_Out0, _Out1, _Out2]
212
    | tuple[_Out0, _Out1]
213
    | tuple[_Out0]
214
):
215
    """Yield a tuple of Coroutine instances all at once.
216

217
    The `yield`ed value `self.calls` is interpreted by the engine within
218
    `generator_send()`. This class will yield a tuple of Coroutine instances,
219
    which is converted into `PyGeneratorResponse::GetMulti`.
220

221
    The engine will fulfill these coroutines in parallel, and return a tuple of _Output
222
    instances to this method, which then returns this tuple to the `@rule` which called
223
    `await concurrently(...) for ... in ...)`.
224
    """
UNCOV
225
    if (
×
226
        isinstance(__arg0, Iterable)
227
        and __arg1 is None
228
        and __arg2 is None
229
        and __arg3 is None
230
        and __arg4 is None
231
        and __arg5 is None
232
        and __arg6 is None
233
        and __arg7 is None
234
        and __arg8 is None
235
        and __arg9 is None
236
        and not __args
237
    ):
UNCOV
238
        return await _Concurrently(tuple(__arg0))
×
239

UNCOV
240
    if (
×
241
        isinstance(__arg0, Coroutine)
242
        and __arg1 is None
243
        and __arg2 is None
244
        and __arg3 is None
245
        and __arg4 is None
246
        and __arg5 is None
247
        and __arg6 is None
248
        and __arg7 is None
249
        and __arg8 is None
250
        and __arg9 is None
251
        and not __args
252
    ):
253
        return await _Concurrently((__arg0,))
×
254

UNCOV
255
    if (
×
256
        isinstance(__arg0, Coroutine)
257
        and isinstance(__arg1, Coroutine)
258
        and __arg2 is None
259
        and __arg3 is None
260
        and __arg4 is None
261
        and __arg5 is None
262
        and __arg6 is None
263
        and __arg7 is None
264
        and __arg8 is None
265
        and __arg9 is None
266
        and not __args
267
    ):
UNCOV
268
        return await _Concurrently((__arg0, __arg1))
×
269

270
    if (
×
271
        isinstance(__arg0, Coroutine)
272
        and isinstance(__arg1, Coroutine)
273
        and isinstance(__arg2, Coroutine)
274
        and __arg3 is None
275
        and __arg4 is None
276
        and __arg5 is None
277
        and __arg6 is None
278
        and __arg7 is None
279
        and __arg8 is None
280
        and __arg9 is None
281
        and not __args
282
    ):
283
        return await _Concurrently((__arg0, __arg1, __arg2))
×
284

285
    if (
×
286
        isinstance(__arg0, Coroutine)
287
        and isinstance(__arg1, Coroutine)
288
        and isinstance(__arg2, Coroutine)
289
        and isinstance(__arg3, Coroutine)
290
        and __arg4 is None
291
        and __arg5 is None
292
        and __arg6 is None
293
        and __arg7 is None
294
        and __arg8 is None
295
        and __arg9 is None
296
        and not __args
297
    ):
298
        return await _Concurrently((__arg0, __arg1, __arg2, __arg3))
×
299

300
    if (
×
301
        isinstance(__arg0, Coroutine)
302
        and isinstance(__arg1, Coroutine)
303
        and isinstance(__arg2, Coroutine)
304
        and isinstance(__arg3, Coroutine)
305
        and isinstance(__arg4, Coroutine)
306
        and __arg5 is None
307
        and __arg6 is None
308
        and __arg7 is None
309
        and __arg8 is None
310
        and __arg9 is None
311
        and not __args
312
    ):
313
        return await _Concurrently((__arg0, __arg1, __arg2, __arg3, __arg4))
×
314

315
    if (
×
316
        isinstance(__arg0, Coroutine)
317
        and isinstance(__arg1, Coroutine)
318
        and isinstance(__arg2, Coroutine)
319
        and isinstance(__arg3, Coroutine)
320
        and isinstance(__arg4, Coroutine)
321
        and isinstance(__arg5, Coroutine)
322
        and __arg6 is None
323
        and __arg7 is None
324
        and __arg8 is None
325
        and __arg9 is None
326
        and not __args
327
    ):
328
        return await _Concurrently((__arg0, __arg1, __arg2, __arg3, __arg4, __arg5))
×
329

330
    if (
×
331
        isinstance(__arg0, Coroutine)
332
        and isinstance(__arg1, Coroutine)
333
        and isinstance(__arg2, Coroutine)
334
        and isinstance(__arg3, Coroutine)
335
        and isinstance(__arg4, Coroutine)
336
        and isinstance(__arg5, Coroutine)
337
        and isinstance(__arg6, Coroutine)
338
        and __arg7 is None
339
        and __arg8 is None
340
        and __arg9 is None
341
        and not __args
342
    ):
343
        return await _Concurrently((__arg0, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6))
×
344

345
    if (
×
346
        isinstance(__arg0, Coroutine)
347
        and isinstance(__arg1, Coroutine)
348
        and isinstance(__arg2, Coroutine)
349
        and isinstance(__arg3, Coroutine)
350
        and isinstance(__arg4, Coroutine)
351
        and isinstance(__arg5, Coroutine)
352
        and isinstance(__arg6, Coroutine)
353
        and isinstance(__arg7, Coroutine)
354
        and __arg8 is None
355
        and __arg9 is None
356
        and not __args
357
    ):
358
        return await _Concurrently((__arg0, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7))
×
359

360
    if (
×
361
        isinstance(__arg0, Coroutine)
362
        and isinstance(__arg1, Coroutine)
363
        and isinstance(__arg2, Coroutine)
364
        and isinstance(__arg3, Coroutine)
365
        and isinstance(__arg4, Coroutine)
366
        and isinstance(__arg5, Coroutine)
367
        and isinstance(__arg6, Coroutine)
368
        and isinstance(__arg7, Coroutine)
369
        and isinstance(__arg8, Coroutine)
370
        and __arg9 is None
371
        and not __args
372
    ):
373
        return await _Concurrently(
×
374
            (__arg0, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7, __arg8)
375
        )
376

377
    if (
×
378
        isinstance(__arg0, Coroutine)
379
        and isinstance(__arg1, Coroutine)
380
        and isinstance(__arg2, Coroutine)
381
        and isinstance(__arg3, Coroutine)
382
        and isinstance(__arg4, Coroutine)
383
        and isinstance(__arg5, Coroutine)
384
        and isinstance(__arg6, Coroutine)
385
        and isinstance(__arg7, Coroutine)
386
        and isinstance(__arg8, Coroutine)
387
        and isinstance(__arg9, Coroutine)
388
        and all(isinstance(arg, Coroutine) for arg in __args)
389
    ):
390
        return await _Concurrently(
×
391
            (
392
                __arg0,
393
                __arg1,
394
                __arg2,
395
                __arg3,
396
                __arg4,
397
                __arg5,
398
                __arg6,
399
                __arg7,
400
                __arg8,
401
                __arg9,
402
                *__args,
403
            )
404
        )
405

406
    args = __arg0, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7, __arg8, __arg9, *__args
×
407

408
    def render_arg(arg: Any) -> str | None:
×
409
        if arg is None:
×
410
            return None
×
411
        return repr(arg)
×
412

413
    likely_args_explicitly_passed = tuple(
×
414
        reversed(
415
            [
416
                render_arg(arg)
417
                for arg in itertools.dropwhile(lambda arg: arg is None, reversed(args))
418
            ]
419
        )
420
    )
421
    if any(arg is None for arg in likely_args_explicitly_passed):
×
422
        raise ValueError(
×
423
            softwrap(
424
                f"""
425
                Unexpected concurrently() None arguments: {
426
                    ", ".join(map(str, likely_args_explicitly_passed))
427
                }
428

429
                When calling concurrently() on individual rule calls, all leading arguments must be
430
                awaitables.
431
                """
432
            )
433
        )
434

435
    raise TypeError(
×
436
        softwrap(
437
            f"""
438
            Unexpected concurrently() argument types: {", ".join(map(str, likely_args_explicitly_passed))}
439

440
            `concurrently` can be used in two ways:
441
              1. concurrently(Iterable[awaitable[T]]) -> Tuple[T]
442
              2. concurrently(awaitable[T1]], ...) -> Tuple[T1, T2, ...]
443

444
            The 1st form is intended for homogenous collections of rule calls and emulates an
445
            async `for ...` comprehension used to iterate over the collection in parallel and
446
            collect the results in a homogenous tuple when all are complete.
447

448
            The 2nd form supports executing heterogeneous rule calls in parallel and collecting
449
            them in a heterogeneous tuple when all are complete. Currently up to 10 heterogeneous
450
            rule calls can be passed while still tracking their output types for type-checking by
451
            MyPy and similar type checkers. If more than 10 rule calls are passed, type checking
452
            will enforce that they are homogeneous.
453
            """
454
        )
455
    )
456

457

458
concurrently = Concurrently
1✔
459

460

461
@dataclass(frozen=True)
1✔
462
class Params:
1✔
463
    """A set of values with distinct types.
464

465
    Distinct types are enforced at consumption time by the rust type of the same name.
466
    """
467

468
    params: tuple[Any, ...]
1✔
469

470
    def __init__(self, *args: Any) -> None:
1✔
471
        object.__setattr__(self, "params", tuple(args))
1✔
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