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

pantsbuild / pants / 18913303678

29 Oct 2025 03:29PM UTC coverage: 80.004% (-0.3%) from 80.283%
18913303678

push

github

web-flow
Updating 3rd party lockfiles for PBS script and MyPy (#22796)

Also small bumps to fastapi and starlette, which updated over the last few days. Starlette updated for features, and then a security vulnerability.

Fastapi bumped just for starlette.

88 of 93 new or added lines in 73 files covered. (94.62%)

221 existing lines in 15 files now uncovered.

77334 of 96663 relevant lines covered (80.0%)

5.2 hits per line

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

96.05
/src/python/pants/backend/shell/target_types.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
17✔
5

6
import re
17✔
7
from enum import Enum
17✔
8

9
from pants.backend.adhoc.target_types import (
17✔
10
    AdhocToolCacheScopeField,
11
    AdhocToolDependenciesField,
12
    AdhocToolExecutionDependenciesField,
13
    AdhocToolExtraEnvVarsField,
14
    AdhocToolLogOutputField,
15
    AdhocToolNamedCachesField,
16
    AdhocToolOutputDependenciesField,
17
    AdhocToolOutputDirectoriesField,
18
    AdhocToolOutputFilesField,
19
    AdhocToolOutputRootDirField,
20
    AdhocToolOutputsMatchMode,
21
    AdhocToolPathEnvModifyModeField,
22
    AdhocToolRunnableDependenciesField,
23
    AdhocToolTimeoutField,
24
    AdhocToolWorkdirField,
25
    AdhocToolWorkspaceInvalidationSourcesField,
26
)
27
from pants.backend.shell.subsystems.shell_setup import ShellSetup
17✔
28
from pants.core.environments.target_types import EnvironmentField
17✔
29
from pants.core.goals.package import OutputPathField
17✔
30
from pants.core.goals.test import RuntimePackageDependenciesField, TestTimeoutField
17✔
31
from pants.core.util_rules.system_binaries import BinaryPathTest
17✔
32
from pants.engine.rules import collect_rules, rule
17✔
33
from pants.engine.target import (
17✔
34
    COMMON_TARGET_FIELDS,
35
    BoolField,
36
    MultipleSourcesField,
37
    OverridesField,
38
    SingleSourceField,
39
    StringField,
40
    StringSequenceField,
41
    Target,
42
    TargetFilesGenerator,
43
    TargetFilesGeneratorSettings,
44
    TargetFilesGeneratorSettingsRequest,
45
    generate_file_based_overrides_field_help_message,
46
    generate_multiple_sources_field_help_message,
47
)
48
from pants.engine.unions import UnionRule
17✔
49
from pants.util.enums import match
17✔
50
from pants.util.strutil import help_text
17✔
51

52

53
class ShellDependenciesField(AdhocToolDependenciesField):
17✔
54
    pass
17✔
55

56

57
class ShellSourceField(SingleSourceField):
17✔
58
    # Normally, we would add `expected_file_extensions = ('.sh',)`, but Bash scripts don't need a
59
    # file extension, so we don't use this.
60
    uses_source_roots = False
17✔
61

62

63
class ShellGeneratingSourcesBase(MultipleSourcesField):
17✔
64
    uses_source_roots = False
17✔
65

66

67
class ShellGeneratorSettingsRequest(TargetFilesGeneratorSettingsRequest):
17✔
68
    pass
17✔
69

70

71
@rule
17✔
72
async def generator_settings(
17✔
73
    _: ShellGeneratorSettingsRequest,
74
    shell_setup: ShellSetup,
75
) -> TargetFilesGeneratorSettings:
76
    return TargetFilesGeneratorSettings(
×
77
        add_dependencies_on_all_siblings=not shell_setup.dependency_inference
78
    )
79

80

81
# -----------------------------------------------------------------------------------------------
82
# `shunit2_test` target
83
# -----------------------------------------------------------------------------------------------
84

85

86
class Shunit2Shell(Enum):
17✔
87
    sh = "sh"
17✔
88
    bash = "bash"
17✔
89
    dash = "dash"
17✔
90
    ksh = "ksh"
17✔
91
    pdksh = "pdksh"
17✔
92
    zsh = "zsh"
17✔
93

94
    @classmethod
17✔
95
    def parse_shebang(cls, shebang: bytes) -> Shunit2Shell | None:
17✔
96
        if not shebang:
1✔
97
            return None
1✔
98
        first_line = shebang.splitlines()[0]
1✔
99
        matches = re.match(rb"^#! *[/\w]*/(?P<program>\w+) *(?P<arg>\w*)", first_line)
1✔
100
        if not matches:
1✔
101
            return None
1✔
102
        program = matches.group("program")
1✔
103
        if program == b"env":
1✔
104
            program = matches.group("arg")
1✔
105
        try:
1✔
106
            return cls(program.decode())
1✔
107
        except ValueError:
×
108
            return None
×
109

110
    @property
17✔
111
    def binary_path_test(self) -> BinaryPathTest | None:
17✔
NEW
112
        arg = match(
×
113
            self,
114
            {
115
                self.sh: None,
116
                self.bash: "--version",
117
                self.dash: None,
118
                self.ksh: "--version",
119
                self.pdksh: None,
120
                self.zsh: "--version",
121
            },
122
        )
123
        if not arg:
×
124
            return None
×
125
        return BinaryPathTest((arg,))
×
126

127

128
class Shunit2TestDependenciesField(ShellDependenciesField):
17✔
129
    supports_transitive_excludes = True
17✔
130

131

132
class Shunit2TestTimeoutField(TestTimeoutField):
17✔
133
    pass
17✔
134

135

136
class SkipShunit2TestsField(BoolField):
17✔
137
    alias = "skip_tests"
17✔
138
    default = False
17✔
139
    help = "If true, don't run this target's tests."
17✔
140

141

142
class Shunit2TestSourceField(ShellSourceField):
17✔
143
    pass
17✔
144

145

146
class Shunit2ShellField(StringField):
17✔
147
    alias = "shell"
17✔
148
    valid_choices = Shunit2Shell
17✔
149
    help = "Which shell to run the tests with. If unspecified, Pants will look for a shebang line."
17✔
150

151

152
class Shunit2TestTarget(Target):
17✔
153
    alias = "shunit2_test"
17✔
154
    core_fields = (
17✔
155
        *COMMON_TARGET_FIELDS,
156
        Shunit2TestSourceField,
157
        Shunit2TestDependenciesField,
158
        Shunit2TestTimeoutField,
159
        SkipShunit2TestsField,
160
        Shunit2ShellField,
161
        RuntimePackageDependenciesField,
162
    )
163
    help = help_text(
17✔
164
        f"""
165
        A single test file for Bourne-based shell scripts using the shunit2 test framework.
166

167
        To use, add tests to your file per https://github.com/kward/shunit2/. Specify the shell
168
        to run with by either setting the field `{Shunit2ShellField.alias}` or including a
169
        shebang. To test the same file with multiple shells, create multiple `shunit2_tests`
170
        targets, one for each shell.
171

172
        Pants will automatically download the `shunit2` bash script and add
173
        `source ./shunit2` to your test for you. If you already have `source ./shunit2`,
174
        Pants will overwrite it to use the correct relative path.
175
        """
176
    )
177

178

179
# -----------------------------------------------------------------------------------------------
180
# `shunit2_tests` target generator
181
# -----------------------------------------------------------------------------------------------
182

183

184
class Shunit2TestsGeneratorSourcesField(ShellGeneratingSourcesBase):
17✔
185
    default = ("*_test.sh", "test_*.sh", "tests.sh")
17✔
186
    help = generate_multiple_sources_field_help_message(
17✔
187
        "Example: `sources=['test.sh', 'test_*.sh', '!test_ignore.sh']`"
188
    )
189

190

191
class Shunit2TestsOverrideField(OverridesField):
17✔
192
    help = generate_file_based_overrides_field_help_message(
17✔
193
        Shunit2TestTarget.alias,
194
        """
195
        overrides={
196
            "foo_test.sh": {"timeout": 120},
197
            "bar_test.sh": {"timeout": 200},
198
            ("foo_test.sh", "bar_test.sh"): {"tags": ["slow_tests"]},
199
        }
200
        """,
201
    )
202

203

204
class Shunit2TestsGeneratorTarget(TargetFilesGenerator):
17✔
205
    alias = "shunit2_tests"
17✔
206
    core_fields = (
17✔
207
        *COMMON_TARGET_FIELDS,
208
        Shunit2TestsGeneratorSourcesField,
209
        Shunit2TestsOverrideField,
210
    )
211
    generated_target_cls = Shunit2TestTarget
17✔
212
    copied_fields = COMMON_TARGET_FIELDS
17✔
213
    moved_fields = (
17✔
214
        Shunit2TestDependenciesField,
215
        Shunit2TestTimeoutField,
216
        SkipShunit2TestsField,
217
        Shunit2ShellField,
218
        RuntimePackageDependenciesField,
219
    )
220
    help = "Generate a `shunit2_test` target for each file in the `sources` field."
17✔
221

222

223
# -----------------------------------------------------------------------------------------------
224
# `shell_source` and `shell_sources` targets
225
# -----------------------------------------------------------------------------------------------
226

227

228
class ShellSourceTarget(Target):
17✔
229
    alias = "shell_source"
17✔
230
    core_fields = (*COMMON_TARGET_FIELDS, ShellDependenciesField, ShellSourceField)
17✔
231
    help = "A single Bourne-based shell script, e.g. a Bash script."
17✔
232

233

234
class ShellSourcesGeneratingSourcesField(ShellGeneratingSourcesBase):
17✔
235
    default = ("*.sh",) + tuple(f"!{pat}" for pat in Shunit2TestsGeneratorSourcesField.default)
17✔
236
    help = generate_multiple_sources_field_help_message(
17✔
237
        "Example: `sources=['example.sh', 'new_*.sh', '!old_ignore.sh']`"
238
    )
239

240

241
class ShellSourcesOverridesField(OverridesField):
17✔
242
    help = generate_file_based_overrides_field_help_message(
17✔
243
        ShellSourceTarget.alias,
244
        """
245
        overrides={
246
            "foo.sh": {"skip_shellcheck": True]},
247
            "bar.sh": {"skip_shfmt": True]},
248
            ("foo.sh", "bar.sh"): {"tags": ["linter_disabled"]},
249
        }
250
        """,
251
    )
252

253

254
class ShellSourcesGeneratorTarget(TargetFilesGenerator):
17✔
255
    alias = "shell_sources"
17✔
256
    core_fields = (
17✔
257
        *COMMON_TARGET_FIELDS,
258
        ShellSourcesGeneratingSourcesField,
259
        ShellSourcesOverridesField,
260
    )
261
    generated_target_cls = ShellSourceTarget
17✔
262
    copied_fields = COMMON_TARGET_FIELDS
17✔
263
    moved_fields = (ShellDependenciesField,)
17✔
264
    help = "Generate a `shell_source` target for each file in the `sources` field."
17✔
265

266

267
# -----------------------------------------------------------------------------------------------
268
# `shell_command` target
269
# -----------------------------------------------------------------------------------------------
270

271

272
class ShellCommandCommandField(StringField):
17✔
273
    alias = "command"
17✔
274
    required = True
17✔
275
    help = help_text(
17✔
276
        """
277
        Shell command to execute.
278

279
        The command is executed as `'bash -c <command>'` by default. If you want to invoke a binary
280
        use `exec -a $0 <binary> <args>` as the command so that the binary gets the correct `argv[0]`
281
        set.
282
        """
283
    )
284

285

286
class ShellCommandOutputFilesField(AdhocToolOutputFilesField):
17✔
287
    pass
17✔
288

289

290
class ShellCommandOutputDirectoriesField(AdhocToolOutputDirectoriesField):
17✔
291
    pass
17✔
292

293

294
class ShellCommandOutputDependenciesField(AdhocToolOutputDependenciesField):
17✔
295
    pass
17✔
296

297

298
class ShellCommandExecutionDependenciesField(AdhocToolExecutionDependenciesField):
17✔
299
    pass
17✔
300

301

302
class RunShellCommandExecutionDependenciesField(ShellCommandExecutionDependenciesField):
17✔
303
    help = help_text(
17✔
304
        lambda: f"""
305
        The execution dependencies for this command.
306

307
        Dependencies specified here are those required to make the command complete successfully
308
        (e.g. file inputs, packages compiled from other targets, etc), but NOT required to make
309
        the outputs of the command useful.
310

311
        See also `{RunShellCommandRunnableDependenciesField.alias}`.
312
        """
313
    )
314

315

316
class ShellCommandRunnableDependenciesField(AdhocToolRunnableDependenciesField):
17✔
317
    pass
17✔
318

319

320
class RunShellCommandRunnableDependenciesField(ShellCommandRunnableDependenciesField):
17✔
321
    help = help_text(
17✔
322
        lambda: f"""
323
        The runnable dependencies for this command.
324

325
        Dependencies specified here are those required to exist on the `PATH` to make the command
326
        complete successfully (interpreters specified in a `#!` command, etc). Note that these
327
        dependencies will be made available on the `PATH` with the name of the target.
328

329
        See also `{RunShellCommandExecutionDependenciesField.alias}`.
330
        """
331
    )
332

333

334
class ShellCommandSourcesField(MultipleSourcesField):
17✔
335
    # We solely register this field for codegen to work.
336
    alias = "_sources"
17✔
337
    uses_source_roots = False
17✔
338
    expected_num_files = 0
17✔
339

340

341
class ShellCommandTimeoutField(AdhocToolTimeoutField):
17✔
342
    pass
17✔
343

344

345
class ShellCommandToolsField(StringSequenceField):
17✔
346
    alias = "tools"
17✔
347
    default = ()
17✔
348
    help = help_text(
17✔
349
        """
350
        Specify required executable tools that might be used.
351

352
        Only the tools explicitly provided will be available on the search PATH,
353
        and these tools must be found on the paths provided by
354
        `[shell-setup].executable_search_paths` (which defaults to the system PATH).
355
        """
356
    )
357

358

359
class ShellCommandExtraEnvVarsField(AdhocToolExtraEnvVarsField):
17✔
360
    pass
17✔
361

362

363
class ShellCommandLogOutputField(AdhocToolLogOutputField):
17✔
364
    pass
17✔
365

366

367
class ShellCommandWorkdirField(AdhocToolWorkdirField):
17✔
368
    pass
17✔
369

370

371
class RunShellCommandWorkdirField(AdhocToolWorkdirField):
17✔
372
    pass
17✔
373

374

375
class ShellCommandOutputRootDirField(AdhocToolOutputRootDirField):
17✔
376
    pass
17✔
377

378

379
class ShellCommandTestDependenciesField(ShellCommandExecutionDependenciesField):
17✔
380
    pass
17✔
381

382

383
class ShellCommandNamedCachesField(AdhocToolNamedCachesField):
17✔
384
    pass
17✔
385

386

387
class ShellCommandWorkspaceInvalidationSourcesField(AdhocToolWorkspaceInvalidationSourcesField):
17✔
388
    pass
17✔
389

390

391
class ShellCommandPathEnvModifyModeField(AdhocToolPathEnvModifyModeField):
17✔
392
    pass
17✔
393

394

395
class ShellCommandOutputsMatchMode(AdhocToolOutputsMatchMode):
17✔
396
    pass
17✔
397

398

399
class ShellCommandCacheScopeField(AdhocToolCacheScopeField):
17✔
400
    pass
17✔
401

402

403
class SkipShellCommandTestsField(BoolField):
17✔
404
    alias = "skip_tests"
17✔
405
    default = False
17✔
406
    help = "If true, don't run this tests for target."
17✔
407

408

409
class SkipShellCommandPackageField(BoolField):
17✔
410
    alias = "skip_package"
17✔
411
    default = False
17✔
412
    help = "If true, don't run this package for target."
17✔
413

414

415
class ShellCommandTarget(Target):
17✔
416
    alias = "shell_command"
17✔
417
    core_fields = (
17✔
418
        *COMMON_TARGET_FIELDS,
419
        ShellCommandOutputDependenciesField,
420
        ShellCommandExecutionDependenciesField,
421
        ShellCommandRunnableDependenciesField,
422
        ShellCommandCommandField,
423
        ShellCommandLogOutputField,
424
        ShellCommandOutputFilesField,
425
        ShellCommandOutputDirectoriesField,
426
        ShellCommandSourcesField,
427
        ShellCommandTimeoutField,
428
        ShellCommandToolsField,
429
        ShellCommandExtraEnvVarsField,
430
        ShellCommandWorkdirField,
431
        ShellCommandNamedCachesField,
432
        ShellCommandOutputRootDirField,
433
        ShellCommandWorkspaceInvalidationSourcesField,
434
        ShellCommandPathEnvModifyModeField,
435
        ShellCommandOutputsMatchMode,
436
        ShellCommandCacheScopeField,
437
        EnvironmentField,
438
    )
439
    help = help_text(
17✔
440
        """
441
        Execute any external tool for its side effects.
442

443
        Example BUILD file:
444

445
            shell_command(
446
                command="./my-script.sh --flag",
447
                tools=["tar", "curl", "cat", "bash", "env"],
448
                execution_dependencies=[":scripts"],
449
                output_files=["logs/my-script.log"],
450
                output_directories=["results"],
451
            )
452

453
            shell_sources(name="scripts")
454

455
        Remember to add this target to the dependencies of each consumer, such as your
456
        `python_tests` or `docker_image`. When relevant, Pants will run your `command` and
457
        insert the `outputs` into that consumer's context.
458

459
        The command may be retried and/or cancelled, so ensure that it is idempotent.
460
        """
461
    )
462

463

464
class ShellCommandRunTarget(Target):
17✔
465
    alias = "run_shell_command"
17✔
466
    core_fields = (
17✔
467
        *COMMON_TARGET_FIELDS,
468
        RunShellCommandExecutionDependenciesField,
469
        RunShellCommandRunnableDependenciesField,
470
        ShellCommandCommandField,
471
        RunShellCommandWorkdirField,
472
    )
473
    help = help_text(
17✔
474
        """
475
        Run a script in the workspace, with all dependencies packaged/copied into a chroot.
476

477
        Example BUILD file:
478

479
            run_shell_command(
480
                command="./scripts/my-script.sh --data-files-dir={chroot}",
481
                execution_dependencies=["src/project/files:data"],
482
            )
483

484
        The `command` may use either `{chroot}` on the command line, or the `$CHROOT`
485
        environment variable to get the root directory for where any dependencies are located.
486

487
        In contrast to the `shell_command`, in addition to `workdir` you only have
488
        the `command` and `execution_dependencies` fields as the `tools` you are going to use are
489
        already on the PATH which is inherited from the Pants environment. Also, the `outputs` does
490
        not apply, as any output files produced will end up directly in your project tree.
491
        """
492
    )
493

494

495
class ShellCommandTestTarget(Target):
17✔
496
    alias = "test_shell_command"
17✔
497

498
    core_fields = (
17✔
499
        *COMMON_TARGET_FIELDS,
500
        ShellCommandTestDependenciesField,
501
        ShellCommandRunnableDependenciesField,
502
        ShellCommandCommandField,
503
        ShellCommandLogOutputField,
504
        ShellCommandSourcesField,
505
        ShellCommandTimeoutField,
506
        ShellCommandToolsField,
507
        ShellCommandExtraEnvVarsField,
508
        ShellCommandPathEnvModifyModeField,
509
        EnvironmentField,
510
        SkipShellCommandTestsField,
511
        ShellCommandWorkdirField,
512
        ShellCommandOutputFilesField,
513
        ShellCommandOutputDirectoriesField,
514
        ShellCommandOutputRootDirField,
515
        ShellCommandOutputsMatchMode,
516
        ShellCommandCacheScopeField,
517
    )
518
    help = help_text(
17✔
519
        """
520
        Run a script as a test via the `test` goal, with all dependencies packaged/copied available in the chroot.
521

522
        Example BUILD file:
523

524
            test_shell_command(
525
                name="test",
526
                tools=["test"],
527
                command="test -r $CHROOT/some-data-file.txt",
528
                execution_dependencies=["src/project/files:data"],
529
            )
530

531
        The `command` may use the `{chroot}` marker on the command line or in environment variables
532
        to get the root directory where any dependencies are materialized during execution.
533

534
        In contrast to the `run_shell_command`, this target is intended to run shell commands as tests
535
        and will only run them via the `test` goal.
536
        """
537
    )
538

539

540
class ShellCommandPackageDependenciesField(ShellCommandExecutionDependenciesField):
17✔
541
    pass
17✔
542

543

544
class ShellCommandPackageTarget(Target):
17✔
545
    alias = "package_shell_command"
17✔
546

547
    core_fields = (
17✔
548
        *COMMON_TARGET_FIELDS,
549
        ShellCommandPackageDependenciesField,
550
        ShellCommandRunnableDependenciesField,
551
        ShellCommandCommandField,
552
        ShellCommandLogOutputField,
553
        ShellCommandSourcesField,
554
        ShellCommandTimeoutField,
555
        ShellCommandToolsField,
556
        ShellCommandExtraEnvVarsField,
557
        ShellCommandPathEnvModifyModeField,
558
        ShellCommandNamedCachesField,
559
        ShellCommandWorkspaceInvalidationSourcesField,
560
        ShellCommandCacheScopeField,
561
        EnvironmentField,
562
        SkipShellCommandPackageField,
563
        ShellCommandWorkdirField,
564
        ShellCommandOutputFilesField,
565
        ShellCommandOutputDirectoriesField,
566
        ShellCommandOutputRootDirField,
567
        ShellCommandOutputsMatchMode,
568
        ShellCommandOutputDependenciesField,
569
        OutputPathField,
570
    )
571

572
    help = help_text(
17✔
573
        """
574
        Run a script to produce distributable outputs via the `package` goal.
575

576
        Example BUILD file:
577

578
            package_shell_command(
579
                name="build-rust-app",
580
                tools=["cargo"],
581
                command="cargo build --release",
582
                output_files=["target/release/binary"],
583
            )
584

585
        The `command` may use the `{chroot}` marker on the command line or in environment variables
586
        to get the root directory where any dependencies are materialized during execution.
587

588
        The outputs specified via `output_files` and `output_directories` will be captured and
589
        made available for other Pants targets to depend on. They will also be copied to the path
590
        specified via the `output_path` field (relative to the dist directory) when running
591
        `pants package`.
592

593
        This target is experimental and its behavior may change in future versions.
594
        """
595
    )
596

597

598
def rules():
17✔
599
    return [
11✔
600
        *collect_rules(),
601
        UnionRule(TargetFilesGeneratorSettingsRequest, ShellGeneratorSettingsRequest),
602
    ]
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

© 2025 Coveralls, Inc