File: test_call_hooks.py

package info (click to toggle)
python-pyproject-hooks 1.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 348 kB
  • sloc: python: 973; sh: 5; makefile: 2
file content (227 lines) | stat: -rw-r--r-- 7,125 bytes parent folder | download
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
import json
import os
import tarfile
import zipfile
from os.path import abspath, dirname
from os.path import join as pjoin
from unittest.mock import Mock

import pytest
from testpath import assert_isfile, modified_env
from testpath.tempdir import TemporaryDirectory, TemporaryWorkingDirectory

from pyproject_hooks import (
    BackendUnavailable,
    BuildBackendHookCaller,
    UnsupportedOperation,
    default_subprocess_runner,
)
from pyproject_hooks._in_process import _in_proc_script_path as in_proc_script_path
from tests.compat import tomllib

SAMPLES_DIR = pjoin(dirname(abspath(__file__)), "samples")
BUILDSYS_PKGS = pjoin(SAMPLES_DIR, "buildsys_pkgs")


def get_hooks(pkg, **kwargs):
    source_dir = pjoin(SAMPLES_DIR, pkg)
    with open(pjoin(source_dir, "pyproject.toml"), "rb") as f:
        data = tomllib.load(f)
    return BuildBackendHookCaller(
        source_dir, data["build-system"]["build-backend"], **kwargs
    )


def test_missing_backend_gives_exception():
    hooks = get_hooks("pkg1")
    with modified_env({"PYTHONPATH": ""}):
        msg = "Cannot import 'buildsys'"
        with pytest.raises(BackendUnavailable, match=msg) as exc:
            hooks.get_requires_for_build_wheel({})
        assert exc.value.backend_name == "buildsys"


def test_get_requires_for_build_wheel():
    hooks = get_hooks("pkg1")
    with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
        res = hooks.get_requires_for_build_wheel({})
    assert res == ["wheelwright"]


def test_get_requires_for_build_editable():
    hooks = get_hooks("pkg1")
    with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
        res = hooks.get_requires_for_build_editable({})
    assert res == ["wheelwright", "editables"]


def test_get_requires_for_build_sdist():
    hooks = get_hooks("pkg1")
    with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
        res = hooks.get_requires_for_build_sdist({})
    assert res == ["frog"]


def test_prepare_metadata_for_build_wheel():
    hooks = get_hooks("pkg1")
    with TemporaryDirectory() as metadatadir:
        with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
            hooks.prepare_metadata_for_build_wheel(metadatadir, {})

        assert_isfile(pjoin(metadatadir, "pkg1-0.5.dist-info", "METADATA"))


def test_prepare_metadata_for_build_editable():
    hooks = get_hooks("pkg1")
    with TemporaryDirectory() as metadatadir:
        with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
            hooks.prepare_metadata_for_build_editable(metadatadir, {})

        assert_isfile(pjoin(metadatadir, "pkg1-0.5.dist-info", "METADATA"))


def test_build_wheel():
    hooks = get_hooks("pkg1")
    with TemporaryDirectory() as builddir:
        with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
            whl_file = hooks.build_wheel(builddir, {})

        assert whl_file.endswith(".whl")
        assert os.sep not in whl_file

        whl_file = pjoin(builddir, whl_file)
        assert_isfile(whl_file)
        assert zipfile.is_zipfile(whl_file)


def test_build_editable():
    hooks = get_hooks("pkg1")
    with TemporaryDirectory() as builddir:
        with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
            whl_file = hooks.build_editable(builddir, {})

        assert whl_file.endswith(".whl")
        assert os.sep not in whl_file

        whl_file = pjoin(builddir, whl_file)
        assert_isfile(whl_file)
        assert zipfile.is_zipfile(whl_file)


def test_build_wheel_relpath():
    hooks = get_hooks("pkg1")
    with TemporaryWorkingDirectory() as builddir:
        with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
            whl_file = hooks.build_wheel(".", {})

        assert whl_file.endswith(".whl")
        assert os.sep not in whl_file

        whl_file = pjoin(builddir, whl_file)
        assert_isfile(whl_file)
        assert zipfile.is_zipfile(whl_file)


def test_build_sdist():
    hooks = get_hooks("pkg1")
    with TemporaryDirectory() as sdistdir:
        with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
            sdist = hooks.build_sdist(sdistdir, {})

        assert sdist.endswith(".tar.gz")
        assert os.sep not in sdist

        sdist = pjoin(sdistdir, sdist)
        assert_isfile(sdist)
        assert tarfile.is_tarfile(sdist)

        with tarfile.open(sdist) as tf:
            contents = tf.getnames()
        assert "pkg1-0.5/pyproject.toml" in contents


def test_build_sdist_unsupported():
    hooks = get_hooks("pkg1")
    with TemporaryDirectory() as sdistdir:
        with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
            with pytest.raises(UnsupportedOperation):
                hooks.build_sdist(sdistdir, {"test_unsupported": True})


def test_runner_replaced_on_exception(monkeypatch):
    monkeypatch.setenv("PYTHONPATH", BUILDSYS_PKGS)

    runner = Mock(wraps=default_subprocess_runner)
    hooks = get_hooks("pkg1", runner=runner)

    hooks.get_requires_for_build_wheel()
    runner.assert_called_once()
    runner.reset_mock()

    runner2 = Mock(wraps=default_subprocess_runner)
    try:
        with hooks.subprocess_runner(runner2):
            hooks.get_requires_for_build_wheel()
            runner2.assert_called_once()
            runner2.reset_mock()
            raise RuntimeError()
    except RuntimeError:
        pass

    hooks.get_requires_for_build_wheel()
    runner.assert_called_once()


def test_custom_python_executable(monkeypatch, tmpdir):
    monkeypatch.setenv("PYTHONPATH", BUILDSYS_PKGS)

    runner = Mock(autospec=default_subprocess_runner)
    hooks = get_hooks("pkg1", runner=runner, python_executable="some-python")

    with hooks.subprocess_runner(runner):
        with pytest.raises(FileNotFoundError):
            # output.json is missing because we didn't actually run the hook
            hooks.get_requires_for_build_wheel()
        runner.assert_called_once()
        assert runner.call_args[0][0][0] == "some-python"


def test_path_pollution():
    hooks = get_hooks("path-pollution")
    with TemporaryDirectory() as outdir:
        with modified_env(
            {
                "PYTHONPATH": BUILDSYS_PKGS,
                "TEST_POLLUTION_OUTDIR": outdir,
            }
        ):
            hooks.get_requires_for_build_wheel({})
        with open(pjoin(outdir, "out.json")) as f:
            captured_sys_path = json.load(f)

    with in_proc_script_path() as path:
        assert os.path.dirname(path) not in captured_sys_path
    assert captured_sys_path[0] == BUILDSYS_PKGS


def test_setup_py():
    hooks = get_hooks("setup-py")
    with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
        res = set(hooks.get_requires_for_build_wheel({}))
    # Depending on the version of setuptools, it may be both, just wheel, or neither
    assert res.issubset({"setuptools", "wheel"})


@pytest.mark.parametrize(
    ("pkg", "expected"),
    [
        ("pkg1", ["build_editable"]),
        ("pkg2", []),
        ("pkg3", ["build_editable"]),
    ],
)
def test__supported_features(pkg, expected):
    hooks = get_hooks(pkg)
    with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
        res = hooks._supported_features()
    assert res == expected