Skip to content

Commit

Permalink
Support Pex CLI --unzip and --include-tools. (#11483)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jsirois committed Jan 21, 2021
1 parent bebdf6d commit 1d78860
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 11 deletions.
8 changes: 8 additions & 0 deletions src/python/pants/backend/python/goals/package_pex_binary.py
Expand Up @@ -10,11 +10,13 @@
PexEmitWarningsField,
PexEntryPointField,
PexIgnoreErrorsField,
PexIncludeToolsField,
PexInheritPathField,
)
from pants.backend.python.target_types import PexPlatformsField as PythonPlatformsField
from pants.backend.python.target_types import (
PexShebangField,
PexUnzipField,
PexZipSafeField,
ResolvedPexEntryPoint,
ResolvePexEntryPointRequest,
Expand Down Expand Up @@ -50,6 +52,8 @@ class PexBinaryFieldSet(PackageFieldSet, RunFieldSet):
shebang: PexShebangField
zip_safe: PexZipSafeField
platforms: PythonPlatformsField
unzip: PexUnzipField
include_tools: PexIncludeToolsField

def generate_additional_args(self, pex_binary_defaults: PexBinaryDefaults) -> Tuple[str, ...]:
args = []
Expand All @@ -65,6 +69,10 @@ def generate_additional_args(self, pex_binary_defaults: PexBinaryDefaults) -> Tu
args.append(f"--python-shebang={self.shebang.value}")
if self.zip_safe.value is False:
args.append("--not-zip-safe")
if self.unzip.value is True:
args.append("--unzip")
if self.include_tools.value is True:
args.append("--include-tools")
return tuple(args)


Expand Down
@@ -1,21 +1,21 @@
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import json
from textwrap import dedent

import pytest

from pants.testutil.pants_integration_test import run_pants, setup_tmpdir
from pants.testutil.pants_integration_test import PantsResult, run_pants, setup_tmpdir


@pytest.mark.parametrize(
"tgt_content",
("entry_point", "unzip", "include_tools"),
[
"python_library(name='lib')\npex_binary(entry_point='app.py')",
"python_library(name='lib')\npex_binary(entry_point='app.py:main')",
("app.py", True, True),
("app.py:main", False, False),
],
)
def test_run_sample_script(tgt_content: str) -> None:
def test_run_sample_script(entry_point: str, unzip: bool, include_tools: bool) -> None:
"""Test that we properly run a `pex_binary` target.
This checks a few things:
Expand All @@ -39,7 +39,16 @@ def main():
main()
"""
),
"src_root1/project/BUILD": tgt_content,
"src_root1/project/BUILD": dedent(
f"""\
python_library(name='lib')
pex_binary(
entry_point={entry_point!r},
unzip={unzip!r},
include_tools={include_tools!r},
)
"""
),
"src_root2/utils/strutil.py": dedent(
"""\
def upper_case(s):
Expand All @@ -48,18 +57,27 @@ def upper_case(s):
),
"src_root2/utils/BUILD": "python_library()",
}
with setup_tmpdir(sources) as tmpdir:
result = run_pants(
[

def run(*extra_args: str, **extra_env: str) -> PantsResult:
with setup_tmpdir(sources) as tmpdir:
args = [
"--backend-packages=pants.backend.python",
f"--source-root-patterns=['/{tmpdir}/src_root1', '/{tmpdir}/src_root2']",
"--pants-ignore=__pycache__",
"--pants-ignore=/src/python",
"run",
f"{tmpdir}/src_root1/project/app.py",
*extra_args,
]
)
return run_pants(args, extra_env=extra_env)

result = run()
assert "Hola, mundo.\n" in result.stderr
assert result.stdout == "HELLO WORLD.\n"
assert result.exit_code == 23

if include_tools:
result = run("--", "info", PEX_TOOLS="1")
assert result.exit_code == 0
pex_info = json.loads(result.stdout)
assert unzip == pex_info["unzip"]
23 changes: 23 additions & 0 deletions src/python/pants/backend/python/target_types.py
Expand Up @@ -254,6 +254,27 @@ def value_or_global_default(self, pex_binary_defaults: PexBinaryDefaults) -> boo
return self.value


class PexUnzipField(BoolField):
alias = "unzip"
default = False
value: bool
help = (
"Whether to have the PEX unzip itself into the PEX_ROOT before running.\n\nEnabling unzip "
"mode can provide lower startup latencies for most PEX files; even on first run."
)


class PexIncludeToolsField(BoolField):
alias = "include_tools"
default = False
value: bool
help = (
"Whether to include Pex tools in the PEX bootstrap code.\n\nWith tools included, the "
"generated PEX file can be executed with `PEX_TOOLS=1 <pex file> --help` to gain access "
"to all the available tools."
)


class PexBinary(Target):
alias = "pex_binary"
core_fields = (
Expand All @@ -268,6 +289,8 @@ class PexBinary(Target):
PexIgnoreErrorsField,
PexShebangField,
PexEmitWarningsField,
PexUnzipField,
PexIncludeToolsField,
)
help = (
"A Python target that can be converted into an executable PEX file.\n\nPEX files are "
Expand Down

0 comments on commit 1d78860

Please sign in to comment.