Skip to content

Commit

Permalink
Handle architecture configuration in base_python on Windows (#178)
Browse files Browse the repository at this point in the history
* Handle architecture configuration in base_python on Windows

* Fix race condition in test with sys.executable

---------

Co-authored-by: schlaich <marc.schlaich@hermle.de>
  • Loading branch information
schlamar and schlaich authored Feb 19, 2025
1 parent 36fec8a commit 93528aa
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 3 deletions.
12 changes: 10 additions & 2 deletions src/tox_uv/_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def _get_python(self, base_python: list[str]) -> PythonInfo | None: # noqa: PLR
version=str(spec),
is_64=spec.architecture == 64, # noqa: PLR2004
platform=sys.platform,
extra={},
extra={"architecture": spec.architecture},
)

return None # pragma: no cover
Expand Down Expand Up @@ -255,16 +255,24 @@ def env_version_spec(self) -> str:
base = self.base_python.version_info
imp = self.base_python.impl_lower
executable = self.base_python.extra.get("executable")
architecture = self.base_python.extra.get("architecture")
if executable:
version_spec = str(executable)
elif (base.major, base.minor) == sys.version_info[:2] and (sys.implementation.name.lower() == imp):
elif (
architecture is None
and (base.major, base.minor) == sys.version_info[:2]
and (sys.implementation.name.lower() == imp)
):
version_spec = sys.executable
else:
uv_imp = imp or ""
if not base.major:
version_spec = f"{uv_imp}"
elif not base.minor:
version_spec = f"{uv_imp}{base.major}"
elif architecture is not None and self.base_python.platform == "win32":
uv_arch = {32: "x86", 64: "x86_64"}[architecture]
version_spec = f"{uv_imp}-{base.major}.{base.minor}-windows-{uv_arch}-none"
else:
version_spec = f"{uv_imp}{base.major}.{base.minor}"
return version_spec
Expand Down
92 changes: 91 additions & 1 deletion tests/test_tox_uv_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
from configparser import ConfigParser
from importlib.metadata import version
from typing import TYPE_CHECKING, get_args
from unittest import mock

import pytest
import tox.tox_env.errors
from tox.tox_env.python.api import PythonInfo, VersionInfo

from tox_uv._venv import PythonPreference
from tox_uv._venv import PythonPreference, UvVenv

if TYPE_CHECKING:
from tox.pytest import ToxProjectCreator
Expand Down Expand Up @@ -420,3 +422,91 @@ def test_uv_pip_constraints_no(tox_project: ToxProjectCreator) -> None:
"Found PIP_CONSTRAINTS defined, you may want to also define UV_CONSTRAINT to match pip behavior."
not in result.out
)


class _TestUvVenv(UvVenv):
@staticmethod
def id() -> str:
return "uv-venv-test" # pragma: no cover

def set_base_python(self, python_info: PythonInfo) -> None:
self._base_python_searched = True
self._base_python = python_info

def get_python_info(self, base_python: str) -> PythonInfo | None:
return self._get_python([base_python])


@pytest.mark.parametrize(
("base_python", "architecture"), [("python3.11", None), ("python3.11-32", 32), ("python3.11-64", 64)]
)
def test_get_python_architecture(base_python: str, architecture: int | None) -> None:
uv_venv = _TestUvVenv(create_args=mock.Mock())
python_info = uv_venv.get_python_info(base_python)
assert python_info is not None
assert python_info.extra["architecture"] == architecture


def test_env_version_spec_no_architecture() -> None:
uv_venv = _TestUvVenv(create_args=mock.MagicMock())
python_info = PythonInfo(
implementation="cpython",
version_info=VersionInfo(
major=3,
minor=11,
micro=9,
releaselevel="",
serial=0,
),
version="",
is_64=True,
platform="win32",
extra={"architecture": None},
)
uv_venv.set_base_python(python_info)
with mock.patch("sys.version_info", (0, 0, 0)): # prevent picking sys.executable
assert uv_venv.env_version_spec() == "cpython3.11"


@pytest.mark.parametrize("architecture", [32, 64])
def test_env_version_spec_architecture_configured(architecture: int) -> None:
uv_venv = _TestUvVenv(create_args=mock.MagicMock())
python_info = PythonInfo(
implementation="cpython",
version_info=VersionInfo(
major=3,
minor=11,
micro=9,
releaselevel="",
serial=0,
),
version="",
is_64=architecture == 64,
platform="win32",
extra={"architecture": architecture},
)
uv_venv.set_base_python(python_info)
uv_arch = {32: "x86", 64: "x86_64"}[architecture]
assert uv_venv.env_version_spec() == f"cpython-3.11-windows-{uv_arch}-none"


@pytest.mark.skipif(sys.platform != "win32", reason="architecture configuration only on Windows")
def test_env_version_spec_architecture_configured_overwrite_sys_exe() -> None: # pragma: win32 cover
uv_venv = _TestUvVenv(create_args=mock.MagicMock())
(major, minor) = sys.version_info[:2]
python_info = PythonInfo(
implementation="cpython",
version_info=VersionInfo(
major=major,
minor=minor,
micro=0,
releaselevel="",
serial=0,
),
version="",
is_64=False,
platform="win32",
extra={"architecture": 32},
)
uv_venv.set_base_python(python_info)
assert uv_venv.env_version_spec() == f"cpython-{major}.{minor}-windows-x86-none"

0 comments on commit 93528aa

Please sign in to comment.