From d74ea367d132476873f82fc4a7ea8467b8944193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Randy=20D=C3=B6ring?= <30527984+radoering@users.noreply.github.com> Date: Sun, 16 Feb 2025 14:32:07 +0100 Subject: [PATCH] fix "env use python" to use Python in PATH This should use the Python, which is in the PATH, not the latest Python. --- src/poetry/utils/env/python/manager.py | 14 +------ src/poetry/utils/env/python/providers.py | 10 ++++- tests/utils/env/python/test_manager.py | 37 +++++++++++++++++++ .../utils/env/python/test_python_providers.py | 11 ++++++ 4 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/poetry/utils/env/python/manager.py b/src/poetry/utils/env/python/manager.py index 3a7c6e00bba..1a0f26bc2b2 100644 --- a/src/poetry/utils/env/python/manager.py +++ b/src/poetry/utils/env/python/manager.py @@ -1,6 +1,5 @@ from __future__ import annotations -import contextlib import os import sys @@ -229,13 +228,6 @@ def get_active_python(cls) -> Python | None: return None - @classmethod - def from_executable(cls, path: Path | str) -> Python: - try: - return cls(python=findpython.PythonVersion(executable=Path(path))) - except (FileNotFoundError, NotADirectoryError, ValueError): - raise ValueError(f"{path} is not a valid Python executable") - @classmethod def get_system_python(cls) -> Python: """ @@ -252,10 +244,8 @@ def get_system_python(cls) -> Python: @classmethod def get_by_name(cls, python_name: str) -> Python | None: - if Path(python_name).exists(): - with contextlib.suppress(ValueError): - # if it is a path try assuming it is an executable - return cls.from_executable(python_name) + if python := ShutilWhichPythonProvider.find_python_by_name(python_name): + return cls(python=python) if python := findpython.find(python_name): return cls(python=python) diff --git a/src/poetry/utils/env/python/providers.py b/src/poetry/utils/env/python/providers.py index d7192d3f407..d39d5e6500f 100644 --- a/src/poetry/utils/env/python/providers.py +++ b/src/poetry/utils/env/python/providers.py @@ -28,10 +28,16 @@ def create(cls) -> Self | None: return cls() def find_pythons(self) -> Iterable[findpython.PythonVersion]: - if path := shutil.which("python"): - return [findpython.PythonVersion(executable=Path(path))] + if python := self.find_python_by_name("python"): + return [python] return [] + @classmethod + def find_python_by_name(cls, name: str) -> findpython.PythonVersion | None: + if path := shutil.which(name): + return findpython.PythonVersion(executable=Path(path)) + return None + @dataclasses.dataclass class PoetryPythonPathProvider(PathProvider): # type: ignore[misc] diff --git a/tests/utils/env/python/test_manager.py b/tests/utils/env/python/test_manager.py index 9ee60d9068c..479f2528ee4 100644 --- a/tests/utils/env/python/test_manager.py +++ b/tests/utils/env/python/test_manager.py @@ -1,6 +1,7 @@ from __future__ import annotations import platform +import sys from typing import TYPE_CHECKING @@ -95,3 +96,39 @@ def find_downloadable_versions_include_incompatible() -> None: assert len( list(Python.find_downloadable_versions(include_incompatible=True)) ) > len(list(Python.find_downloadable_versions())) + + +@pytest.mark.parametrize( + ("name", "expected_minor"), + [ + ("3.9", 9), + ("3.10", 10), + ("3.11", None), + ], +) +def test_get_by_name_version( + mocked_python_register: MockedPythonRegister, name: str, expected_minor: int | None +) -> None: + mocked_python_register("3.9.1", implementation="CPython", parent="a") + mocked_python_register("3.10.3", implementation="CPython", parent="b") + + python = Python.get_by_name(name) + if expected_minor is None: + assert python is None + else: + assert python is not None + assert python.minor == expected_minor + + +def test_get_by_name_python(without_mocked_findpython: None) -> None: + python = Python.get_by_name("python") + assert python is not None + assert python.version.major == 3 + assert python.version.minor == sys.version_info.minor + + +def test_get_by_name_path(without_mocked_findpython: None) -> None: + python = Python.get_by_name(sys.executable) + assert python is not None + assert python.version.major == 3 + assert python.version.minor == sys.version_info.minor diff --git a/tests/utils/env/python/test_python_providers.py b/tests/utils/env/python/test_python_providers.py index 8d122b58d2d..f2c3e6e7752 100644 --- a/tests/utils/env/python/test_python_providers.py +++ b/tests/utils/env/python/test_python_providers.py @@ -1,16 +1,27 @@ from __future__ import annotations +import sys + from typing import TYPE_CHECKING from poetry.core.constraints.version import Version from poetry.utils.env.python.providers import PoetryPythonPathProvider +from poetry.utils.env.python.providers import ShutilWhichPythonProvider if TYPE_CHECKING: from tests.types import MockedPoetryPythonRegister +def test_shutil_which_python_provider() -> None: + provider = ShutilWhichPythonProvider.create() + assert provider + pythons = list(provider.find_pythons()) + assert len(pythons) == 1 + assert pythons[0].minor == sys.version_info.minor + + def test_poetry_python_path_provider_no_pythons() -> None: provider = PoetryPythonPathProvider.create() assert provider