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

pantsbuild / pants / 19605163397

23 Nov 2025 03:21AM UTC coverage: 80.249% (-0.04%) from 80.284%
19605163397

Pull #22911

github

web-flow
Merge 94c33cc2d into 5244eeb7a
Pull Request #22911: Replaced isort with ruff lint

13 of 16 new or added lines in 13 files covered. (81.25%)

32 existing lines in 1 file now uncovered.

78348 of 97631 relevant lines covered (80.25%)

3.09 hits per line

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

82.99
/src/python/pants/backend/python/goals/package_pex_binary_integration_test.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
1✔
5

6
import json
1✔
7
import os.path
1✔
8
import pkgutil
1✔
9
import subprocess
1✔
10
from dataclasses import dataclass
1✔
11
from textwrap import dedent
1✔
12

13
import pytest
1✔
14

15
from pants.backend.python import target_types_rules
1✔
16
from pants.backend.python.goals import package_dists, package_pex_binary
1✔
17
from pants.backend.python.goals.package_pex_binary import (
1✔
18
    PexBinaryFieldSet,
19
    PexFromTargetsRequestForBuiltPackage,
20
)
21
from pants.backend.python.macros.python_artifact import PythonArtifact
1✔
22
from pants.backend.python.providers.python_build_standalone import rules as pbs
1✔
23
from pants.backend.python.target_types import (
1✔
24
    PexBinary,
25
    PexLayout,
26
    PythonDistribution,
27
    PythonRequirementTarget,
28
    PythonSourcesGeneratorTarget,
29
)
30
from pants.backend.python.util_rules import pex_from_targets
1✔
31
from pants.build_graph.address import Address
1✔
32
from pants.core.goals.package import BuiltPackage
1✔
33
from pants.core.target_types import (
1✔
34
    FilesGeneratorTarget,
35
    FileTarget,
36
    RelocatedFiles,
37
    ResourcesGeneratorTarget,
38
)
39
from pants.core.target_types import rules as core_target_types_rules
1✔
40
from pants.engine.internals.scheduler import ExecutionError
1✔
41
from pants.testutil.python_interpreter_selection import skip_unless_python38_present
1✔
42
from pants.testutil.python_rule_runner import PythonRuleRunner
1✔
43
from pants.testutil.rule_runner import QueryRule
1✔
44
from pants.testutil.skip_utils import skip_if_linux_arm64
1✔
45

46

47
@pytest.fixture
1✔
48
def rule_runner() -> PythonRuleRunner:
1✔
49
    rule_runner = PythonRuleRunner(
1✔
50
        rules=[
51
            *package_pex_binary.rules(),
52
            *pex_from_targets.rules(),
53
            *target_types_rules.rules(),
54
            *core_target_types_rules(),
55
            *package_dists.rules(),
56
            QueryRule(BuiltPackage, [PexBinaryFieldSet]),
57
            QueryRule(PexFromTargetsRequestForBuiltPackage, [PexBinaryFieldSet]),
58
        ],
59
        target_types=[
60
            FileTarget,
61
            FilesGeneratorTarget,
62
            PexBinary,
63
            PythonDistribution,
64
            PythonRequirementTarget,
65
            PythonSourcesGeneratorTarget,
66
            RelocatedFiles,
67
            ResourcesGeneratorTarget,
68
        ],
69
        objects={"python_artifact": PythonArtifact},
70
    )
71
    rule_runner.set_options([], env_inherit={"PATH", "PYENV_ROOT", "HOME"})
1✔
72
    return rule_runner
1✔
73

74

