diff --git a/CHANGES.md b/CHANGES.md index 56963c2d1..70085b5fd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Release Notes +## 2.91.5 + +This release fixes Pex PEX scie behavior to match Pex. + +* Make ephemeral exec, venv repl and scie compatible (#3129) + ## 2.91.4 This release brings 2 performance fixes from @tobni: diff --git a/pex/bin/pex.py b/pex/bin/pex.py index fd05b1d11..a7dd5c254 100644 --- a/pex/bin/pex.py +++ b/pex/bin/pex.py @@ -1289,6 +1289,11 @@ def main(args=None): try: with global_environment(options) as env: + # These are set if we're running from within a Pex PEX scie and already installed. + # We clear them to allow recursive use of Pex / PEX. + env.pop("__PEX_EXE__", None) + env.pop("__PEX_ENTRY_POINT__", None) + try: resolver_configuration = resolver_options.configure( options, diff --git a/pex/pex_boot.py b/pex/pex_boot.py index 11ed684dc..c69b1b1d1 100644 --- a/pex/pex_boot.py +++ b/pex/pex_boot.py @@ -229,12 +229,11 @@ def boot( if os.path.isfile(pex_exe): sys.argv[0] = pex_exe - overridden_entry_point = os.environ.get("__PEX_ENTRY_POINT__", None) + overridden_pex = os.environ.get("__PEX_EXE__", None) sys.path[0] = os.path.abspath(sys.path[0]) - sys.path.insert( - 0, os.path.abspath(os.path.join(overridden_entry_point or entry_point, bootstrap_dir)) - ) + sys.path.insert(0, os.path.abspath(os.path.join(overridden_pex or entry_point, bootstrap_dir))) + overridden_entry_point = os.environ.get("__PEX_ENTRY_POINT__", None) if overridden_entry_point and overridden_entry_point != entry_point: # This PEX has already been installed out of band; so we short-circuit to execute the # pre-installed PEX. diff --git a/pex/version.py b/pex/version.py index 10c259b40..9047d3f61 100644 --- a/pex/version.py +++ b/pex/version.py @@ -1,4 +1,4 @@ # Copyright 2015 Pex project contributors. # Licensed under the Apache License, Version 2.0 (see LICENSE). -__version__ = "2.91.4" +__version__ = "2.91.5" diff --git a/tests/integration/cli/commands/test_lock_subset.py b/tests/integration/cli/commands/test_lock_subset.py index 2b40fb73b..87a9a266b 100644 --- a/tests/integration/cli/commands/test_lock_subset.py +++ b/tests/integration/cli/commands/test_lock_subset.py @@ -139,7 +139,7 @@ def test_lock_subset_miss(lock): # type: (str) -> None _, original_locked_reqs = index(lock) - requests_version = original_locked_reqs[ProjectName("requests")].pin.version + requests_version = original_locked_reqs[ProjectName("requests")].pin.version.raw run_pex3( "lock", "subset", "--lock", lock, "requests!={version}".format(version=requests_version) ).assert_failure( diff --git a/tests/integration/scie/test_scie_ephemeral_run.py b/tests/integration/scie/test_scie_ephemeral_run.py new file mode 100644 index 000000000..505f0ad51 --- /dev/null +++ b/tests/integration/scie/test_scie_ephemeral_run.py @@ -0,0 +1,68 @@ +# Copyright 2026 Pex project contributors. +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import absolute_import + +import subprocess +import sys + +import pytest + +from pex.typing import TYPE_CHECKING +from testing import IS_PYPY, make_env, run_pex_command +from testing.pytest_utils.tmp import Tempdir +from testing.scie import skip_if_no_provider + +if TYPE_CHECKING: + from typing import List + + +@pytest.mark.parametrize( + "execution_mode_args", + [ + pytest.param([], id="ZIPAPP"), + pytest.param(["--venv"], id="VENV"), + ], +) +@skip_if_no_provider +def test_scie_ephemeral_run( + tmpdir, # type: Tempdir + pex_wheel, # type: str + execution_mode_args, # type: List[str] +): + # type: (...) -> None + + pex_scie = tmpdir.join("pex") + run_pex_command( + args=[pex_wheel, "-c", "pex", "-o", pex_scie, "--scie", "eager"] + execution_mode_args + ).assert_success() + + ic = "{impl}=={major}.{minor}.*".format( + impl="PyPy" if IS_PYPY else "CPython", major=sys.version_info[0], minor=sys.version_info[1] + ) + + # Verify the scie can perform an ephemeral run with `-- -c`. + output = subprocess.check_output( + args=[ + pex_scie, + "--interpreter-constraint", + ic, + "--", + "-c", + "import sys; print(sys.executable)", + ], + env=make_env(PATH=None), + ) + assert output.decode("utf-8").strip() + + # Verify the scie can drop into a REPL via ephemeral run. + process = subprocess.Popen( + args=[pex_scie, "--interpreter-constraint", ic, "--"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=make_env(PATH=None), + ) + stdout, stderr = process.communicate(input=b"import sys; print(sys.executable)\nquit()\n") + assert process.returncode == 0, stderr.decode("utf-8") + assert b">>>" in stdout or b">>>" in stderr