1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
from __future__ import annotations
import os
import sys
import textwrap
from pathlib import Path
from typing import TYPE_CHECKING
import findpython
import packaging.version
import pytest
from poetry.core.constraints.version import Version
from poetry.utils.env.python import Python
if TYPE_CHECKING:
from unittest.mock import MagicMock
from pytest_mock import MockerFixture
from poetry.config.config import Config
from tests.types import MockedPythonRegister
from tests.types import ProjectFactory
@pytest.fixture(scope="session")
def python_version() -> Version:
version = sys.version.split(" ", 1)[0]
if version[-1] == "+":
version = version[:-1]
return Version.parse(version)
def test_python_get_version_on_the_fly() -> None:
python = Python.get_system_python()
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()
assert python.executable.resolve() == findpython.find().executable.resolve()
assert python.version == Version.parse(
".".join(str(v) for v in sys.version_info[:3])
)
def test_python_get_preferred_default(config: Config, python_version: Version) -> None:
python = Python.get_preferred_python(config)
assert python.executable.resolve() == Path(sys.executable).resolve()
assert python.version == python_version
def test_get_preferred_python_use_poetry_python_disabled(
config: Config, mocker: MockerFixture
) -> None:
mocker.patch(
"poetry.utils.env.python.Python.get_active_python",
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"),
)
),
)
config.config["virtualenvs"]["use-poetry-python"] = False
python = Python.get_preferred_python(config)
assert python.executable.as_posix().startswith("/usr/bin/python")
assert python.version == Version.parse("3.7.1")
def test_get_preferred_python_use_poetry_python_disabled_fallback(
config: Config, with_no_active_python: MagicMock
) -> None:
config.config["virtualenvs"]["use-poetry-python"] = False
python = Python.get_preferred_python(config)
assert with_no_active_python.call_count == 1
assert python.executable.resolve() == Path(sys.executable).resolve()
def test_fallback_on_detect_active_python(with_no_active_python: MagicMock) -> None:
active_python = Python.get_active_python()
assert active_python is None
assert with_no_active_python.call_count == 1
@pytest.mark.skipif(sys.platform != "win32", reason="Windows only")
def test_detect_active_python_with_bat(
tmp_path: Path, without_mocked_findpython: None, python_version: Version
) -> None:
"""On Windows pyenv uses batch files for python management."""
python_wrapper = tmp_path / "python.bat"
with python_wrapper.open("w", encoding="locale") as f:
f.write(
textwrap.dedent(f"""
@echo off
SET PYTHON_EXE="{sys.executable}"
%PYTHON_EXE% %*
""")
)
os.environ["PATH"] = str(python_wrapper.parent) + os.pathsep + os.environ["PATH"]
python = Python.get_active_python()
assert python is not None
# 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()
assert python.version == python_version
def test_python_find_compatible(
project_factory: ProjectFactory, mocked_python_register: MockedPythonRegister
) -> None:
# 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)
mocked_python_register("3.12")
python = Python.get_compatible_python(poetry)
assert Version.from_parts(3, 4) <= python.version <= Version.from_parts(4, 0)
|