diff --git a/src/tox_uv/_run_lock.py b/src/tox_uv/_run_lock.py index 374737b..2846be9 100644 --- a/src/tox_uv/_run_lock.py +++ b/src/tox_uv/_run_lock.py @@ -60,7 +60,13 @@ def register_config(self) -> None: def _setup_env(self) -> None: super()._setup_env() - cmd = ["uv", "sync", "--frozen"] + cmd = [ + "uv", + "sync", + "--frozen", + ] + if self.conf["uv_python_preference"] != "none": + cmd.extend(("--python-preference", self.conf["uv_python_preference"])) for extra in cast("set[str]", sorted(self.conf["extras"])): cmd.extend(("--extra", extra)) if not self.conf["with_dev"]: diff --git a/src/tox_uv/_venv.py b/src/tox_uv/_venv.py index f54bb96..4734ca1 100644 --- a/src/tox_uv/_venv.py +++ b/src/tox_uv/_venv.py @@ -35,6 +35,7 @@ PythonPreference: TypeAlias = Literal[ + "none", "only-managed", "managed", "system", @@ -70,20 +71,24 @@ def register_config(self) -> None: self.conf.add_config( keys=["uv_python_preference"], of_type=cast("Type[Optional[PythonPreference]]", Optional[PythonPreference]), # noqa: UP006 - default=None, + default="system", 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," + " installed by uv [possible values: none, only-managed, installed," " managed, system, only-system]. Use none to use uv's" - " default." + " default. Our default value is 'system', while uv's default" + " value is 'managed' because we prefer using same python" + " interpreters with all tox environments and avoid accidental" + " downloading of other interpreters." ), ) def python_cache(self) -> dict[str, Any]: result = super().python_cache() result["seed"] = self.conf["uv_seed"] - result["python_preference"] = self.conf["uv_python_preference"] + if self.conf["uv_python_preference"] != "none": + result["python_preference"] = self.conf["uv_python_preference"] result["venv"] = str(self.venv_dir.relative_to(self.env_dir)) return result @@ -207,7 +212,7 @@ def create_python_env(self) -> None: cmd.append("--seed") if self.conf["system_site_packages"]: cmd.append("--system-site-packages") - if self.conf["uv_python_preference"]: + 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) diff --git a/tests/test_tox_uv_lock.py b/tests/test_tox_uv_lock.py index 1539d88..992fc85 100644 --- a/tests/test_tox_uv_lock.py +++ b/tests/test_tox_uv_lock.py @@ -31,13 +31,41 @@ def test_uv_lock_list_dependencies_command(tox_project: ToxProjectCreator) -> No ( "py", "venv", - [uv, "venv", "-p", sys.executable, "--allow-existing", "-v", str(project.path / ".tox" / "py")], + [ + uv, + "venv", + "-p", + sys.executable, + "--allow-existing", + "-v", + "--python-preference", + "system", + str(project.path / ".tox" / "py"), + ], + ), + ( + "py", + "uv-sync", + [ + "uv", + "sync", + "--frozen", + "--python-preference", + "system", + "--extra", + "dev", + "--extra", + "type", + "--no-dev", + "-v", + ], ), - ("py", "uv-sync", ["uv", "sync", "--frozen", "--extra", "dev", "--extra", "type", "--no-dev", "-v"]), ("py", "freeze", [uv, "--color", "never", "pip", "freeze"]), ("py", "commands[0]", ["python", "hello"]), ] - assert calls == expected + assert len(calls) == len(expected) + for i in range(len(calls)): + assert calls[i] == expected[i] @pytest.mark.parametrize("verbose", ["", "-v", "-vv", "-vvv"]) @@ -63,9 +91,35 @@ def test_uv_lock_command(tox_project: ToxProjectCreator, verbose: str) -> None: ( "py", "venv", - [uv, "venv", "-p", sys.executable, "--allow-existing", *v_args, str(project.path / ".tox" / "py")], + [ + uv, + "venv", + "-p", + sys.executable, + "--allow-existing", + *v_args, + "--python-preference", + "system", + str(project.path / ".tox" / "py"), + ], + ), + ( + "py", + "uv-sync", + [ + "uv", + "sync", + "--frozen", + "--python-preference", + "system", + "--extra", + "dev", + "--extra", + "type", + "--no-dev", + *v_args, + ], ), - ("py", "uv-sync", ["uv", "sync", "--frozen", "--extra", "dev", "--extra", "type", "--no-dev", *v_args]), ("py", "commands[0]", ["python", "hello"]), ] assert calls == expected @@ -91,9 +145,19 @@ def test_uv_lock_with_dev(tox_project: ToxProjectCreator) -> None: ( "py", "venv", - [uv, "venv", "-p", sys.executable, "--allow-existing", "-v", str(project.path / ".tox" / "py")], + [ + uv, + "venv", + "-p", + sys.executable, + "--allow-existing", + "-v", + "--python-preference", + "system", + str(project.path / ".tox" / "py"), + ], ), - ("py", "uv-sync", ["uv", "sync", "--frozen", "-v"]), + ("py", "uv-sync", ["uv", "sync", "--frozen", "--python-preference", "system", "-v"]), ] assert calls == expected @@ -124,9 +188,23 @@ def test_uv_lock_with_install_pkg(tox_project: ToxProjectCreator, name: str) -> ( "py", "venv", - [uv, "venv", "-p", sys.executable, "--allow-existing", "-v", str(project.path / ".tox" / "py")], + [ + uv, + "venv", + "-p", + sys.executable, + "--allow-existing", + "-v", + "--python-preference", + "system", + str(project.path / ".tox" / "py"), + ], + ), + ( + "py", + "uv-sync", + ["uv", "sync", "--frozen", "--python-preference", "system", "--no-dev", "--no-install-project", "-v"], ), - ("py", "uv-sync", ["uv", "sync", "--frozen", "--no-dev", "--no-install-project", "-v"]), ( "py", "install_external", @@ -157,12 +235,21 @@ def test_uv_sync_extra_flags(tox_project: ToxProjectCreator) -> None: ( "py", "venv", - [uv, "venv", "-p", sys.executable, "--allow-existing", str(project.path / ".tox" / "py")], + [ + uv, + "venv", + "-p", + sys.executable, + "--allow-existing", + "--python-preference", + "system", + str(project.path / ".tox" / "py"), + ], ), ( "py", "uv-sync", - ["uv", "sync", "--frozen", "--no-editable", "--inexact"], + ["uv", "sync", "--frozen", "--python-preference", "system", "--no-editable", "--inexact"], ), ("py", "commands[0]", ["python", "hello"]), ] @@ -190,12 +277,21 @@ def test_uv_sync_extra_flags_toml(tox_project: ToxProjectCreator) -> None: ( "py", "venv", - [uv, "venv", "-p", sys.executable, "--allow-existing", str(project.path / ".tox" / "py")], + [ + uv, + "venv", + "-p", + sys.executable, + "--allow-existing", + "--python-preference", + "system", + str(project.path / ".tox" / "py"), + ], ), ( "py", "uv-sync", - ["uv", "sync", "--frozen", "--no-editable", "--inexact"], + ["uv", "sync", "--frozen", "--python-preference", "system", "--no-editable", "--inexact"], ), ("py", "commands[0]", ["python", "hello"]), ] @@ -223,12 +319,72 @@ def test_uv_sync_dependency_groups(tox_project: ToxProjectCreator) -> None: ( "py", "venv", - [uv, "venv", "-p", sys.executable, "--allow-existing", str(project.path / ".tox" / "py")], + [ + uv, + "venv", + "-p", + sys.executable, + "--allow-existing", + "--python-preference", + "system", + str(project.path / ".tox" / "py"), + ], + ), + ( + "py", + "uv-sync", + ["uv", "sync", "--frozen", "--python-preference", "system", "--group", "test", "--group", "type"], + ), + ("py", "commands[0]", ["python", "hello"]), + ] + assert calls == expected + + +@pytest.mark.parametrize( + ("uv_python_preference", "injected"), + [ + pytest.param("none", [], id="on"), + pytest.param("system", ["--python-preference", "system"], id="off"), + ], +) +def test_uv_sync_uv_python_preference( + tox_project: ToxProjectCreator, uv_python_preference: str, injected: list[str] +) -> None: + project = tox_project({ + "tox.toml": f""" + [env_run_base] + runner = "uv-venv-lock-runner" + with_dev = true + dependency_groups = ["test", "type"] + commands = [["python", "hello"]] + uv_python_preference = "{uv_python_preference}" + """ + }) + execute_calls = project.patch_execute(lambda r: 0 if r.run_id != "venv" else None) + result = project.run() + result.assert_success() + + calls = [(i[0][0].conf.name, i[0][3].run_id, i[0][3].cmd) for i in execute_calls.call_args_list] + uv = find_uv_bin() + + expected = [ + ( + "py", + "venv", + [ + uv, + "venv", + "-p", + sys.executable, + "--allow-existing", + *injected, + str(project.path / ".tox" / "py"), + ], ), ( "py", "uv-sync", - ["uv", "sync", "--frozen", "--group", "test", "--group", "type"], + ["uv", "sync", "--frozen", *injected, "--group", "test", "--group", "type"], ), ("py", "commands[0]", ["python", "hello"]), ]