-
-
Notifications
You must be signed in to change notification settings - Fork 646
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Speed up
run
to no longer rebuild a Pex on source file changes (#10410
) Whereas `binary` must include source files in the PEX, `run` does not need to. We get less cache invalidation and generally faster performance by instead having the chroot simply be populated with the source files, similar to how we implement Pytest. We still use a Pex to handle the `entry_point` field and to resolve all 3rd party requirements. Before, with a whitespace change: ``` ▶ /usr/bin/time ./pants run build-support/bin/generate_travis_yml.py > .travis.yml 2.72 real 0.73 user 0.21 sys ``` After, with a whitespace change: ``` ▶ /usr/bin/time ./pants run build-support/bin/generate_travis_yml.py > .travis.yml 1.87 real 0.73 user 0.21 sys ``` Implements half of #10406.
- Loading branch information
1 parent
2eb679a
commit 64bfcbb
Showing
6 changed files
with
187 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
72 changes: 72 additions & 0 deletions
72
src/python/pants/backend/python/rules/run_python_binary.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from pants.backend.python.rules.create_python_binary import PythonBinaryFieldSet | ||
from pants.backend.python.rules.pex import Pex, PexPlatforms | ||
from pants.backend.python.rules.pex_from_targets import PexFromTargetsRequest | ||
from pants.backend.python.rules.python_sources import ( | ||
UnstrippedPythonSources, | ||
UnstrippedPythonSourcesRequest, | ||
) | ||
from pants.backend.python.target_types import PythonBinaryDefaults, PythonBinarySources | ||
from pants.core.goals.binary import BinaryFieldSet | ||
from pants.core.goals.run import RunRequest | ||
from pants.core.util_rules.determine_source_files import AllSourceFilesRequest, SourceFiles | ||
from pants.engine.addresses import Addresses | ||
from pants.engine.fs import Digest, MergeDigests | ||
from pants.engine.rules import SubsystemRule, rule | ||
from pants.engine.selectors import Get, MultiGet | ||
from pants.engine.target import InvalidFieldException, TransitiveTargets | ||
from pants.engine.unions import UnionRule | ||
|
||
|
||
@rule | ||
async def run_python_binary( | ||
field_set: PythonBinaryFieldSet, python_binary_defaults: PythonBinaryDefaults | ||
) -> RunRequest: | ||
entry_point = field_set.entry_point.value | ||
if entry_point is None: | ||
binary_sources = await Get( | ||
SourceFiles, AllSourceFilesRequest([field_set.sources], strip_source_roots=True) | ||
) | ||
entry_point = PythonBinarySources.translate_source_file_to_entry_point(binary_sources.files) | ||
if entry_point is None: | ||
raise InvalidFieldException( | ||
"You must either specify `sources` or `entry_point` for the `python_binary` target " | ||
f"{repr(field_set.address)} in order to run it, but both fields were undefined." | ||
) | ||
|
||
transitive_targets = await Get(TransitiveTargets, Addresses([field_set.address])) | ||
|
||
output_filename = f"{field_set.address.target_name}.pex" | ||
pex_request = Get( | ||
Pex, | ||
PexFromTargetsRequest( | ||
addresses=Addresses([field_set.address]), | ||
platforms=PexPlatforms.create_from_platforms_field(field_set.platforms), | ||
output_filename=output_filename, | ||
additional_args=field_set.generate_additional_args(python_binary_defaults), | ||
include_source_files=False, | ||
), | ||
) | ||
source_files_request = Get( | ||
UnstrippedPythonSources, | ||
UnstrippedPythonSourcesRequest(transitive_targets.closure, include_files=True), | ||
) | ||
pex, source_files = await MultiGet(pex_request, source_files_request) | ||
|
||
merged_digest = await Get(Digest, MergeDigests([pex.digest, source_files.snapshot.digest])) | ||
return RunRequest( | ||
digest=merged_digest, | ||
binary_name=pex.output_filename, | ||
extra_args=("-m", entry_point), | ||
env={"PEX_EXTRA_SYS_PATH": ":".join(source_files.source_roots)}, | ||
) | ||
|
||
|
||
def rules(): | ||
return [ | ||
run_python_binary, | ||
UnionRule(BinaryFieldSet, PythonBinaryFieldSet), | ||
SubsystemRule(PythonBinaryDefaults), | ||
] |
68 changes: 68 additions & 0 deletions
68
src/python/pants/backend/python/rules/run_python_binary_integration_test.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from pathlib import Path | ||
from textwrap import dedent | ||
|
||
from pants.base.build_environment import get_buildroot | ||
from pants.testutil.pants_run_integration_test import PantsRunIntegrationTest | ||
from pants.util.contextutil import temporary_dir | ||
|
||
|
||
class RunPythonBinaryIntegrationTest(PantsRunIntegrationTest): | ||
def test_sample_script(self) -> None: | ||
"""Test that we properly run a `python_binary` target. | ||
This checks a few things: | ||
- We can handle source roots. | ||
- We properly load third party requirements. | ||
- We propagate the error code. | ||
""" | ||
with temporary_dir(root_dir=get_buildroot()) as tmpdir: | ||
tmpdir_relative = Path(tmpdir).relative_to(get_buildroot()) | ||
|
||
src_root1 = Path(tmpdir, "src_root1/project") | ||
src_root1.mkdir(parents=True) | ||
(src_root1 / "app.py").write_text( | ||
dedent( | ||
"""\ | ||
import sys | ||
from utils.strutil import upper_case | ||
if __name__ == "__main__": | ||
print(upper_case("Hello world.")) | ||
print("Hola, mundo.", file=sys.stderr) | ||
sys.exit(23) | ||
""" | ||
) | ||
) | ||
(src_root1 / "BUILD").write_text("python_binary(sources=['app.py'])") | ||
|
||
src_root2 = Path(tmpdir, "src_root2/utils") | ||
src_root2.mkdir(parents=True) | ||
(src_root2 / "strutil.py").write_text( | ||
dedent( | ||
"""\ | ||
def upper_case(s): | ||
return s.upper() | ||
""" | ||
) | ||
) | ||
(src_root2 / "BUILD").write_text("python_library()") | ||
result = self.run_pants( | ||
[ | ||
"--dependency-inference", | ||
( | ||
f"--source-root-patterns=['/{tmpdir_relative}/src_root1', " | ||
f"'/{tmpdir_relative}/src_root2']" | ||
), | ||
"--pants-ignore=__pycache__", | ||
"run", | ||
f"{tmpdir_relative}/src_root1/project/app.py", | ||
] | ||
) | ||
|
||
assert result.returncode == 23 | ||
assert result.stdout_data == "HELLO WORLD.\n" | ||
assert "Hola, mundo.\n" in result.stderr_data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters