summaryrefslogtreecommitdiff
path: root/tests/tox_env/python/test_python_api.py
blob: aa4c1470b8335e7ee931171191d80b646afe6835 (plain)
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
from __future__ import annotations

import sys
from pathlib import Path
from typing import Callable

import pytest
from pytest_mock import MockerFixture

from tox.pytest import ToxProjectCreator
from tox.tox_env.errors import Fail
from tox.tox_env.python.api import Python


def test_requirements_txt(tox_project: ToxProjectCreator) -> None:
    prj = tox_project(
        {
            "tox.ini": "[testenv]\npackage=skip\ndeps=-rrequirements.txt",
            "requirements.txt": "nose",
        },
    )
    execute_calls = prj.patch_execute(lambda r: 0 if "install" in r.run_id else None)
    result = prj.run("r", "-e", "py")
    result.assert_success()

    assert execute_calls.call_count == 1
    exp = ["python", "-I", "-m", "pip", "install", "-r", "requirements.txt"]
    got_cmd = execute_calls.call_args[0][3].cmd

    assert got_cmd == exp


def test_conflicting_base_python_factor() -> None:
    major, minor = sys.version_info[0:2]
    name = f"py{major}{minor}-py{major}{minor-1}"
    with pytest.raises(ValueError, match=f"conflicting factors py{major}{minor}, py{major}{minor-1} in {name}"):
        Python.extract_base_python(name)


def test_build_wheel_in_non_base_pkg_env(
    tox_project: ToxProjectCreator,
    patch_prev_py: Callable[[bool], tuple[str, str]],
    demo_pkg_inline: Path,
    mocker: MockerFixture,
) -> None:
    mocker.patch("tox.tox_env.python.virtual_env.api.session_via_cli")
    prev_ver, impl = patch_prev_py(True)
    prev_py = f"py{prev_ver}"
    prj = tox_project({"tox.ini": f"[tox]\nenv_list= {prev_py}\n[testenv]\npackage=wheel"})
    execute_calls = prj.patch_execute(lambda r: 0 if "install" in r.run_id else None)
    result = prj.run("-r", "--root", str(demo_pkg_inline))
    result.assert_success()
    calls = [(i[0][0].conf.name, i[0][3].run_id) for i in execute_calls.call_args_list]
    assert calls == [
        (f".pkg-{impl}{prev_ver}", "_optional_hooks"),
        (f".pkg-{impl}{prev_ver}", "get_requires_for_build_wheel"),
        (f".pkg-{impl}{prev_ver}", "build_wheel"),
        (f"py{prev_ver}", "install_package"),
        (f".pkg-{impl}{prev_ver}", "_exit"),
    ]


def test_diff_msg_added_removed_changed() -> None:
    before = {"A": "1", "F": "8", "C": "3", "D": "4", "E": "6"}
    after = {"G": "9", "B": "2", "C": "3", "D": "5", "E": "7"}
    expected = "python added A='1' | F='8', removed G='9' | B='2', changed D='5'->'4' | E='7'->'6'"
    assert Python._diff_msg(before, after) == expected


def test_diff_msg_no_diff() -> None:
    assert Python._diff_msg({}, {}) == "python "


@pytest.mark.parametrize("ignore_conflict", [True, False])
@pytest.mark.parametrize(
    ("env", "base_python"),
    [
        ("magic", ["pypy"]),
        ("magic", ["py39"]),
    ],
    ids=lambda a: "|".join(a) if isinstance(a, list) else str(a),
)
def test_base_python_env_no_conflict(env: str, base_python: list[str], ignore_conflict: bool) -> None:
    result = Python._validate_base_python(env, base_python, ignore_conflict)
    assert result is base_python


@pytest.mark.parametrize("ignore_conflict", [True, False])
@pytest.mark.parametrize(
    ("env", "base_python", "conflict"),
    [
        ("cpython", ["pypy"], ["pypy"]),
        ("pypy", ["cpython"], ["cpython"]),
        ("pypy2", ["pypy3"], ["pypy3"]),
        ("py3", ["py2"], ["py2"]),
        ("py38", ["py39"], ["py39"]),
        ("py38", ["py38", "py39"], ["py39"]),
        ("py310", ["py38", "py39"], ["py38", "py39"]),
        ("py3.11.1", ["py3.11.2"], ["py3.11.2"]),
        ("py3-64", ["py3-32"], ["py3-32"]),
        ("py310-magic", ["py39"], ["py39"]),
    ],
    ids=lambda a: "|".join(a) if isinstance(a, list) else str(a),
)
def test_base_python_env_conflict(env: str, base_python: list[str], conflict: list[str], ignore_conflict: bool) -> None:
    if ignore_conflict:
        result = Python._validate_base_python(env, base_python, ignore_conflict)
        assert result == [env]
    else:
        msg = f"env name {env} conflicting with base python {conflict[0]}"
        with pytest.raises(Fail, match=msg):
            Python._validate_base_python(env, base_python, ignore_conflict)


@pytest.mark.parametrize("ignore_conflict", [True, False, None])
def test_base_python_env_conflict_show_conf(tox_project: ToxProjectCreator, ignore_conflict: bool) -> None:
    py_ver = "".join(str(i) for i in sys.version_info[0:2])
    py_ver_next = "".join(str(i) for i in (sys.version_info[0], sys.version_info[1] + 2))
    ini = f"[testenv]\npackage=skip\nbase_python=py{py_ver_next}"
    if ignore_conflict is not None:
        ini += f"\n[tox]\nignore_base_python_conflict={ignore_conflict}"
    project = tox_project({"tox.ini": ini})
    result = project.run("c", "-e", f"py{py_ver}", "-k", "base_python")
    result.assert_success()
    if ignore_conflict:
        out = f"[testenv:py{py_ver}]\nbase_python = py{py_ver}\n"
    else:
        comma_in_exc = sys.version_info[0:2] <= (3, 6)
        out = (
            f"[testenv:py{py_ver}]\nbase_python = # Exception: Fail('env name py{py_ver} conflicting with"
            f" base python py{py_ver_next}'{',' if comma_in_exc else ''})\n"
        )
    result.assert_out_err(out, "")