75
def test_warn_files_targets(rule_runner: PythonRuleRunner, caplog) -> None:
1✔
76
    rule_runner.write_files(
1✔
77
        {
78
            "assets/f.txt": "",
79
            "assets/BUILD": dedent(
80
                """\
81
                files(name='files', sources=['f.txt'])
82
                relocated_files(
83
                    name='relocated',
84
                    files_targets=[':files'],
85
                    src='assets',
86
                    dest='new_assets',
87
                )
88

89
                # Resources are fine.
90
                resources(name='resources', sources=['f.txt'])
91
                """
92
            ),
93
            "src/py/project/__init__.py": "",
94
            "src/py/project/app.py": "print('hello')",
95
            "src/py/project/BUILD": dedent(
96
                """\
97
                pex_binary(
98
                    dependencies=['assets:files', 'assets:relocated', 'assets:resources'],
99
                    entry_point="none",
100
                )
101
                """
102
            ),
103
        }
104
    )
105
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
106
    field_set = PexBinaryFieldSet.create(tgt)
1✔
107

108
    assert not caplog.records
1✔
109
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
110
    assert caplog.records
1✔
111
    assert f"The target {tgt.address} (`pex_binary`) transitively depends on" in caplog.text
1✔
112
    assert "assets/f.txt:files" in caplog.text
1✔
113
    assert "assets:relocated" in caplog.text
1✔
114
    assert "assets:resources" not in caplog.text
1✔
115

116
    assert len(result.artifacts) == 1
1✔
117
    assert result.artifacts[0].relpath == "src.py.project/project.pex"
1✔
118

119

120
def test_include_sources_avoids_files_targets_warning(
1✔
121
    rule_runner: PythonRuleRunner, caplog
122
) -> None:
123
    rule_runner.write_files(
1✔
124
        {
125
            "assets/f.txt": "",
126
            "assets/BUILD": dedent(
127
                """\
128
                files(name='files', sources=['f.txt'])
129
                relocated_files(
130
                    name='relocated',
131
                    files_targets=[':files'],
132
                    src='assets',
133
                    dest='new_assets',
134
                )
135
                """
136
            ),
137
            "src/py/project/__init__.py": "",
138
            "src/py/project/app.py": "print('hello')",
139
            "src/py/project/BUILD": dedent(
140
                """\
141
                python_sources(
142
                    name='sources',
143
                    interpreter_constraints=["CPython==3.10.*"]
144
                )
145

146
                python_distribution(
147
                    name='wheel',
148
                    dependencies=[
149
                        ':sources',
150
                        'assets:relocated',
151
                    ],
152
                    provides=python_artifact(
153
                        name='my-dist',
154
                        version='1.2.3',
155
                    ),
156
                    interpreter_constraints=["CPython==3.10.*"]
157
                )
158

159
                pex_binary(
160
                    dependencies=[':wheel'],
161
                    entry_point="none",
162
                    include_sources=False,
163
                    interpreter_constraints=["CPython==3.10.*"]
164
                )
165
                """
166
            ),
167
        }
168
    )
169
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
170
    field_set = PexBinaryFieldSet.create(tgt)
1✔
171

172
    assert not caplog.records
1✔
173
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
174
    assert not caplog.records
1✔
175

176
    assert len(result.artifacts) == 1
1✔
177
    assert result.artifacts[0].relpath == "src.py.project/project.pex"
1✔
178

179

180
@pytest.mark.parametrize(
1✔
181
    "layout",
182
    [pytest.param(layout, id=layout.value) for layout in PexLayout],
183
)
184
def test_layout(rule_runner: PythonRuleRunner, layout: PexLayout) -> None:
1✔
185
    rule_runner.write_files(
1✔
186
        {
187
            "src/py/project/app.py": dedent(
188
                """\
189
                import os
190
                import sys
191
                for env in ["FOO", "--inject-arg", "quotes '"]:
192
                    print(f"{env}={os.environ.get(env)}")
193
                print(f"ARGV={sys.argv[1:]}")
194
                """
195
            ),
196
            "src/py/project/BUILD": dedent(
197
                f"""\
198
                python_sources(name="lib")
199
                pex_binary(
200
                    entry_point="app.py",
201
                    args=['123', 'abc', '--inject-env', "quotes 'n spaces"],
202
                    env={{'FOO': 'xxx', '--inject-arg': 'yyy', "quotes '": 'n spaces'}},
203
                    layout="{layout.value}",
204
                )
205
                """
206
            ),
207
        }
208
    )
