Skip to content

Commit 1d78860

Browse files
authored
Support Pex CLI --unzip and --include-tools. (#11483)
These options can help improve performance of end user PEX files and allow them to be used in more target environments via the `venv` tool respectively. Add corresponding `pex_binary` fields `unzip` and `include_tools` to allow users to opt-in to these options. Fixes #11482
1 parent bebdf6d commit 1d78860

File tree

3 files changed

+60
-11
lines changed

3 files changed

+60
-11
lines changed

src/python/pants/backend/python/goals/package_pex_binary.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
PexEmitWarningsField,
1111
PexEntryPointField,
1212
PexIgnoreErrorsField,
13+
PexIncludeToolsField,
1314
PexInheritPathField,
1415
)
1516
from pants.backend.python.target_types import PexPlatformsField as PythonPlatformsField
1617
from pants.backend.python.target_types import (
1718
PexShebangField,
19+
PexUnzipField,
1820
PexZipSafeField,
1921
ResolvedPexEntryPoint,
2022
ResolvePexEntryPointRequest,
@@ -50,6 +52,8 @@ class PexBinaryFieldSet(PackageFieldSet, RunFieldSet):
5052
shebang: PexShebangField
5153
zip_safe: PexZipSafeField
5254
platforms: PythonPlatformsField
55+
unzip: PexUnzipField
56+
include_tools: PexIncludeToolsField
5357

5458
def generate_additional_args(self, pex_binary_defaults: PexBinaryDefaults) -> Tuple[str, ...]:
5559
args = []
@@ -65,6 +69,10 @@ def generate_additional_args(self, pex_binary_defaults: PexBinaryDefaults) -> Tu
6569
args.append(f"--python-shebang={self.shebang.value}")
6670
if self.zip_safe.value is False:
6771
args.append("--not-zip-safe")
72+
if self.unzip.value is True:
73+
args.append("--unzip")
74+
if self.include_tools.value is True:
75+
args.append("--include-tools")
6876
return tuple(args)
6977

7078

src/python/pants/backend/python/goals/run_pex_binary_integration_test.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
22
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3-
3+
import json
44
from textwrap import dedent
55

66
import pytest
77

8-
from pants.testutil.pants_integration_test import run_pants, setup_tmpdir
8+
from pants.testutil.pants_integration_test import PantsResult, run_pants, setup_tmpdir
99

1010

1111
@pytest.mark.parametrize(
12-
"tgt_content",
12+
("entry_point", "unzip", "include_tools"),
1313
[
14-
"python_library(name='lib')\npex_binary(entry_point='app.py')",
15-
"python_library(name='lib')\npex_binary(entry_point='app.py:main')",
14+
("app.py", True, True),
15+
("app.py:main", False, False),
1616
],
1717
)
18-
def test_run_sample_script(tgt_content: str) -> None:
18+
def test_run_sample_script(entry_point: str, unzip: bool, include_tools: bool) -> None:
1919
"""Test that we properly run a `pex_binary` target.
2020
2121
This checks a few things:
@@ -39,7 +39,16 @@ def main():
3939
main()
4040
"""
4141
),
42-
"src_root1/project/BUILD": tgt_content,
42+
"src_root1/project/BUILD": dedent(
43+
f"""\
44+
python_library(name='lib')
45+
pex_binary(
46+
entry_point={entry_point!r},
47+
unzip={unzip!r},
48+
include_tools={include_tools!r},
49+
)
50+
"""
51+
),
4352
"src_root2/utils/strutil.py": dedent(
4453
"""\
4554
def upper_case(s):
@@ -48,18 +57,27 @@ def upper_case(s):
4857
),
4958
"src_root2/utils/BUILD": "python_library()",
5059
}
51-
with setup_tmpdir(sources) as tmpdir:
52-
result = run_pants(
53-
[
60+
61+
def run(*extra_args: str, **extra_env: str) -> PantsResult:
62+
with setup_tmpdir(sources) as tmpdir:
63+
args = [
5464
"--backend-packages=pants.backend.python",
5565
f"--source-root-patterns=['/{tmpdir}/src_root1', '/{tmpdir}/src_root2']",
5666
"--pants-ignore=__pycache__",
5767
"--pants-ignore=/src/python",
5868
"run",
5969
f"{tmpdir}/src_root1/project/app.py",
70+
*extra_args,
6071
]
61-
)
72+
return run_pants(args, extra_env=extra_env)
6273

74+
result = run()
6375
assert "Hola, mundo.\n" in result.stderr
6476
assert result.stdout == "HELLO WORLD.\n"
6577
assert result.exit_code == 23
78+
79+
if include_tools:
80+
result = run("--", "info", PEX_TOOLS="1")
81+
assert result.exit_code == 0
82+
pex_info = json.loads(result.stdout)
83+
assert unzip == pex_info["unzip"]

src/python/pants/backend/python/target_types.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,27 @@ def value_or_global_default(self, pex_binary_defaults: PexBinaryDefaults) -> boo
254254
return self.value
255255

256256

257+
class PexUnzipField(BoolField):
258+
alias = "unzip"
259+
default = False
260+
value: bool
261+
help = (
262+
"Whether to have the PEX unzip itself into the PEX_ROOT before running.\n\nEnabling unzip "
263+
"mode can provide lower startup latencies for most PEX files; even on first run."
264+
)
265+
266+
267+
class PexIncludeToolsField(BoolField):
268+
alias = "include_tools"
269+
default = False
270+
value: bool
271+
help = (
272+
"Whether to include Pex tools in the PEX bootstrap code.\n\nWith tools included, the "
273+
"generated PEX file can be executed with `PEX_TOOLS=1 <pex file> --help` to gain access "
274+
"to all the available tools."
275+
)
276+
277+
257278
class PexBinary(Target):
258279
alias = "pex_binary"
259280
core_fields = (
@@ -268,6 +289,8 @@ class PexBinary(Target):
268289
PexIgnoreErrorsField,
269290
PexShebangField,
270291
PexEmitWarningsField,
292+
PexUnzipField,
293+
PexIncludeToolsField,
271294
)
272295
help = (
273296
"A Python target that can be converted into an executable PEX file.\n\nPEX files are "

0 commit comments

Comments
 (0)