File: test_inplace_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 (122 lines) | stat: -rw-r--r-- 4,355 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
from inspect import cleandoc
from os.path import abspath, dirname
from os.path import join as pjoin
from pathlib import Path

import pytest
from testpath import modified_env
from testpath.tempdir import TemporaryDirectory

from pyproject_hooks import BackendUnavailable, BuildBackendHookCaller
from tests.compat import tomllib

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


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


@pytest.mark.parametrize(
    "backend_path",
    [
        [".", "subdir"],
        ["../pkg1", "subdir/.."],
    ],
)
def test_backend_path_within_tree(backend_path):
    BuildBackendHookCaller(SOURCE_DIR, "dummy", backend_path)


@pytest.mark.parametrize(
    "backend_path",
    [
        [SOURCE_DIR],
        [".", ".."],
        ["subdir/../.."],
        ["/"],
    ],
)
def test_backend_out_of_tree(backend_path):
    # TODO: Do we want to insist on ValueError, or invent another exception?
    with pytest.raises(Exception):
        BuildBackendHookCaller(SOURCE_DIR, "dummy", backend_path)


@pytest.mark.parametrize("example", ("pkg_intree", "pkg_nested_intree"))
def test_intree_backend(example):
    hooks = get_hooks(example)
    with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
        res = hooks.get_requires_for_build_sdist({})
    assert res == ["intree_backend_called"]


@pytest.mark.parametrize("backend", ("buildsys", "nested.buildsys"))
def test_intree_backend_not_in_path(backend):
    hooks = get_hooks("pkg_intree", backend=backend)
    with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
        msg = f"Cannot find module {backend!r} in .*pkg_intree.*backend"
        with pytest.raises(BackendUnavailable, match=msg):
            hooks.get_requires_for_build_sdist({})


def test_intree_backend_loaded_from_correct_backend_path():
    """
    PEP 517 establishes that the backend code should be loaded from ``backend-path``,
    and recognizes that not always the environment isolation is perfect
    (e.g. it explicitly mentions ``--system-site-packages``).
    Therefore, even in a situation where a third-party ``MetaPathFinder`` has
    precedence over ``importlib.machinery.PathFinder``, the backend should
    still be loaded from ``backend-path``.
    """
    hooks = get_hooks("pkg_intree", backend="intree_backend")
    with TemporaryDirectory() as tmp:
        invalid = Path(tmp, ".invalid", "intree_backend.py")
        invalid.parent.mkdir()
        invalid.write_text("raise ImportError('Do not import')", encoding="utf-8")
        install_finder_with_sitecustomize(tmp, {"intree_backend": str(invalid)})
        with modified_env({"PYTHONPATH": tmp}):  # Override `sitecustomize`.
            res = hooks.get_requires_for_build_sdist({})
    assert res == ["intree_backend_called"]


def test_intree_backend_importlib_metadata_interoperation():
    pytest.importorskip("importlib.metadata")

    hooks = get_hooks("pkg_intree_metadata", backend="intree_backend")
    assert hooks.get_requires_for_build_sdist({}) == [
        "_test_backend.importlib_metadata",
        "hello",
        "world",
    ]


def install_finder_with_sitecustomize(directory, mapping):
    finder = f"""
        import sys
        from importlib.util import spec_from_file_location

        MAPPING = {mapping!r}

        class _Finder:  # MetaPathFinder
            @classmethod
            def find_spec(cls, fullname, path=None, target=None):
                if fullname in MAPPING:
                    return spec_from_file_location(fullname, MAPPING[fullname])

        def install():
            if not any(finder == _Finder for finder in sys.meta_path):
                sys.meta_path.insert(0, _Finder)
    """
    sitecustomize = "import _test_finder_; _test_finder_.install()"
    Path(directory, "_test_finder_.py").write_text(cleandoc(finder), encoding="utf-8")
    Path(directory, "sitecustomize.py").write_text(sitecustomize, encoding="utf-8")