From ef418319e76affd8bea7c7c8737bf075f7c4f41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 15 Aug 2024 16:50:35 +0200 Subject: [PATCH 1/4] Fixup the spec string for sys.executable The previous spec string was: namespace(name='cpython', cache_tag='cpython-313', version=sys.version_info(major=3, minor=13, micro=0, releaselevel='candidate', serial=1), hexversion=51183809, _multiarch='x86_64-linux-gnu')313-True When it was supposed to be: cpython313-64 Fixes https://github.com/tox-dev/tox/pull/3325/files#r1718230254 Fixes https://github.com/tox-dev/tox/pull/3325/files#r1718246292 Adds tests for a new method. --- src/tox/tox_env/python/api.py | 11 ++++++++-- tests/tox_env/python/test_python_api.py | 27 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/tox/tox_env/python/api.py b/src/tox/tox_env/python/api.py index d27ca45d9..003014317 100644 --- a/src/tox/tox_env/python/api.py +++ b/src/tox/tox_env/python/api.py @@ -157,6 +157,14 @@ def extract_base_python(cls, env_name: str) -> str | None: return next(iter(candidates)) return None + @classmethod + def python_spec_for_sys_executable(cls) -> PythonSpec: + implementation = sys.implementation.name + version = sys.version_info + bits = "64" if sys.maxsize > 2**32 else "32" + string_spec = f"{implementation}{version.major}{version.minor}-{bits}" + return PythonSpec.from_string_spec(string_spec) + @classmethod def _validate_base_python( cls, @@ -172,8 +180,7 @@ def _validate_base_python( if spec_base.path is not None: path = Path(spec_base.path).absolute() if str(spec_base.path) == sys.executable: - ver, is_64 = sys.version_info, sys.maxsize != 2**32 - spec_base = PythonSpec.from_string_spec(f"{sys.implementation}{ver.major}{ver.minor}-{is_64}") + spec_base = cls.python_spec_for_sys_executable() else: spec_base = cls.python_spec_for_path(path) if any( diff --git a/tests/tox_env/python/test_python_api.py b/tests/tox_env/python/test_python_api.py index 8690cc838..02eccd4b1 100644 --- a/tests/tox_env/python/test_python_api.py +++ b/tests/tox_env/python/test_python_api.py @@ -1,6 +1,7 @@ from __future__ import annotations import sys +from types import SimpleNamespace from typing import TYPE_CHECKING, Callable from unittest.mock import patch @@ -289,3 +290,29 @@ def test_usedevelop_with_nonexistent_basepython(tox_project: ToxProjectCreator) project = tox_project({"tox.ini": ini}) result = project.run() assert result.code == 0 + + +@pytest.mark.parametrize( + ("impl", "major", "minor", "arch"), + [ + ("cpython", 3, 12, 64), + ("pypy", 3, 9, 32), + ], +) +def test_python_spec_for_sys_executable(impl: str, major: int, minor: int, arch: int, mocker: MockerFixture) -> None: + version_info = SimpleNamespace(major=major, minor=minor, micro=5, releaselevel="final", serial=0) + implementation = SimpleNamespace( + name=impl, + cache_tag=f"{impl}-{major}{minor}", + version=version_info, + hexversion=..., + _multiarch=..., + ) + mocker.patch.object(sys, "version_info", version_info) + mocker.patch.object(sys, "implementation", implementation) + mocker.patch.object(sys, "maxsize", 2**arch // 2 - 1) + spec = Python.python_spec_for_sys_executable() + assert spec.implementation == impl + assert spec.major == major + assert spec.minor == minor + assert spec.architecture == arch From 077c06cffc24ef53f9f1009e1a9ad1306c3407e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Sat, 17 Aug 2024 16:30:12 +0200 Subject: [PATCH 2/4] Make the new method private --- src/tox/tox_env/python/api.py | 4 ++-- tests/tox_env/python/test_python_api.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tox/tox_env/python/api.py b/src/tox/tox_env/python/api.py index 003014317..e231dcda4 100644 --- a/src/tox/tox_env/python/api.py +++ b/src/tox/tox_env/python/api.py @@ -158,7 +158,7 @@ def extract_base_python(cls, env_name: str) -> str | None: return None @classmethod - def python_spec_for_sys_executable(cls) -> PythonSpec: + def _python_spec_for_sys_executable(cls) -> PythonSpec: implementation = sys.implementation.name version = sys.version_info bits = "64" if sys.maxsize > 2**32 else "32" @@ -180,7 +180,7 @@ def _validate_base_python( if spec_base.path is not None: path = Path(spec_base.path).absolute() if str(spec_base.path) == sys.executable: - spec_base = cls.python_spec_for_sys_executable() + spec_base = cls._python_spec_for_sys_executable() else: spec_base = cls.python_spec_for_path(path) if any( diff --git a/tests/tox_env/python/test_python_api.py b/tests/tox_env/python/test_python_api.py index 02eccd4b1..15d3b45c9 100644 --- a/tests/tox_env/python/test_python_api.py +++ b/tests/tox_env/python/test_python_api.py @@ -311,7 +311,7 @@ def test_python_spec_for_sys_executable(impl: str, major: int, minor: int, arch: mocker.patch.object(sys, "version_info", version_info) mocker.patch.object(sys, "implementation", implementation) mocker.patch.object(sys, "maxsize", 2**arch // 2 - 1) - spec = Python.python_spec_for_sys_executable() + spec = Python._python_spec_for_sys_executable() assert spec.implementation == impl assert spec.major == major assert spec.minor == minor From e911fba542f0fdaf1536ab37fcbc99787789d20f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Sat, 17 Aug 2024 16:30:43 +0200 Subject: [PATCH 3/4] Silence the linter, this is a test for a private method The error was: tests/tox_env/python/test_python_api.py:314:12: SLF001 Private member accessed: `_python_spec_for_sys_executable` | 312 | mocker.patch.object(sys, "implementation", implementation) 313 | mocker.patch.object(sys, "maxsize", 2**arch // 2 - 1) 314 | spec = Python._python_spec_for_sys_executable() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SLF001 315 | assert spec.implementation == impl 316 | assert spec.major == major | Found 1 error. --- tests/tox_env/python/test_python_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tox_env/python/test_python_api.py b/tests/tox_env/python/test_python_api.py index 15d3b45c9..b809e0f40 100644 --- a/tests/tox_env/python/test_python_api.py +++ b/tests/tox_env/python/test_python_api.py @@ -311,7 +311,7 @@ def test_python_spec_for_sys_executable(impl: str, major: int, minor: int, arch: mocker.patch.object(sys, "version_info", version_info) mocker.patch.object(sys, "implementation", implementation) mocker.patch.object(sys, "maxsize", 2**arch // 2 - 1) - spec = Python._python_spec_for_sys_executable() + spec = Python._python_spec_for_sys_executable() # noqa: SLF001 assert spec.implementation == impl assert spec.major == major assert spec.minor == minor From f0af9ff8ad756c11fb830d294b4e56e8db5baa6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Sat, 17 Aug 2024 16:42:51 +0200 Subject: [PATCH 4/4] Add a changelog fragment --- docs/changelog/3327.bugfix.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 docs/changelog/3327.bugfix.rst diff --git a/docs/changelog/3327.bugfix.rst b/docs/changelog/3327.bugfix.rst new file mode 100644 index 000000000..dbad98cf6 --- /dev/null +++ b/docs/changelog/3327.bugfix.rst @@ -0,0 +1,2 @@ +Fix and test the string spec for the ``sys.executable`` interpreter (introduced in :pull:`3325`) +- by :user:`hroncok`