209
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
210
    field_set = PexBinaryFieldSet.create(tgt)
1✔
211
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
212
    assert len(result.artifacts) == 1
1✔
213
    expected_pex_relpath = "src.py.project/project.pex"
1✔
214
    assert expected_pex_relpath == result.artifacts[0].relpath
1✔
215

216
    rule_runner.write_digest(result.digest)
1✔
217
    executable = os.path.join(
1✔
218
        rule_runner.build_root,
219
        (
220
            expected_pex_relpath
221
            if PexLayout.ZIPAPP is layout
222
            else os.path.join(expected_pex_relpath, "__main__.py")
223
        ),
224
    )
225
    stdout = dedent(
1✔
226
        """\
227
        FOO=xxx
228
        --inject-arg=yyy
229
        quotes '=n spaces
230
        ARGV=['123', 'abc', '--inject-env', "quotes 'n spaces"]
231
        """
232
    ).encode()
233
    assert stdout == subprocess.run([executable], check=True, stdout=subprocess.PIPE).stdout
1✔
234

235

236
@pytest.fixture
1✔
237
def pex_executable(rule_runner: PythonRuleRunner) -> str:
1✔
238
    rule_runner.write_files(
×
239
        {
240
            "pex_exe/BUILD": dedent(
241
                """\
242
                python_requirement(name="req", requirements=["pex==2.1.112"])
243
                pex_binary(dependencies=[":req"], script="pex")
244
                """
245
            ),
246
        }
247
    )
248
    tgt = rule_runner.get_target(Address("pex_exe"))
×
249
    field_set = PexBinaryFieldSet.create(tgt)
×
250
    result = rule_runner.request(BuiltPackage, [field_set])
×
251
    assert len(result.artifacts) == 1
×
252
    expected_pex_relpath = "pex_exe/pex_exe.pex"
×
253
    assert expected_pex_relpath == result.artifacts[0].relpath
×
254
    rule_runner.write_digest(result.digest)
×
255
    return os.path.join(rule_runner.build_root, expected_pex_relpath)
×
256

257

258
@skip_if_linux_arm64
1✔
259
@skip_unless_python38_present
1✔
260
@pytest.mark.parametrize("target_type", ["files", "resources"])
1✔
261
def test_complete_platforms(rule_runner: PythonRuleRunner, target_type: str) -> None:
1✔
262
    linux_complete_platform = pkgutil.get_data(__name__, "platform-linux-py38.json")
1✔
263
    assert linux_complete_platform is not None
1✔
264

265
    mac_complete_platform = pkgutil.get_data(__name__, "platform-mac-py38.json")
1✔
266
    assert mac_complete_platform is not None
1✔
267

268
    rule_runner.write_files(
1✔
269
        {
270
            "src/py/project/platform-linux-py38.json": linux_complete_platform,
271
            "src/py/project/platform-mac-py38.json": mac_complete_platform,
272
            "src/py/project/BUILD": dedent(
273
                f"""\
274
                python_requirement(name="p537", requirements=["p537==1.0.6"])
275
                {target_type}(name="platforms", sources=["platform*.json"])
276
                pex_binary(
277
                    dependencies=[":p537"],
278
                    complete_platforms=[":platforms"],
279
                    include_tools=True,
280
                )
281
                """
282
            ),
283
        }
284
    )
285
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
286
    field_set = PexBinaryFieldSet.create(tgt)
1✔
287
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
288
    assert len(result.artifacts) == 1
1✔
289
    expected_pex_relpath = "src.py.project/project.pex"
1✔
290
    assert expected_pex_relpath == result.artifacts[0].relpath
1✔
291

292
    rule_runner.write_digest(result.digest)
