From 04363ff01a1a82c61bc814b3d4cf0eb533621ffe Mon Sep 17 00:00:00 2001 From: Peter Pentchev Date: Sat, 16 Mar 2024 05:16:50 +0200 Subject: [PATCH] Pass sys.executable to `uv venv` if it is the same version (#40) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Pass sys.executable to `uv venv` if it is the same version Support the case when pythonX.Y is not in the search path, but it corresponds to the currently running Python interpreter. This may happen if e.g. Tox is installed (along with tox-uv) in a virtual environment that has been created with a Python interpreter coming from a non-standard location, and then `tox` is invoked directly from the virtual environment's `bin/` directory: [roam@straylight ~]$ mkdir ~/not-in-path [roam@straylight ~]$ uv venv -p /opt/some-vendor/python3/bin/python3 ~/not-in-path/venv Using Python 3.8.17 interpreter at: /opt/some-vendor/python3/bin/python3 Creating virtualenv at: /home/roam/not-in-path/venv Activate with: source /home/roam/not-in-path/venv/bin/activate [roam@straylight ~]$ (set -e; . ~/not-in-path/venv/bin/activate; uv pip install tox tox-uv) ...snip... [roam@straylight ~]$ printf -- '%s\n' '[testenv]' 'package=skip' 'commands=python3 -c "print()"' > ~/not-in-path/tox.ini [roam@straylight ~]$ ~/not-in-path/venv/bin/tox -c ~/not-in-path/tox.ini py: venv /home/roam/not-in-path> venv/bin/uv venv -p 3.8 /home/roam/not-in-path/.tox/py/.venv × No Python 3.8 in `PATH`. Is Python 3.8 installed? py: exit 1 (0.01 seconds) /home/roam/not-in-path> venv/bin/uv venv -p 3.8 /home/roam/not-in-path/.tox/py/.venv pid=1059253 py: FAIL code 1 (0.02 seconds) evaluation failed :( (0.11 seconds) [roam@straylight ~]$ * Let Ruff know that we try to use subprocess responsibly * Tox is certainly installed during the test suite run --- src/tox_uv/_venv.py | 8 +++++++- tests/test_tox_uv_venv.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index 9e76c4d..261d4f7 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -113,7 +113,13 @@ def _default_pass_env(self) -> list[str]: def create_python_env(self) -> None: base = self.base_python.version_info - version_spec = f"{base.major}.{base.minor}" if base.minor else f"{base.major}" + version_spec = ( + sys.executable + if (base.major, base.minor) == sys.version_info[:2] + else f"{base.major}.{base.minor}" + if base.minor + else f"{base.major}" + ) cmd: list[str] = [self.uv, "venv", "-p", version_spec] if self.options.verbosity > 2: # noqa: PLR2004 cmd.append("-v") diff --git a/tests/test_tox_uv_venv.py b/tests/test_tox_uv_venv.py index 1186642..8a67432 100644 --- a/tests/test_tox_uv_venv.py +++ b/tests/test_tox_uv_venv.py @@ -1,5 +1,10 @@ from __future__ import annotations +import importlib.util +import os +import os.path +import pathlib +import subprocess # noqa: S404 import sys from configparser import ConfigParser from typing import TYPE_CHECKING @@ -86,3 +91,28 @@ def test_uv_env_site_package_dir(tox_project: ToxProjectCreator) -> None: else: # pragma: win32 no cover path = str(env_dir / "lib" / f"python{ver.major}.{ver.minor}" / "site-packages") assert path in result.out + + +def test_uv_env_python_not_in_path(tox_project: ToxProjectCreator) -> None: + # Make sure there is no pythonX.Y in the search path + ver = sys.version_info + exe_ext = ".exe" if sys.platform == "win32" else "" + python_exe = f"python{ver.major}.{ver.minor}{exe_ext}" + env = dict(os.environ) + env["PATH"] = os.path.pathsep.join( + path for path in env["PATH"].split(os.path.pathsep) if not (pathlib.Path(path) / python_exe).is_file() + ) + + # Make sure the Python interpreter can find our Tox module + tox_spec = importlib.util.find_spec("tox") + assert tox_spec is not None + tox_lines = subprocess.check_output( + [sys.executable, "-c", "import tox; print(tox.__file__);"], encoding="UTF-8", env=env + ).splitlines() + assert tox_lines == [tox_spec.origin] + + # Now use that Python interpreter to run Tox + project = tox_project({"tox.ini": "[testenv]\npackage=skip\ncommands=python -c 'print(\"{env_python}\")'"}) + tox_ini = project.path / "tox.ini" + assert tox_ini.is_file() + subprocess.check_call([sys.executable, "-m", "tox", "-c", tox_ini], env=env)