From 9efe34a2b0c2e4c5c2dea428bae15bfa6e79e37e Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Fri, 26 Jul 2024 18:04:36 +0200 Subject: [PATCH 01/17] python fetch --- src/tox_uv/_venv.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index 8245aa2..28573e6 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -43,12 +43,23 @@ def __init__(self, create_args: ToxEnvCreateArgs) -> None: def register_config(self) -> None: super().register_config() - desc = "add seed packages to the created venv" - self.conf.add_config(keys=["uv_seed"], of_type=bool, default=False, desc=desc) + self.conf.add_config( + keys=["uv_seed"], + of_type=bool, + default=False, + desc="add seed packages to the created venv", + ) + self.conf.add_config( + keys=["python_fetch"], + of_type=str, + default="no", + desc="Whether to automatically download Python when required [possible values: no, automatic, manual]", + ) def python_cache(self) -> dict[str, Any]: result = super().python_cache() result["seed"] = self.conf["uv_seed"] + result["python_fetch"] = self.conf["python_fetch"] result["venv"] = str(self.venv_dir.relative_to(self.env_dir)) return result @@ -154,6 +165,8 @@ def create_python_env(self) -> None: cmd.append("-v") if self.conf["uv_seed"]: cmd.append("--seed") + if self.conf["python_fetch"] != "no": + cmd.extend(["--python-fetch", self.conf["python_fetch"]]) cmd.append(str(self.venv_dir)) outcome = self.execute(cmd, stdin=StdinSource.OFF, run_id="venv", show=None) outcome.assert_success() From 5d5d6dd1035a70558f7c1ae27a034a750d4189db Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Fri, 26 Jul 2024 18:17:00 +0200 Subject: [PATCH 02/17] add uv prefix --- src/tox_uv/_venv.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index 28573e6..7c810ef 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -50,7 +50,7 @@ def register_config(self) -> None: desc="add seed packages to the created venv", ) self.conf.add_config( - keys=["python_fetch"], + keys=["uv_python_fetch"], of_type=str, default="no", desc="Whether to automatically download Python when required [possible values: no, automatic, manual]", @@ -59,7 +59,7 @@ def register_config(self) -> None: def python_cache(self) -> dict[str, Any]: result = super().python_cache() result["seed"] = self.conf["uv_seed"] - result["python_fetch"] = self.conf["python_fetch"] + result["python_fetch"] = self.conf["uv_python_fetch"] result["venv"] = str(self.venv_dir.relative_to(self.env_dir)) return result @@ -165,8 +165,8 @@ def create_python_env(self) -> None: cmd.append("-v") if self.conf["uv_seed"]: cmd.append("--seed") - if self.conf["python_fetch"] != "no": - cmd.extend(["--python-fetch", self.conf["python_fetch"]]) + if self.conf["uv_python_fetch"] != "no": + cmd.extend(["--python-fetch", self.conf["uv_python_fetch"]]) cmd.append(str(self.venv_dir)) outcome = self.execute(cmd, stdin=StdinSource.OFF, run_id="venv", show=None) outcome.assert_success() From 77e4f208249e53da0cd84511503cbd867c12c368 Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Fri, 26 Jul 2024 18:37:09 +0200 Subject: [PATCH 03/17] change python-fetch to python-preference --- src/tox_uv/_venv.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index 7c810ef..d116229 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -50,16 +50,22 @@ def register_config(self) -> None: desc="add seed packages to the created venv", ) self.conf.add_config( - keys=["uv_python_fetch"], + keys=["uv_python_preference"], of_type=str, - default="no", - desc="Whether to automatically download Python when required [possible values: no, automatic, manual]", + default="none", + desc=( + "Whether to prefer using Python installations that are already" + " present on the system, or those that are downloaded and" + " installed by uv [possible values: only-managed, installed," + " managed, system, only-system, none]. Use none to use uv's" + " default." + ), ) def python_cache(self) -> dict[str, Any]: result = super().python_cache() result["seed"] = self.conf["uv_seed"] - result["python_fetch"] = self.conf["uv_python_fetch"] + result["python_preference"] = self.conf["uv_python_preference"] result["venv"] = str(self.venv_dir.relative_to(self.env_dir)) return result @@ -165,8 +171,8 @@ def create_python_env(self) -> None: cmd.append("-v") if self.conf["uv_seed"]: cmd.append("--seed") - if self.conf["uv_python_fetch"] != "no": - cmd.extend(["--python-fetch", self.conf["uv_python_fetch"]]) + if self.conf["uv_python_preference"] != "none": + cmd.extend(["--python-preference", self.conf["uv_python_preference"]]) cmd.append(str(self.venv_dir)) outcome = self.execute(cmd, stdin=StdinSource.OFF, run_id="venv", show=None) outcome.assert_success() From f1d67fd5e1bb81051d0d0a3999b8647d452f6b9a Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Fri, 26 Jul 2024 18:57:37 +0200 Subject: [PATCH 04/17] update README --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 0db7464..4b3bf12 100644 --- a/README.md +++ b/README.md @@ -42,3 +42,14 @@ This is a uv specific feature that may be used as an alternative to frozen const intention is to validate the lower bounds of your dependencies during test executions. [resolution strategy]: https://github.com/astral-sh/uv/blob/0.1.20/README.md#resolution-strategy + +### uv_python_preference + +This flag, set on a tox environement level, controls how uv select the Python +interpreter. + +By default, uv will attempt to use Python versions found on the system and only +download managed interpreters when necessary. However, It's possible to adjust +uv's Python version selection preference with the +[python-preference](https://docs.astral.sh/uv/python-versions/#adjusting-python-version-preferences) +option. From ad7f2af675ad5faf5c7a1bf3e8277ed3ff85567a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:58:34 +0000 Subject: [PATCH 05/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b3bf12..141d248 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ intention is to validate the lower bounds of your dependencies during test execu ### uv_python_preference -This flag, set on a tox environement level, controls how uv select the Python +This flag, set on a tox environment level, controls how uv select the Python interpreter. By default, uv will attempt to use Python versions found on the system and only From 5405df71a1e69f8351f66756c8b30aa1cd816fe3 Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Fri, 26 Jul 2024 19:08:11 +0200 Subject: [PATCH 06/17] add test --- tests/test_tox_uv_venv.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_tox_uv_venv.py b/tests/test_tox_uv_venv.py index fc5f9ef..36da3b2 100644 --- a/tests/test_tox_uv_venv.py +++ b/tests/test_tox_uv_venv.py @@ -154,6 +154,25 @@ def test_uv_env_python(tox_project: ToxProjectCreator) -> None: assert env_bin_dir in result.out +def test_uv_env_python_preference(tox_project: ToxProjectCreator) -> None: + project = tox_project( + { + "tox.ini": ( + "[testenv]\n" + "package=skip\n" + "uv_python_preference=only-managed\n" + "commands=python -c 'print(\"{env_python}\")'" + ) + } + ) + result = project.run("-vv") + result.assert_success() + + exe = "python.exe" if sys.platform == "win32" else "python" + env_bin_dir = str(project.path / ".tox" / "py" / ("Scripts" if sys.platform == "win32" else "bin") / exe) + assert env_bin_dir in result.out + + def test_uv_env_site_package_dir_run(tox_project: ToxProjectCreator) -> None: project = tox_project({"tox.ini": "[testenv]\npackage=skip\ncommands=python -c 'print(\"{envsitepackagesdir}\")'"}) result = project.run("-vv") From b9a20467bced275a3d24f0721588bf77b303e9df Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:09:11 +0000 Subject: [PATCH 07/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_tox_uv_venv.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/test_tox_uv_venv.py b/tests/test_tox_uv_venv.py index 36da3b2..487cf21 100644 --- a/tests/test_tox_uv_venv.py +++ b/tests/test_tox_uv_venv.py @@ -155,16 +155,14 @@ def test_uv_env_python(tox_project: ToxProjectCreator) -> None: def test_uv_env_python_preference(tox_project: ToxProjectCreator) -> None: - project = tox_project( - { - "tox.ini": ( - "[testenv]\n" - "package=skip\n" - "uv_python_preference=only-managed\n" - "commands=python -c 'print(\"{env_python}\")'" - ) - } - ) + project = tox_project({ + "tox.ini": ( + "[testenv]\n" + "package=skip\n" + "uv_python_preference=only-managed\n" + "commands=python -c 'print(\"{env_python}\")'" + ) + }) result = project.run("-vv") result.assert_success() From aab348c921a2e9f72726f05fa8be2e5f5118ac4f Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Sun, 28 Jul 2024 20:04:05 +0200 Subject: [PATCH 08/17] use literal type --- src/tox_uv/_venv.py | 15 ++++++++++----- tests/test_tox_uv_venv.py | 12 ++++++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index d116229..12d1b72 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -16,7 +16,7 @@ from pathlib import Path from platform import python_implementation -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, Literal, Optional, cast from tox.execute.local_sub_process import LocalSubProcessExecutor from tox.execute.request import StdinSource @@ -34,6 +34,11 @@ from tox.tox_env.installer import Installer +PythonPreference = Literal[ + "only-managed", "installed", "managed", "system", "only-system" +] + + class UvVenv(Python, ABC): def __init__(self, create_args: ToxEnvCreateArgs) -> None: self._executor: Execute | None = None @@ -51,13 +56,13 @@ def register_config(self) -> None: ) self.conf.add_config( keys=["uv_python_preference"], - of_type=str, - default="none", + of_type=Optional[PythonPreference], + default=None, desc=( "Whether to prefer using Python installations that are already" " present on the system, or those that are downloaded and" " installed by uv [possible values: only-managed, installed," - " managed, system, only-system, none]. Use none to use uv's" + " managed, system, only-system]. Use none to use uv's" " default." ), ) @@ -171,7 +176,7 @@ def create_python_env(self) -> None: cmd.append("-v") if self.conf["uv_seed"]: cmd.append("--seed") - if self.conf["uv_python_preference"] != "none": + if self.conf["uv_python_preference"]: cmd.extend(["--python-preference", self.conf["uv_python_preference"]]) cmd.append(str(self.venv_dir)) outcome = self.execute(cmd, stdin=StdinSource.OFF, run_id="venv", show=None) diff --git a/tests/test_tox_uv_venv.py b/tests/test_tox_uv_venv.py index 487cf21..18a4da1 100644 --- a/tests/test_tox_uv_venv.py +++ b/tests/test_tox_uv_venv.py @@ -154,12 +154,20 @@ def test_uv_env_python(tox_project: ToxProjectCreator) -> None: assert env_bin_dir in result.out -def test_uv_env_python_preference(tox_project: ToxProjectCreator) -> None: +@pytest.mark.parametrize( + "preference", + ["only-managed", "installed", "managed", "system", "only-system"], +) +def test_uv_env_python_preference( + tox_project: ToxProjectCreator, + *, + preference: str, +) -> None: project = tox_project({ "tox.ini": ( "[testenv]\n" "package=skip\n" - "uv_python_preference=only-managed\n" + f"uv_python_preference={preference}\n" "commands=python -c 'print(\"{env_python}\")'" ) }) From aadfeea05549093979599c9c8d00090b55baa94d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 28 Jul 2024 18:09:36 +0000 Subject: [PATCH 09/17] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/tox_uv/_venv.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index 12d1b72..dd82691 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -34,9 +34,7 @@ from tox.tox_env.installer import Installer -PythonPreference = Literal[ - "only-managed", "installed", "managed", "system", "only-system" -] +PythonPreference = Literal["only-managed", "installed", "managed", "system", "only-system"] class UvVenv(Python, ABC): From 9f3a2ff18bc38f85bf4d9f8bc1e911355f01f46a Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Sun, 28 Jul 2024 20:14:15 +0200 Subject: [PATCH 10/17] use literal type --- src/tox_uv/_venv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index dd82691..153f23c 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -16,7 +16,7 @@ from pathlib import Path from platform import python_implementation -from typing import TYPE_CHECKING, Any, Literal, Optional, cast +from typing import TYPE_CHECKING, Any, Literal, Union, cast from tox.execute.local_sub_process import LocalSubProcessExecutor from tox.execute.request import StdinSource @@ -54,7 +54,7 @@ def register_config(self) -> None: ) self.conf.add_config( keys=["uv_python_preference"], - of_type=Optional[PythonPreference], + of_type=Union[PythonPreference, None], default=None, desc=( "Whether to prefer using Python installations that are already" From 2b99d79b8adf4e916553232805b6bb3088243e8d Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Mon, 29 Jul 2024 11:16:25 +0200 Subject: [PATCH 11/17] CI should pass --- src/tox_uv/_venv.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index 153f23c..b91676c 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -16,7 +16,7 @@ from pathlib import Path from platform import python_implementation -from typing import TYPE_CHECKING, Any, Literal, Union, cast +from typing import TYPE_CHECKING, Any, Literal, Optional, TypeAlias, cast from tox.execute.local_sub_process import LocalSubProcessExecutor from tox.execute.request import StdinSource @@ -34,7 +34,7 @@ from tox.tox_env.installer import Installer -PythonPreference = Literal["only-managed", "installed", "managed", "system", "only-system"] +PythonPreference: TypeAlias = Literal["only-managed", "installed", "managed", "system", "only-system"] class UvVenv(Python, ABC): @@ -52,9 +52,11 @@ def register_config(self) -> None: default=False, desc="add seed packages to the created venv", ) + # The cast(...) might seems superfluous but removing it + # makes mypy crash. The problem is probably on tox's typing side self.conf.add_config( keys=["uv_python_preference"], - of_type=Union[PythonPreference, None], + of_type=cast(type[Optional[PythonPreference]], Optional[PythonPreference]), default=None, desc=( "Whether to prefer using Python installations that are already" From 91bac72439fa366496ea5a292b3353496ae77da9 Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Mon, 29 Jul 2024 17:28:21 +0200 Subject: [PATCH 12/17] remove type alias --- src/tox_uv/_venv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index b91676c..21c9a52 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -16,7 +16,7 @@ from pathlib import Path from platform import python_implementation -from typing import TYPE_CHECKING, Any, Literal, Optional, TypeAlias, cast +from typing import TYPE_CHECKING, Any, Literal, Optional, cast from tox.execute.local_sub_process import LocalSubProcessExecutor from tox.execute.request import StdinSource @@ -34,7 +34,7 @@ from tox.tox_env.installer import Installer -PythonPreference: TypeAlias = Literal["only-managed", "installed", "managed", "system", "only-system"] +PythonPreference = Literal["only-managed", "installed", "managed", "system", "only-system"] class UvVenv(Python, ABC): From 8af497ba457bec90673e42486bb7d478c7d4ffb2 Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Mon, 29 Jul 2024 17:31:08 +0200 Subject: [PATCH 13/17] use literal type --- src/tox_uv/_venv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index 21c9a52..8444b1f 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -56,7 +56,7 @@ def register_config(self) -> None: # makes mypy crash. The problem is probably on tox's typing side self.conf.add_config( keys=["uv_python_preference"], - of_type=cast(type[Optional[PythonPreference]], Optional[PythonPreference]), + of_type=cast(type, Optional[PythonPreference]), default=None, desc=( "Whether to prefer using Python installations that are already" From 56ee3f8df88620a3cb71ee2ab2134c95b2f8efc2 Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Mon, 29 Jul 2024 17:53:27 +0200 Subject: [PATCH 14/17] typing extensions --- pyproject.toml | 3 +++ src/tox_uv/_venv.py | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7e7d359..58044af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ dependencies = [ "importlib-resources>=6.4; python_version<'3.9'", "packaging>=24", "tox<5,>=4.15", + "typing-extensions>=4.12.2", "uv<1,>=0.2.5", ] optional-dependencies.test = [ @@ -51,6 +52,8 @@ optional-dependencies.test = [ "pytest>=8.2.1", "pytest-cov>=5", "pytest-mock>=3.14", + "pytest-xdist>=3.6.1", + "xdoctest>=1.1.5", ] urls.Changelog = "https://github.com/tox-dev/tox-uv/releases" urls.Documentation = "https://github.com/tox-dev/tox-uv#tox-uv" diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index 40e5c01..5476940 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -16,12 +16,13 @@ from pathlib import Path from platform import python_implementation -from typing import TYPE_CHECKING, Any, Literal, Optional, cast +from typing import TYPE_CHECKING, Any, Literal, Optional, Type, cast from tox.execute.local_sub_process import LocalSubProcessExecutor from tox.execute.request import StdinSource from tox.tox_env.errors import Skip from tox.tox_env.python.api import Python, PythonInfo, VersionInfo +from typing_extensions import TypeAlias from uv import find_uv_bin from virtualenv import app_data from virtualenv.discovery import cached_py_info @@ -35,7 +36,7 @@ from tox.tox_env.installer import Installer -PythonPreference = Literal["only-managed", "installed", "managed", "system", "only-system"] +PythonPreference: TypeAlias = Literal["only-managed", "installed", "managed", "system", "only-system"] class UvVenv(Python, ABC): @@ -57,7 +58,7 @@ def register_config(self) -> None: # makes mypy crash. The problem is probably on tox's typing side self.conf.add_config( keys=["uv_python_preference"], - of_type=cast(type, Optional[PythonPreference]), + of_type=cast(Type[Optional[PythonPreference]], Optional[PythonPreference]), default=None, desc=( "Whether to prefer using Python installations that are already" From 34735e36e8231b53d17c9dc0da29789995a1d906 Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Mon, 29 Jul 2024 17:53:45 +0200 Subject: [PATCH 15/17] clean deps --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 58044af..d7357d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,8 +52,6 @@ optional-dependencies.test = [ "pytest>=8.2.1", "pytest-cov>=5", "pytest-mock>=3.14", - "pytest-xdist>=3.6.1", - "xdoctest>=1.1.5", ] urls.Changelog = "https://github.com/tox-dev/tox-uv/releases" urls.Documentation = "https://github.com/tox-dev/tox-uv#tox-uv" From 653a53cf224ed7bf0bacc00e2e53facf252d37aa Mon Sep 17 00:00:00 2001 From: Romain Brault Date: Mon, 29 Jul 2024 18:15:45 +0200 Subject: [PATCH 16/17] conditional typing_extensions --- src/tox_uv/_venv.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index 5476940..2b36d76 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -22,7 +22,12 @@ from tox.execute.request import StdinSource from tox.tox_env.errors import Skip from tox.tox_env.python.api import Python, PythonInfo, VersionInfo -from typing_extensions import TypeAlias + +if sys.version_info >= (3, 10): # pragma: no cover (py310+) + from typing import TypeAlias +else: # pragma: no cover ( Date: Mon, 29 Jul 2024 18:17:55 +0200 Subject: [PATCH 17/17] conditional typing_extensions --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d7357d2..84e7c0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ dependencies = [ "importlib-resources>=6.4; python_version<'3.9'", "packaging>=24", "tox<5,>=4.15", - "typing-extensions>=4.12.2", + "typing-extensions>=4.12.2; python_version<'3.10'", "uv<1,>=0.2.5", ] optional-dependencies.test = [