1✔
293
    executable = os.path.join(rule_runner.build_root, expected_pex_relpath)
1✔
294
    pex_info = json.loads(
1✔
295
        subprocess.run(
296
            args=[executable, "info"],
297
            env=dict(PEX_TOOLS="1", **os.environ),
298
            stdout=subprocess.PIPE,
299
            check=True,
300
        ).stdout
301
    )
302
    assert sorted(
1✔
303
        [
304
            "p537-1.0.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl",
305
            "p537-1.0.6-cp38-cp38-macosx_10_15_x86_64.whl",
306
        ]
307
    ) == sorted(pex_info["distributions"])
308

309

310
def test_non_hermetic_venv_scripts(rule_runner: PythonRuleRunner) -> None:
1✔
311
    rule_runner.write_files(
1✔
312
        {
313
            "src/py/project/app.py": dedent(
314
                """\
315
                import json
316
                import os
317
                import sys
318

319

320
                json.dump(
321
                    {
322
                        "PYTHONPATH": os.environ.get("PYTHONPATH"),
323
                        "sys.path": sys.path,
324
                    },
325
                    sys.stdout,
326
                )
327
                """
328
            ),
329
            "src/py/project/BUILD": dedent(
330
                """\
331
                python_sources(name="app")
332

333
                pex_binary(
334
                    name="hermetic",
335
                    entry_point="app.py",
336
                    execution_mode="venv",
337
                    venv_hermetic_scripts=True,
338
                    dependencies=[
339
                        ":app",
340
                    ],
341
                )
342

343
                pex_binary(
344
                    name="non-hermetic",
345
                    entry_point="app.py",
346
                    execution_mode="venv",
347
                    venv_hermetic_scripts=False,
348
                    dependencies=[
349
                        ":app",
350
                    ],
351
                )
352
                """
353
            ),
354
        }
355
    )
356

357
    @dataclass(frozen=True)
1✔
358
    class Results:
1✔
359
        pythonpath: str | None
1✔
360
        sys_path: list[str]
1✔
361

362
    def execute_pex(address: Address, **extra_env) -> Results:
1✔
363
        tgt = rule_runner.get_target(address)
1✔
364
        field_set = PexBinaryFieldSet.create(tgt)
1✔
365
        result = rule_runner.request(BuiltPackage, [field_set])
1✔
366
        assert len(result.artifacts) == 1
1✔
367
        rule_runner.write_digest(result.digest)
1✔
368
        relpath = result.artifacts[0].relpath
1✔
369
        assert relpath is not None
1✔
370
        pex = os.path.join(rule_runner.build_root, relpath)
1✔
371
        assert os.path.isfile(pex)
1✔
372
        process = subprocess.run(
1✔
373
            args=[pex],
374
            env={**os.environ, **extra_env},
375
            cwd=rule_runner.build_root,
376
            stdout=subprocess.PIPE,
377
            check=True,
378
        )
379
        data = json.loads(process.stdout)
1✔
380
        return Results(pythonpath=data["PYTHONPATH"], sys_path=data["sys.path"])
1✔
381

382
    bob_sys_path_entry = os.path.join(rule_runner.build_root, "bob")
1✔
383

384
    hermetic_results = execute_pex(
1✔
385
        Address("src/py/project", target_name="hermetic"), PYTHONPATH="bob"
386
    )
387
    assert "bob" == hermetic_results.pythonpath
1✔
388
    assert bob_sys_path_entry not in hermetic_results.sys_path
1✔
389

390
    non_hermetic_results = execute_pex(
1✔
391
        Address("src/py/project", target_name="non-hermetic"), PYTHONPATH="bob"
392
    )
393
    assert "bob" == non_hermetic_results.pythonpath
1✔
394
    assert bob_sys_path_entry in non_hermetic_results.sys_path
1✔
395

396

397
def test_sh_boot_plumb(rule_runner: PythonRuleRunner) -> None:
1✔
398
    rule_runner.write_files(
1✔
399
        {
400
            "src/py/project/app.py": dedent(
401
                """\
402
                print("hello")
403
                """
404
            ),
405
            "src/py/project/BUILD": dedent(
406
                """\
407
                python_sources(name="lib")
408
                pex_binary(
409
                    entry_point="app.py",
410
                    sh_boot=True
411
                )
412
                """
413
            ),
414
        }
415
    )
416
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
417
    field_set = PexBinaryFieldSet.create(tgt)
1✔
418
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
419
    assert len(result.artifacts) == 1
1✔
420
    expected_pex_relpath = "src.py.project/project.pex"
1✔
421
    assert expected_pex_relpath == result.artifacts[0].relpath
1✔
422

423
    rule_runner.write_digest(result.digest)
1✔
424

425
    executable = os.path.join(rule_runner.build_root, expected_pex_relpath)
1✔
426
    with open(executable, "rb") as f:
1✔
427
        shebang = f.readline().decode()
1✔
428
        assert "#!/bin/sh" in shebang
1✔
429

430

431
def test_extra_build_args(rule_runner: PythonRuleRunner) -> None:
1✔
432
    rule_runner.write_files(
1✔
433
        {
434
            "src/py/project/app.py": dedent(
435
                """\
436
                print("hello")
437
                """
438
            ),
439
            "src/py/project/BUILD": dedent(
440
                """\
441
                python_sources(name="lib")
442
                pex_binary(
443
                    entry_point="app.py",
444
                    extra_build_args=["--example-extra-arg", "value-goes-here"]
445
                )
446
                """
447
            ),
448
        }
449
    )
450

451
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
452
    field_set = PexBinaryFieldSet.create(tgt)
1✔
453
    result = rule_runner.request(PexFromTargetsRequestForBuiltPackage, [field_set])
1✔
454

455
    additional_args = result.request.additional_args
1✔
456

457
    assert additional_args[-2] == "--example-extra-arg"
1✔
458
    assert additional_args[-1] == "value-goes-here"
1✔
459

460

461
def test_package_with_python_provider() -> None:
1✔
462
    # Per https://github.com/pantsbuild/pants/issues/21048, test packaging a local/unconstrained pex
463
    # binary when using a Python that isn't automatically visible on $PATH (using the PBS provider
464
    # as just one way to get such a Python)
465

466
    rule_runner = PythonRuleRunner(
1✔
467
        rules=[
468
            *package_pex_binary.rules(),
469
            *pex_from_targets.rules(),
470
            *target_types_rules.rules(),
471
            *core_target_types_rules(),
472
            *pbs.rules(),
473
            QueryRule(BuiltPackage, [PexBinaryFieldSet]),
474
        ],
475
        target_types=[
476
            PexBinary,
477
            PythonSourcesGeneratorTarget,
478
        ],
479
    )
480

481
    rule_runner.write_files(
1✔
482
        {
483
            "app.py": "",
484
            "BUILD": dedent(
485
                """\
486
                python_sources(name="src")
487
                pex_binary(name="target", entry_point="./app.py")
488
                """
489
            ),
490
        }
491
    )
492

493
    tgt = rule_runner.get_target(Address("", target_name="target"))
1✔
494
    field_set = PexBinaryFieldSet.create(tgt)
1✔
495

496
    # a random (https://xkcd.com/221/) old version of Python, that seems unlikely to be installed on
497
    # most systems, by default... but also, we don't propagate PATH (etc.) for this rule_runner, so
498
    # the test shouldn't be able to find system interpreters anyway.
499
    #
500
    # (Thus we have two layers of "assurance" the test is doing what is intended.)
501
    rule_runner.set_options(["--python-interpreter-constraints=CPython==3.10.2"])
1✔
502

503
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
504
    assert len(result.artifacts) == 1
1✔
505
    assert result.artifacts[0].relpath == "target.pex"
1✔
506

507

