From 610994c0558f4ea1a6e2c541a5e6660469a8db8f Mon Sep 17 00:00:00 2001 From: HandSonic <3> Date: Sat, 11 May 2024 14:44:02 +0800 Subject: [PATCH 1/4] Fix #1949 --- docs/changelog/1949.bugfix.rst | 1 + src/virtualenv/util/zipapp.py | 6 ++++-- tests/integration/test_zipapp.py | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 docs/changelog/1949.bugfix.rst diff --git a/docs/changelog/1949.bugfix.rst b/docs/changelog/1949.bugfix.rst new file mode 100644 index 000000000..fa77dc9b9 --- /dev/null +++ b/docs/changelog/1949.bugfix.rst @@ -0,0 +1 @@ +``virtualenv.pyz`` no longer fails when zipapp path contains a symlink - by :user:`HandSonic` and :user:`petamas`. diff --git a/src/virtualenv/util/zipapp.py b/src/virtualenv/util/zipapp.py index f736e3763..78cdb9143 100644 --- a/src/virtualenv/util/zipapp.py +++ b/src/virtualenv/util/zipapp.py @@ -23,8 +23,10 @@ def extract(full_path, dest): def _get_path_within_zip(full_path): - full_path = os.path.abspath(str(full_path)) - sub_file = full_path[len(ROOT) + 1 :] + full_path = os.path.realpath(os.path.abspath(str(full_path))) + prefix = ROOT + os.sep + assert full_path.startswith(prefix), f"full_path={full_path} should start with prefix={prefix}" + sub_file = full_path[len(prefix) :] if IS_WIN: # paths are always UNIX separators, even on Windows, though __file__ still follows platform default sub_file = sub_file.replace(os.sep, "/") diff --git a/tests/integration/test_zipapp.py b/tests/integration/test_zipapp.py index 7157a9e75..dfc6d9759 100644 --- a/tests/integration/test_zipapp.py +++ b/tests/integration/test_zipapp.py @@ -9,6 +9,7 @@ from flaky import flaky from virtualenv.discovery.py_info import PythonInfo +from virtualenv.info import fs_supports_symlink from virtualenv.run import cli_run HERE = Path(__file__).parent @@ -83,6 +84,24 @@ def _run(*args): return _run +@pytest.fixture() +def call_zipapp_symlink(zipapp, tmp_path, zipapp_test_env, temp_app_data): # noqa: ARG001 + def _run(*args): + symlinked = zipapp.parent / "symlinked_virtualenv.pyz" + symlinked.symlink_to(str(zipapp)) + cmd = [str(zipapp_test_env), str(symlinked), "-vv", str(tmp_path / "env"), *list(args)] + subprocess.check_call(cmd) + + return _run + + +@pytest.mark.skipif(not fs_supports_symlink(), reason="symlink not supported") +def test_zipapp_in_symlink(capsys, call_zipapp_symlink): + call_zipapp_symlink("--reset-app-data") + _out, err = capsys.readouterr() + assert not err + + @flaky(max_runs=2, min_passes=1) def test_zipapp_help(call_zipapp, capsys): call_zipapp("-h") From 08a82077fb4e463480dae6d940b561533d967352 Mon Sep 17 00:00:00 2001 From: HandSonic <8078023+HandSonic@users.noreply.github.com> Date: Sat, 11 May 2024 23:26:52 +0800 Subject: [PATCH 2/4] fix the pre-commit --- src/virtualenv/util/zipapp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/virtualenv/util/zipapp.py b/src/virtualenv/util/zipapp.py index 78cdb9143..1628e37be 100644 --- a/src/virtualenv/util/zipapp.py +++ b/src/virtualenv/util/zipapp.py @@ -25,7 +25,7 @@ def extract(full_path, dest): def _get_path_within_zip(full_path): full_path = os.path.realpath(os.path.abspath(str(full_path))) prefix = ROOT + os.sep - assert full_path.startswith(prefix), f"full_path={full_path} should start with prefix={prefix}" + assert full_path.startswith(prefix), f"full_path={full_path} should start with prefix={prefix}" # noqa: S101 sub_file = full_path[len(prefix) :] if IS_WIN: # paths are always UNIX separators, even on Windows, though __file__ still follows platform default From 7f3566168e3890f068de832bf898ce7e7092e2b3 Mon Sep 17 00:00:00 2001 From: HandSonic <8078023+HandSonic@users.noreply.github.com> Date: Sun, 12 May 2024 00:52:06 +0800 Subject: [PATCH 3/4] Change assertions to runtime exceptions --- src/virtualenv/util/zipapp.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/virtualenv/util/zipapp.py b/src/virtualenv/util/zipapp.py index 1628e37be..74564ce1f 100644 --- a/src/virtualenv/util/zipapp.py +++ b/src/virtualenv/util/zipapp.py @@ -25,7 +25,9 @@ def extract(full_path, dest): def _get_path_within_zip(full_path): full_path = os.path.realpath(os.path.abspath(str(full_path))) prefix = ROOT + os.sep - assert full_path.startswith(prefix), f"full_path={full_path} should start with prefix={prefix}" # noqa: S101 + if not full_path.startswith(prefix): + msg = f"full_path={full_path} should start with prefix={prefix}." + raise RuntimeError(msg) sub_file = full_path[len(prefix) :] if IS_WIN: # paths are always UNIX separators, even on Windows, though __file__ still follows platform default From 659584ed94a7c08e244a950e1a7fa52355ba206d Mon Sep 17 00:00:00 2001 From: HandSonic <8078023+HandSonic@users.noreply.github.com> Date: Sun, 12 May 2024 01:10:39 +0800 Subject: [PATCH 4/4] Use f-string. --- src/virtualenv/util/zipapp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/virtualenv/util/zipapp.py b/src/virtualenv/util/zipapp.py index 74564ce1f..958db1543 100644 --- a/src/virtualenv/util/zipapp.py +++ b/src/virtualenv/util/zipapp.py @@ -24,7 +24,7 @@ def extract(full_path, dest): def _get_path_within_zip(full_path): full_path = os.path.realpath(os.path.abspath(str(full_path))) - prefix = ROOT + os.sep + prefix = f"{ROOT}{os.sep}" if not full_path.startswith(prefix): msg = f"full_path={full_path} should start with prefix={prefix}." raise RuntimeError(msg)