Skip to content

Commit d5eaaac

Browse files
authored
Switch Pex installed wheels to --prefix scheme. (#1661)
Previously Pex used Pip's `--target` scheme which had both known bugs (pypa/pip#7658) and unknown quirks that Pex was failing to fully be able to work around. Switch to the `--prefix` scheme which exactly mirrors the scheme venvs use so that venvs can be created with content of all sorts placed where it belongs. This removes fragile parsing and interpretation of the RECORD; now Pex only creates a RECORD, which is much more straight forward, when building a venv. Partially addresses #1655 by switching to sha256 for all external artifact hashing. Only internal hashing remains for: 1. `interpreters` / INTERP-INFO 2. `venvs` and `unzipped_pexes` / PEX-INFO pex_hash (but this is a hash that includes all distributions' sha256 hashes). Fixes #1656 Closes #1662
1 parent 8e891a2 commit d5eaaac

File tree

40 files changed

+1062
-966
lines changed

40 files changed

+1062
-966
lines changed

pex/common.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@
4747
DETERMINISTIC_DATETIME_TIMESTAMP = (DETERMINISTIC_DATETIME - _UNIX_EPOCH).total_seconds()
4848

4949

50+
def find_site_packages(prefix_dir):
51+
# type: (str) -> Optional[str]
52+
"""Return the absolute path to the site-packages directory of the given Python installation."""
53+
for root, dirs, _ in os.walk(prefix_dir):
54+
for d in dirs:
55+
if "site-packages" == d:
56+
return os.path.join(root, d)
57+
return None
58+
59+
5060
def filter_pyc_dirs(dirs):
5161
# type: (Iterable[str]) -> Iterator[str]
5262
"""Return an iterator over the input `dirs` filtering out Python bytecode cache directories."""

pex/finders.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88

99
from pex.common import is_python_script
10+
from pex.pep_376 import InstalledWheel
1011
from pex.third_party.pkg_resources import Distribution
1112
from pex.typing import TYPE_CHECKING, cast
1213

@@ -27,7 +28,7 @@ def find(
2728
name, # type: str
2829
):
2930
# type: (...) -> Optional[DistributionScript]
30-
script_path = os.path.join(dist.location, "bin", name)
31+
script_path = InstalledWheel.load(dist.location).stashed_path("bin", name)
3132
return cls(dist=dist, path=script_path) if os.path.isfile(script_path) else None
3233

3334
dist = attr.ib() # type: Distribution

pex/interpreter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ def create_interpreter(
773773
process = Executor.open_process(
774774
cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
775775
)
776-
job = Job(command=cmd, process=process, finalizer=lambda: safe_rmtree(cwd))
776+
job = Job(command=cmd, process=process, finalizer=lambda _: safe_rmtree(cwd))
777777
return SpawnedJob.file(job, output_file=cache_file, result_func=create_interpreter)
778778

779779
@classmethod

pex/jobs.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,15 @@ def __init__(
5050
self,
5151
command, # type: Iterable[str]
5252
process, # type: subprocess.Popen
53-
finalizer=None, # type: Optional[Callable[[], None]]
53+
finalizer=None, # type: Optional[Callable[[int], None]]
5454
):
5555
# type: (...) -> None
5656
"""
5757
:param command: The command used to spawn the job process.
5858
:param process: The spawned process handle.
59-
:param finalizer: An optional cleanup function to call exactly once when the underlying
60-
process terminates in the course of calling this job's public methods.
59+
:param finalizer: An optional cleanup function to call exactly once with the process return
60+
code when the underlying process terminates in the course of calling this
61+
job's public methods.
6162
"""
6263
self._command = tuple(command)
6364
self._process = process
@@ -131,7 +132,7 @@ def create_error(
131132

132133
def _finalize_job(self):
133134
if self._finalizer is not None:
134-
self._finalizer()
135+
self._finalizer(self._process.returncode)
135136
self._finalizer = None
136137

137138
def _check_returncode(self, stderr=None):

0 commit comments

Comments
 (0)