508
def test_scie_defaults(rule_runner: PythonRuleRunner) -> None:
1✔
509
    rule_runner.write_files(
1✔
510
        {
511
            "src/py/project/app.py": dedent(
512
                """\
513
                print("hello")
514
                """
515
            ),
516
            "src/py/project/BUILD": dedent(
517
                """\
518
                python_sources(name="lib")
519
                pex_binary(
520
                    entry_point="app.py",
521
                    scie="lazy"
522
                )
523
                """
524
            ),
525
        }
526
    )
527
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
528
    field_set = PexBinaryFieldSet.create(tgt)
1✔
529
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
UNCOV
530
    assert len(result.artifacts) == 2
×
UNCOV
531
    expected_pex_relpath = "src.py.project/project.pex"
×
UNCOV
532
    assert expected_pex_relpath == result.artifacts[0].relpath
×
UNCOV
533
    expected_scie_relpath = "src.py.project/project"
×
UNCOV
534
    assert expected_scie_relpath == result.artifacts[1].relpath
×
535

536

537
@pytest.mark.parametrize("scie_hash_alg", ["md5", "sha1", "sha256", "sha384", "sha512"])
1✔
538
def test_scie_hash_present(rule_runner: PythonRuleRunner, scie_hash_alg: str) -> None:
1✔
539
    rule_runner.write_files(
1✔
540
        {
541
            "src/py/project/app.py": dedent(
542
                """\
543
                print("hello")
544
                """
545
            ),
546
            "src/py/project/BUILD": dedent(
547
                f"""\
548
                python_sources(name="lib")
549
                pex_binary(
550
                    entry_point="app.py",
551
                    scie="lazy",
552
                    scie_hash_alg="{scie_hash_alg}"
553
                )
554
                """
555
            ),
556
        }
557
    )
558
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
559
    field_set = PexBinaryFieldSet.create(tgt)
1✔
560
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
UNCOV
561
    assert len(result.artifacts) == 3
×
UNCOV
562
    expected_pex_relpath = "src.py.project/project.pex"
×
UNCOV
563
    assert expected_pex_relpath == result.artifacts[0].relpath
×
UNCOV
564
    expected_scie_relpath = "src.py.project/project"
×
UNCOV
565
    assert expected_scie_relpath == result.artifacts[1].relpath
×
UNCOV
566
    expected_scie_hash_relpath = f"src.py.project/project.{scie_hash_alg}"
×
UNCOV
567
    assert expected_scie_hash_relpath == result.artifacts[2].relpath
×
568

569

570
def test_scie_platform_file_suffix(rule_runner: PythonRuleRunner) -> None:
1✔
571
    rule_runner.write_files(
1✔
572
        {
573
            "src/py/project/app.py": dedent(
574
                """\
575
                print("hello")
576
                """
577
            ),
578
            "src/py/project/BUILD": dedent(
579
                """\
580
                python_sources(name="lib")
581
                pex_binary(
582
                    entry_point="app.py",
583
                    scie="lazy",
584
                    scie_name_style="platform-file-suffix",
585
                    scie_platform=["linux-aarch64", "linux-x86_64"],
586
                )
587
                """
588
            ),
589
        }
590
    )
591
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
592
    field_set = PexBinaryFieldSet.create(tgt)
1✔
593
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
UNCOV
594
    assert len(result.artifacts) == 3
×
UNCOV
595
    expected_relpaths = {
×
596
        "src.py.project/project.pex",
597
        "src.py.project/project-linux-aarch64",
598
        "src.py.project/project-linux-x86_64",
599
    }
UNCOV
600
    relpaths = {artifact.relpath for artifact in result.artifacts}
×
UNCOV
601
    assert expected_relpaths == relpaths
×
602

603

604
def test_scie_platform_parent_dir(rule_runner: PythonRuleRunner) -> None:
1✔
605
    rule_runner.write_files(
1✔
606
        {
607
            "src/py/project/app.py": dedent(
608
                """\
609
                print("hello")
610
                """
611
            ),
612
            "src/py/project/BUILD": dedent(
613
                """\
614
                python_sources(name="lib")
615
                pex_binary(
616
                    entry_point="app.py",
617
                    scie="lazy",
618
                    scie_name_style="platform-parent-dir",
619
                    scie_platform=["linux-aarch64", "linux-x86_64"],
620
                )
621
                """
622
            ),
623
        }
624
    )
625
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
626
    field_set = PexBinaryFieldSet.create(tgt)
1✔
627
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
UNCOV
628
    assert len(result.artifacts) == 3
×
UNCOV
629
    expected_pex_relpath = "src.py.project/project.pex"
×
UNCOV
630
    assert expected_pex_relpath == result.artifacts[0].relpath
×
631
    # The result is to directories, materialize to look inside and make sure
632
    # the right files are there
UNCOV
633
    rule_runner.write_digest(result.digest)
×
UNCOV
634
    os.path.exists(os.path.join(rule_runner.build_root, "src.py.project/linux-aarch64/project"))
×
UNCOV
635
    os.path.exists(os.path.join(rule_runner.build_root, "src.py.project/linux-x86_64/project"))
×
636

637

638
@pytest.mark.parametrize(
1✔
639
    "passthrough",
640
    [
641
        "",
642
        "scie_busybox_pex_entrypoint_env_passthrough=True,",
643
        "scie_busybox_pex_entrypoint_env_passthrough=False,",
644
    ],
645
)
646
def test_scie_busybox_moo(rule_runner: PythonRuleRunner, passthrough: str) -> None:
1✔
647
    rule_runner.write_files(
1✔
648
        {
649
            "src/py/project/app.py": dedent(
650
                """\
651
                print("hello")
652
                """
653
            ),
654
            "src/py/project/BUILD": dedent(
655
                f"""\
656
                python_sources(name="lib")
657
                python_requirement(name="cowsay", requirements=["cowsay==6.1"])
658
                pex_binary(
659
                    scie="lazy",
660
                    dependencies=[":cowsay"],
661
                    scie_busybox='@',
662
                    {passthrough}
663
                )
664
                """
665
            ),
666
        }
667
    )
668
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
669
    field_set = PexBinaryFieldSet.create(tgt)
1✔
670
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
671
    # Just asserting the right files are there to avoid downloading the whole
672
    # PBS during testing
UNCOV
673
    assert len(result.artifacts) == 2
×
UNCOV
674
    expected_pex_relpath = "src.py.project/project.pex"
×
UNCOV
675
    assert expected_pex_relpath == result.artifacts[0].relpath
×
UNCOV
676
    expected_scie_relpath = "src.py.project/project"
×
UNCOV
677
    assert expected_scie_relpath == result.artifacts[1].relpath
×
678

679

680
def test_scie_pbs_version(rule_runner: PythonRuleRunner) -> None:
1✔
681
    rule_runner.write_files(
1✔
682
        {
683
            "src/py/project/app.py": dedent(
684
                """\
685
                print("hello")
686
                """
687
            ),
688
            "src/py/project/BUILD": dedent(
689
                """\
690
                python_sources(name="lib")
691
                python_requirement(name="cowsay", requirements=["cowsay==6.1"])
692
                pex_binary(
693
                    entry_point="app.py",
694
                    scie="lazy",
695
                    scie_pbs_release="20241219"
696
                )
697
                """
698
            ),
699
        }
700
    )
701
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
702
    field_set = PexBinaryFieldSet.create(tgt)
1✔
703
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
704
    # Just asserting the right files are there to avoid downloading the whole
705
    # PBS during testing
706
    assert len(result.artifacts) == 2
1✔
707
    expected_pex_relpath = "src.py.project/project.pex"
1✔
708
    assert expected_pex_relpath == result.artifacts[0].relpath
1✔
709
    expected_scie_relpath = "src.py.project/project"
