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

pantsbuild / pants / 20428083889

22 Dec 2025 09:43AM UTC coverage: 80.285% (-0.01%) from 80.296%
20428083889

Pull #21918

github

web-flow
Merge c895684e5 into 06f105be8
Pull Request #21918: [WIP] partition protobuf dependency inference by any "resolve-like" fields from plugins

191 of 263 new or added lines in 9 files covered. (72.62%)

44 existing lines in 2 files now uncovered.

78686 of 98008 relevant lines covered (80.29%)

3.65 hits per line

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

96.27
/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
2✔
5

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

13
import pytest
2✔
14

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

46

47
@pytest.fixture
2✔
48
def rule_runner() -> PythonRuleRunner:
2✔
49
    rule_runner = PythonRuleRunner(
2✔
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"})
2✔
72
    return rule_runner
2✔
73

74

75
def test_warn_files_targets(rule_runner: PythonRuleRunner, caplog) -> None:
2✔
76
    rule_runner.write_files(
2✔
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"))
2✔
106
    field_set = PexBinaryFieldSet.create(tgt)
2✔
107

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

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

119

120
def test_include_sources_avoids_files_targets_warning(
2✔
121
    rule_runner: PythonRuleRunner, caplog
122
) -> None:
123
    rule_runner.write_files(
2✔
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"))
2✔
170
    field_set = PexBinaryFieldSet.create(tgt)
2✔
171

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

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

179

180
@pytest.mark.parametrize(
2✔
181
    "layout",
182
    [pytest.param(layout, id=layout.value) for layout in PexLayout],
183
)
184
def test_layout(rule_runner: PythonRuleRunner, layout: PexLayout) -> None:
2✔
185
    rule_runner.write_files(
2✔
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"))
2✔
210
    field_set = PexBinaryFieldSet.create(tgt)
2✔
211
    result = rule_runner.request(BuiltPackage, [field_set])
2✔
212
    assert len(result.artifacts) == 1
2✔
213
    expected_pex_relpath = "src.py.project/project.pex"
2✔
214
    assert expected_pex_relpath == result.artifacts[0].relpath
2✔
215

216
    rule_runner.write_digest(result.digest)
2✔
217
    executable = os.path.join(
2✔
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(
2✔
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
2✔
234

235

236
@pytest.fixture
2✔
237
def pex_executable(rule_runner: PythonRuleRunner) -> str:
2✔
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
2✔
259
@skip_unless_python38_present
2✔
260
@pytest.mark.parametrize("target_type", ["files", "resources"])
2✔
261
def test_complete_platforms(rule_runner: PythonRuleRunner, target_type: str) -> None:
2✔
262
    linux_complete_platform = pkgutil.get_data(__name__, "platform-linux-py38.json")
2✔
263
    assert linux_complete_platform is not None
2✔
264

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

268
    rule_runner.write_files(
2✔
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"))
2✔
286
    field_set = PexBinaryFieldSet.create(tgt)
2✔
287
    result = rule_runner.request(BuiltPackage, [field_set])
2✔
288
    assert len(result.artifacts) == 1
2✔
289
    expected_pex_relpath = "src.py.project/project.pex"
2✔
290
    assert expected_pex_relpath == result.artifacts[0].relpath
2✔
291

292
    rule_runner.write_digest(result.digest)
2✔
293
    executable = os.path.join(rule_runner.build_root, expected_pex_relpath)
2✔
294
    pex_info = json.loads(
2✔
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(
2✔
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:
2✔
311
    rule_runner.write_files(
2✔
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)
2✔
358
    class Results:
2✔
359
        pythonpath: str | None
2✔
360
        sys_path: list[str]
2✔
361

362
    def execute_pex(address: Address, **extra_env) -> Results:
2✔
363
        tgt = rule_runner.get_target(address)
2✔
364
        field_set = PexBinaryFieldSet.create(tgt)
2✔
365
        result = rule_runner.request(BuiltPackage, [field_set])
2✔
366
        assert len(result.artifacts) == 1
2✔
367
        rule_runner.write_digest(result.digest)
2✔
368
        relpath = result.artifacts[0].relpath
2✔
369
        assert relpath is not None
2✔
370
        pex = os.path.join(rule_runner.build_root, relpath)
2✔
371
        assert os.path.isfile(pex)
2✔
372
        process = subprocess.run(
2✔
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)
2✔
380
        return Results(pythonpath=data["PYTHONPATH"], sys_path=data["sys.path"])
2✔
381

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

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

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

396

397
def test_sh_boot_plumb(rule_runner: PythonRuleRunner) -> None:
2✔
398
    rule_runner.write_files(
2✔
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"))
2✔
417
    field_set = PexBinaryFieldSet.create(tgt)
2✔
418
    result = rule_runner.request(BuiltPackage, [field_set])
2✔
419
    assert len(result.artifacts) == 1
2✔
420
    expected_pex_relpath = "src.py.project/project.pex"
2✔
421
    assert expected_pex_relpath == result.artifacts[0].relpath
2✔
422

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

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

430

431
def test_extra_build_args(rule_runner: PythonRuleRunner) -> None:
2✔
432
    rule_runner.write_files(
2✔
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"))
2✔
452
    field_set = PexBinaryFieldSet.create(tgt)
2✔
453
    result = rule_runner.request(PexFromTargetsRequestForBuiltPackage, [field_set])
2✔
454

455
    additional_args = result.request.additional_args
2✔
456

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

460

461
def test_package_with_python_provider() -> None:
2✔
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(
2✔
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(
2✔
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"))
2✔
494
    field_set = PexBinaryFieldSet.create(tgt)
2✔
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"])
2✔
502

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

507

508
def test_scie_defaults(rule_runner: PythonRuleRunner) -> None:
2✔
509
    rule_runner.write_files(
2✔
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
                    scie_pbs_release="20251031",  # The last release that includes Python 3.9.
523
                )
524
                """
525
            ),
526
        }
527
    )
528
    tgt = rule_runner.get_target(Address("src/py/project"))
2✔
529
    field_set = PexBinaryFieldSet.create(tgt)
2✔
530
    result = rule_runner.request(BuiltPackage, [field_set])
2✔
UNCOV
531
    assert len(result.artifacts) == 2
1✔
UNCOV
532
    expected_pex_relpath = "src.py.project/project.pex"
1✔
UNCOV
533
    assert expected_pex_relpath == result.artifacts[0].relpath
1✔
UNCOV
534
    expected_scie_relpath = "src.py.project/project"
1✔
UNCOV
535
    assert expected_scie_relpath == result.artifacts[1].relpath
1✔
536

537

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

571

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

606

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

641

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

684

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

717

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

750

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

780

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

© 2025 Coveralls, Inc