2024-09-23 20:49:45 +02:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
2025-01-23 14:45:50 +01:00
|
|
|
import textwrap
|
2024-09-23 20:49:45 +02:00
|
|
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
|
|
2025-01-23 14:45:50 +01:00
|
|
|
import findpython
|
|
|
|
|
import packaging.version
|
2024-09-23 20:49:45 +02:00
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from poetry.core.constraints.version import Version
|
|
|
|
|
|
2025-01-29 01:53:45 +01:00
|
|
|
from poetry.utils.env.python import Python
|
2024-09-23 20:49:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
2025-01-23 14:45:50 +01:00
|
|
|
from unittest.mock import MagicMock
|
|
|
|
|
|
2024-09-23 20:49:45 +02:00
|
|
|
from pytest_mock import MockerFixture
|
|
|
|
|
|
|
|
|
|
from poetry.config.config import Config
|
2025-01-23 14:45:50 +01:00
|
|
|
from tests.types import MockedPythonRegister
|
2024-09-23 20:49:45 +02:00
|
|
|
from tests.types import ProjectFactory
|
|
|
|
|
|
|
|
|
|
|
2025-08-27 10:44:14 +02:00
|
|
|
@pytest.fixture(scope="session")
|
|
|
|
|
def python_version() -> Version:
|
|
|
|
|
version = sys.version.split(" ", 1)[0]
|
|
|
|
|
if version[-1] == "+":
|
|
|
|
|
version = version[:-1]
|
|
|
|
|
return Version.parse(version)
|
|
|
|
|
|
|
|
|
|
|
2024-09-23 20:49:45 +02:00
|
|
|
def test_python_get_version_on_the_fly() -> None:
|
2025-01-23 14:45:50 +01:00
|
|
|
python = Python.get_system_python()
|
2024-09-23 20:49:45 +02:00
|
|
|
|
|
|
|
|
assert python.version == Version.parse(
|
|
|
|
|
".".join([str(s) for s in sys.version_info[:3]])
|
|
|
|
|
)
|
|
|
|
|
assert python.patch_version == Version.parse(
|
|
|
|
|
".".join([str(s) for s in sys.version_info[:3]])
|
|
|
|
|
)
|
|
|
|
|
assert python.minor_version == Version.parse(
|
|
|
|
|
".".join([str(s) for s in sys.version_info[:2]])
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_python_get_system_python() -> None:
|
|
|
|
|
python = Python.get_system_python()
|
|
|
|
|
|
2025-03-29 08:55:22 -07:00
|
|
|
assert python.executable.resolve() == findpython.find().executable.resolve()
|
2024-09-23 20:49:45 +02:00
|
|
|
assert python.version == Version.parse(
|
|
|
|
|
".".join(str(v) for v in sys.version_info[:3])
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-08-27 10:44:14 +02:00
|
|
|
def test_python_get_preferred_default(config: Config, python_version: Version) -> None:
|
2024-09-23 20:49:45 +02:00
|
|
|
python = Python.get_preferred_python(config)
|
2025-08-01 01:39:14 +02:00
|
|
|
|
2025-03-29 08:55:22 -07:00
|
|
|
assert python.executable.resolve() == Path(sys.executable).resolve()
|
2025-08-27 10:44:14 +02:00
|
|
|
assert python.version == python_version
|
2024-09-23 20:49:45 +02:00
|
|
|
|
|
|
|
|
|
2024-10-27 16:45:29 +01:00
|
|
|
def test_get_preferred_python_use_poetry_python_disabled(
|
|
|
|
|
config: Config, mocker: MockerFixture
|
|
|
|
|
) -> None:
|
2024-09-23 20:49:45 +02:00
|
|
|
mocker.patch(
|
2025-01-29 01:53:45 +01:00
|
|
|
"poetry.utils.env.python.Python.get_active_python",
|
2025-01-23 14:45:50 +01:00
|
|
|
return_value=Python(
|
|
|
|
|
python=findpython.PythonVersion(
|
|
|
|
|
executable=Path("/usr/bin/python3.7"),
|
|
|
|
|
_version=packaging.version.Version("3.7.1"),
|
|
|
|
|
_interpreter=Path("/usr/bin/python3.7"),
|
|
|
|
|
)
|
|
|
|
|
),
|
2024-09-23 20:49:45 +02:00
|
|
|
)
|
2025-01-23 14:45:50 +01:00
|
|
|
|
2024-10-27 16:45:29 +01:00
|
|
|
config.config["virtualenvs"]["use-poetry-python"] = False
|
2024-09-23 20:49:45 +02:00
|
|
|
python = Python.get_preferred_python(config)
|
|
|
|
|
|
|
|
|
|
assert python.executable.as_posix().startswith("/usr/bin/python")
|
|
|
|
|
assert python.version == Version.parse("3.7.1")
|
|
|
|
|
|
|
|
|
|
|
2024-10-27 16:45:29 +01:00
|
|
|
def test_get_preferred_python_use_poetry_python_disabled_fallback(
|
2025-01-23 14:45:50 +01:00
|
|
|
config: Config, with_no_active_python: MagicMock
|
2024-09-23 20:49:45 +02:00
|
|
|
) -> None:
|
2024-10-27 16:45:29 +01:00
|
|
|
config.config["virtualenvs"]["use-poetry-python"] = False
|
2024-11-14 23:40:46 +00:00
|
|
|
python = Python.get_preferred_python(config)
|
2024-09-23 20:49:45 +02:00
|
|
|
|
2025-01-23 14:45:50 +01:00
|
|
|
assert with_no_active_python.call_count == 1
|
2025-03-29 08:55:22 -07:00
|
|
|
assert python.executable.resolve() == Path(sys.executable).resolve()
|
2024-09-23 20:49:45 +02:00
|
|
|
|
|
|
|
|
|
2025-01-23 14:45:50 +01:00
|
|
|
def test_fallback_on_detect_active_python(with_no_active_python: MagicMock) -> None:
|
|
|
|
|
active_python = Python.get_active_python()
|
2024-09-23 20:49:45 +02:00
|
|
|
assert active_python is None
|
2025-01-23 14:45:50 +01:00
|
|
|
assert with_no_active_python.call_count == 1
|
2024-09-23 20:49:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(sys.platform != "win32", reason="Windows only")
|
2025-01-23 14:45:50 +01:00
|
|
|
def test_detect_active_python_with_bat(
|
2025-08-27 10:44:14 +02:00
|
|
|
tmp_path: Path, without_mocked_findpython: None, python_version: Version
|
2025-01-23 14:45:50 +01:00
|
|
|
) -> None:
|
2024-09-23 20:49:45 +02:00
|
|
|
"""On Windows pyenv uses batch files for python management."""
|
|
|
|
|
python_wrapper = tmp_path / "python.bat"
|
2025-01-23 14:45:50 +01:00
|
|
|
|
2025-11-29 09:33:26 +01:00
|
|
|
with python_wrapper.open("w", encoding="locale") as f:
|
2025-01-23 14:45:50 +01:00
|
|
|
f.write(
|
|
|
|
|
textwrap.dedent(f"""
|
|
|
|
|
@echo off
|
|
|
|
|
SET PYTHON_EXE="{sys.executable}"
|
|
|
|
|
%PYTHON_EXE% %*
|
|
|
|
|
""")
|
|
|
|
|
)
|
2024-09-23 20:49:45 +02:00
|
|
|
os.environ["PATH"] = str(python_wrapper.parent) + os.pathsep + os.environ["PATH"]
|
|
|
|
|
|
2025-01-23 14:45:50 +01:00
|
|
|
python = Python.get_active_python()
|
|
|
|
|
assert python is not None
|
2024-09-23 20:49:45 +02:00
|
|
|
|
2025-01-23 14:45:50 +01:00
|
|
|
# TODO: Asses if Poetry needs to discover real path in these cases as
|
|
|
|
|
# this is not a symlink and won't be handled by findpython
|
|
|
|
|
assert python.executable.as_posix() == Path(sys.executable).as_posix()
|
2025-08-27 10:44:14 +02:00
|
|
|
assert python.version == python_version
|
2024-09-23 20:49:45 +02:00
|
|
|
|
|
|
|
|
|
2025-01-23 14:45:50 +01:00
|
|
|
def test_python_find_compatible(
|
|
|
|
|
project_factory: ProjectFactory, mocked_python_register: MockedPythonRegister
|
|
|
|
|
) -> None:
|
2024-09-23 20:49:45 +02:00
|
|
|
# Note: This test may fail on Windows systems using Python from the Microsoft Store,
|
|
|
|
|
# as the executable is named `py.exe`, which is not currently recognized by
|
|
|
|
|
# Python.get_compatible_python. This issue will be resolved in #2117.
|
|
|
|
|
# However, this does not cause problems in our case because Poetry's own
|
|
|
|
|
# Python interpreter is used before attempting to find another compatible version.
|
|
|
|
|
fixture = Path(__file__).parent.parent / "fixtures" / "simple_project"
|
|
|
|
|
poetry = project_factory("simple-project", source=fixture)
|
2025-01-23 14:45:50 +01:00
|
|
|
mocked_python_register("3.12")
|
2024-09-23 20:49:45 +02:00
|
|
|
python = Python.get_compatible_python(poetry)
|
|
|
|
|
|
|
|
|
|
assert Version.from_parts(3, 4) <= python.version <= Version.from_parts(4, 0)
|