Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python preference #70

Merged
merged 18 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 environment 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.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dependencies = [
"importlib-resources>=6.4; python_version<'3.9'",
"packaging>=24",
"tox<5,>=4.15",
"typing-extensions>=4.12.2; python_version<'3.10'",
"uv<1,>=0.2.5",
]
optional-dependencies.test = [
Expand Down
36 changes: 33 additions & 3 deletions src/tox_uv/_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@

from pathlib import Path
from platform import python_implementation
from typing import TYPE_CHECKING, Any, 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

if sys.version_info >= (3, 10): # pragma: no cover (py310+)
from typing import TypeAlias
else: # pragma: no cover (<py310)
from typing_extensions import TypeAlias

from uv import find_uv_bin
from virtualenv import app_data
from virtualenv.discovery import cached_py_info
Expand All @@ -35,6 +41,9 @@
from tox.tox_env.installer import Installer


PythonPreference: TypeAlias = Literal["only-managed", "installed", "managed", "system", "only-system"]


class UvVenv(Python, ABC):
def __init__(self, create_args: ToxEnvCreateArgs) -> None:
self._executor: Execute | None = None
Expand All @@ -44,12 +53,31 @@ 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",
)
# 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=cast(Type[Optional[PythonPreference]], 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]. 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_preference"] = self.conf["uv_python_preference"]
result["venv"] = str(self.venv_dir.relative_to(self.env_dir))
return result

Expand Down Expand Up @@ -158,6 +186,8 @@ def create_python_env(self) -> None:
cmd.append("-v")
if self.conf["uv_seed"]:
cmd.append("--seed")
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)

Expand Down
25 changes: 25 additions & 0 deletions tests/test_tox_uv_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,31 @@ def test_uv_env_python(tox_project: ToxProjectCreator) -> None:
assert env_bin_dir in result.out


@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"
f"uv_python_preference={preference}\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")
Expand Down
Loading