Skip to content

Commit 6d64128

Browse files
authored
Fix pex3 scie create --dest-dir handling. (#3076)
Fixes #3075
1 parent f048bdd commit 6d64128

File tree

4 files changed

+99
-31
lines changed

4 files changed

+99
-31
lines changed

CHANGES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Release Notes
22

3+
## 2.82.1
4+
5+
This release fixes `pex3 scie create --dest-dir` to work when the specified PEX is a local file
6+
path. Previously `--dest-dir` only worked when the specified PEX was an URL.
7+
8+
* Fix `pex3 scie create --dest-dir` handling. (#3076)
9+
310
## 2.82.0
411

512
This release adds support for resource path bindings to plain PEXes as a follow-on to adding

pex/cli/commands/scie.py

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import hashlib
77
import io
88
import os
9+
import shutil
910
from argparse import ArgumentParser
1011

1112
from pex import scie, specifier_sets
@@ -26,7 +27,7 @@
2627
from pex.util import CacheHelper
2728

2829
if TYPE_CHECKING:
29-
from typing import Optional, Union
30+
from typing import Optional, Tuple, Union
3031

3132
import attr # vendor:skip
3233
else:
@@ -188,6 +189,30 @@ def _fetch(
188189
return dest
189190

190191

192+
def _extract_pex_info(path):
193+
# type: (str) -> Union[Tuple[str, PexInfo], Error]
194+
195+
pex_info_or_error = catch(PexInfo.from_pex, path)
196+
if isinstance(pex_info_or_error, Error):
197+
return Error(
198+
"The path {path} does not appear to be a PEX: {err}".format(
199+
path=path, err=pex_info_or_error
200+
)
201+
)
202+
203+
raw_pex_version = pex_info_or_error.build_properties.get("pex_version")
204+
if raw_pex_version and Version(raw_pex_version) < Version("2.1.25"):
205+
return Error(
206+
"Can only create scies from PEXes built by Pex 2.1.25 (which was released on "
207+
"January 21st, 2021) or newer.\n"
208+
"The PEX at {path} was built by Pex {pex_version}.".format(
209+
path=path, pex_version=raw_pex_version
210+
)
211+
)
212+
213+
return path, pex_info_or_error
214+
215+
191216
class Scie(OutputMixin, BuildTimeCommand):
192217
"""Manipulate scies."""
193218

@@ -240,40 +265,38 @@ def _create(self):
240265

241266
resolver_configuration = resolver_options.configure(self.options)
242267
if os.path.exists(self.options.pex[0]):
243-
pex_file = self.options.pex[0]
244-
else:
245-
pex_file = try_(
246-
_fetch(
247-
url=ArtifactURL.parse(self.options.pex[0]),
248-
fetcher=URLFetcher(
249-
network_configuration=resolver_configuration.network_configuration,
250-
handle_file_urls=True,
251-
password_entries=resolver_configuration.repos_configuration.password_entries,
252-
),
253-
dest_dir=self.options.dest_dir,
254-
)
255-
)
256-
257-
pex_info_or_error = catch(PexInfo.from_pex, pex_file)
258-
if isinstance(pex_info_or_error, Error):
259-
return Error(
260-
"The path {pex_file} does not appear to be a PEX: {err}".format(
261-
pex_file=pex_file, err=pex_info_or_error
268+
pex_file, pex_info = try_(_extract_pex_info(self.options.pex[0]))
269+
if self.options.dest_dir and os.path.realpath(
270+
self.options.dest_dir
271+
) != os.path.realpath(os.path.dirname(pex_file)):
272+
pex_dest = os.path.join(
273+
safe_mkdir(self.options.dest_dir), os.path.basename(pex_file)
262274
)
263-
)
264-
raw_pex_version = pex_info_or_error.build_properties.get("pex_version")
265-
if raw_pex_version and Version(raw_pex_version) < Version("2.1.25"):
266-
return Error(
267-
"Can only create scies from PEXes built by Pex 2.1.25 (which was released on "
268-
"January 21st, 2021) or newer.\n"
269-
"The PEX at {pex_file} was built by Pex {pex_version}.".format(
270-
pex_file=pex_file, pex_version=raw_pex_version
275+
if os.path.isfile(pex_file):
276+
shutil.copy(pex_file, pex_dest)
277+
else:
278+
shutil.copytree(pex_file, pex_dest)
279+
pex_file = pex_dest
280+
else:
281+
pex_file, pex_info = try_(
282+
_extract_pex_info(
283+
try_(
284+
_fetch(
285+
url=ArtifactURL.parse(self.options.pex[0]),
286+
fetcher=URLFetcher(
287+
network_configuration=resolver_configuration.network_configuration,
288+
handle_file_urls=True,
289+
password_entries=resolver_configuration.repos_configuration.password_entries,
290+
),
291+
dest_dir=self.options.dest_dir,
292+
)
293+
)
271294
)
272295
)
273296

274297
targets = try_(
275298
_resolve_targets(
276-
pex_interpreter_constraints=pex_info_or_error.interpreter_constraints,
299+
pex_interpreter_constraints=pex_info.interpreter_constraints,
277300
target_configuration=target_options.configure(
278301
self.options, pip_configuration=resolver_configuration.pip_configuration
279302
),
@@ -313,6 +336,9 @@ def _create(self):
313336
if scie_configuration.options.scie_only and os.path.realpath(
314337
pex_file
315338
) != os.path.realpath(scie_info.file):
316-
os.unlink(pex_file)
339+
if os.path.isfile(pex_file):
340+
os.unlink(pex_file)
341+
else:
342+
shutil.rmtree(pex_file)
317343

318344
return Ok()

pex/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Copyright 2015 Pex project contributors.
22
# Licensed under the Apache License, Version 2.0 (see LICENSE).
33

4-
__version__ = "2.82.0"
4+
__version__ = "2.82.1"

tests/integration/cli/commands/test_scie_create.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
import subprocess
1111
import sys
1212

13+
import pytest
14+
1315
from pex.common import safe_delete
1416
from pex.fetcher import URLFetcher
1517
from pex.interpreter import PythonInterpreter
18+
from pex.layout import Layout
1619
from pex.pep_440 import Version
1720
from pex.typing import TYPE_CHECKING
1821
from pex.util import CacheHelper
@@ -292,3 +295,35 @@ def test_pex_url(tmpdir):
292295
"2.80.0"
293296
== subprocess.check_output(args=[os.path.join(dest, "pex"), "-V"]).decode("utf-8").strip()
294297
)
298+
299+
300+
@skip_if_no_provider
301+
@pytest.mark.parametrize(
302+
"layout", [pytest.param(layout, id=str(layout)) for layout in Layout.values()]
303+
)
304+
def test_pex_file_path_dest(
305+
tmpdir, # type: Tempdir
306+
layout, # type: Layout.Value
307+
):
308+
pex = tmpdir.join("cowsay.pex")
309+
run_pex_command(
310+
args=["cowsay<6", "-c", "cowsay", "-o", pex, "--layout", str(layout)]
311+
).assert_success()
312+
313+
dest = tmpdir.join("dest")
314+
run_pex3("scie", "create", "--style", "eager", "-d", dest, pex).assert_success()
315+
316+
assert b"| Moo! |" in subprocess.check_output(args=[sys.executable, pex, "Moo!"])
317+
assert not os.path.exists(tmpdir.join("cowsay"))
318+
assert b"| Foo! |" in subprocess.check_output(
319+
args=[sys.executable, os.path.join(dest, "cowsay.pex"), "Foo!"]
320+
)
321+
assert b"| Boo! |" in subprocess.check_output(args=[os.path.join(dest, "cowsay"), "Boo!"])
322+
323+
dest2 = tmpdir.join("dest2")
324+
run_pex3("scie", "create", "--style", "eager", "--scie-only", "-d", dest2, pex).assert_success()
325+
326+
assert b"| Zoo! |" in subprocess.check_output(args=[sys.executable, pex, "Zoo!"])
327+
assert not os.path.exists(tmpdir.join("cowsay"))
328+
assert not os.path.exists(os.path.join(dest2, "cowsay.pex"))
329+
assert b"| Goo! |" in subprocess.check_output(args=[os.path.join(dest2, "cowsay"), "Goo!"])

0 commit comments

Comments
 (0)