1✔
710
    assert expected_scie_relpath == result.artifacts[1].relpath
1✔
711

712

713
def test_scie_python_version_available(rule_runner: PythonRuleRunner) -> None:
1✔
714
    rule_runner.write_files(
1✔
715
        {
716
            "src/py/project/app.py": dedent(
717
                """\
718
                print("hello")
719
                """
720
            ),
721
            "src/py/project/BUILD": dedent(
722
                """\
723
                python_sources(name="lib")
724
                pex_binary(
725
                    entry_point="app.py",
726
                    scie="lazy",
727
                    scie_pbs_release="20251031",
728
                    scie_python_version="3.12.12"
729
                )
730
                """
731
            ),
732
        }
733
    )
734
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
735
    field_set = PexBinaryFieldSet.create(tgt)
1✔
736
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
737
    # Just asserting the right files are there to avoid downloading the whole
738
    # PBS during testing
739
    assert len(result.artifacts) == 2
1✔
740
    expected_pex_relpath = "src.py.project/project.pex"
1✔
741
    assert expected_pex_relpath == result.artifacts[0].relpath
1✔
742
    expected_scie_relpath = "src.py.project/project"
1✔
743
    assert expected_scie_relpath == result.artifacts[1].relpath
1✔
744

745

746
def test_scie_python_version_unavailable(rule_runner: PythonRuleRunner) -> None:
1✔
747
    # The PBS project does not produce binaries for every patch release
748
    rule_runner.write_files(
1✔
749
        {
750
            "src/py/project/app.py": dedent(
751
                """\
752
                print("hello")
753
                """
754
            ),
755
            "src/py/project/BUILD": dedent(
756
                """\
757
                python_sources(name="lib")
758
                pex_binary(
759
                    entry_point="app.py",
760
                    scie="lazy",
761
                    scie_pbs_release="20251031",
762
                    scie_python_version="3.12.2"
763
                )
764
                """
765
            ),
766
        }
767
    )
768
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
769
    field_set = PexBinaryFieldSet.create(tgt)
1✔
770
    with pytest.raises(
1✔
771
        ExecutionError, match="No released assets found for release 20251031 Python 3.12.2"
772
    ):
773
        rule_runner.request(BuiltPackage, [field_set])
1✔
774

775

776
@pytest.mark.parametrize(
1✔
777
    "stripped",
778
    [
779
        "",
780
        "scie_pbs_stripped=True,",
781
        "scie_pbs_stripped=False,",
782
    ],
783
)
784
def test_scie_pbs_stripped(rule_runner: PythonRuleRunner, stripped: str) -> None:
1✔
785
    rule_runner.write_files(
1✔
786
        {
787
            "src/py/project/app.py": dedent(
788
                """\
789
                print("hello")
790
                """
791
            ),
792
            "src/py/project/BUILD": dedent(
793
                f"""\
794
                python_sources(name="lib")
795
                python_requirement(name="cowsay", requirements=["cowsay==6.1"])
796
                pex_binary(
797
                    scie="lazy",
798
                    dependencies=[":cowsay"],
799
                    scie_busybox='@',
800
                    {stripped}
801
                )
802
                """
803
            ),
804
        }
805
    )
806
    tgt = rule_runner.get_target(Address("src/py/project"))
1✔
807
    field_set = PexBinaryFieldSet.create(tgt)
1✔
808
    result = rule_runner.request(BuiltPackage, [field_set])
1✔
809
    # Just asserting the right files are there to avoid downloading the whole
810
    # PBS during testing
UNCOV
811
    assert len(result.artifacts) == 2
×
UNCOV
812
    expected_pex_relpath = "src.py.project/project.pex"
×
UNCOV
813
    assert expected_pex_relpath == result.artifacts[0].relpath
×
UNCOV
814
    expected_scie_relpath = "src.py.project/project"
×
UNCOV
815
    assert expected_scie_relpath == result.artifacts[1].relpath
×
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