Skip to content

Commit 4f6a965

Browse files
authored
Merge pull request #7394 from takluyver/i6599
Fix building packages with backend-path in pyproject.toml
2 parents 9557610 + 59550aa commit 4f6a965

File tree

9 files changed

+102
-6
lines changed

9 files changed

+102
-6
lines changed

news/6599.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix building packages which specify ``backend-path`` in pyproject.toml.

src/pip/_internal/pyproject.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io
44
import os
55
import sys
6+
from collections import namedtuple
67

78
from pip._vendor import pytoml, six
89
from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
@@ -11,7 +12,7 @@
1112
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
1213

1314
if MYPY_CHECK_RUNNING:
14-
from typing import Any, Tuple, Optional, List
15+
from typing import Any, Optional, List
1516

1617

1718
def _is_list_of_str(obj):
@@ -33,13 +34,18 @@ def make_pyproject_path(unpacked_source_directory):
3334
return path
3435

3536

37+
BuildSystemDetails = namedtuple('BuildSystemDetails', [
38+
'requires', 'backend', 'check', 'backend_path'
39+
])
40+
41+
3642
def load_pyproject_toml(
3743
use_pep517, # type: Optional[bool]
3844
pyproject_toml, # type: str
3945
setup_py, # type: str
4046
req_name # type: str
4147
):
42-
# type: (...) -> Optional[Tuple[List[str], str, List[str]]]
48+
# type: (...) -> Optional[BuildSystemDetails]
4349
"""Load the pyproject.toml file.
4450
4551
Parameters:
@@ -57,6 +63,8 @@ def load_pyproject_toml(
5763
name of PEP 517 backend,
5864
requirements we should check are installed after setting
5965
up the build environment
66+
directory paths to import the backend from (backend-path),
67+
relative to the project root.
6068
)
6169
"""
6270
has_pyproject = os.path.isfile(pyproject_toml)
@@ -167,6 +175,7 @@ def load_pyproject_toml(
167175
)
168176

169177
backend = build_system.get("build-backend")
178+
backend_path = build_system.get("backend-path", [])
170179
check = [] # type: List[str]
171180
if backend is None:
172181
# If the user didn't specify a backend, we assume they want to use
@@ -184,4 +193,4 @@ def load_pyproject_toml(
184193
backend = "setuptools.build_meta:__legacy__"
185194
check = ["setuptools>=40.8.0", "wheel"]
186195

187-
return (requires, backend, check)
196+
return BuildSystemDetails(requires, backend, check, backend_path)

src/pip/_internal/req/req_install.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -601,11 +601,11 @@ def load_pyproject_toml(self):
601601
return
602602

603603
self.use_pep517 = True
604-
requires, backend, check = pyproject_toml_data
604+
requires, backend, check, backend_path = pyproject_toml_data
605605
self.requirements_to_check = check
606606
self.pyproject_requires = requires
607607
self.pep517_backend = Pep517HookCaller(
608-
self.unpacked_source_directory, backend
608+
self.unpacked_source_directory, backend, backend_path=backend_path,
609609
)
610610

611611
def prepare_metadata(self):
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import os
2+
3+
from setuptools.build_meta import build_sdist
4+
from setuptools.build_meta import build_wheel as setuptools_build_wheel
5+
from setuptools.build_meta import (get_requires_for_build_sdist,
6+
get_requires_for_build_wheel,
7+
prepare_metadata_for_build_wheel)
8+
9+
10+
def build_wheel(*a, **kw):
11+
# Create the marker file to record that the hook was called
12+
with open(os.environ['PIP_TEST_MARKER_FILE'], 'wb'):
13+
pass
14+
15+
return setuptools_build_wheel(*a, **kw)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[build-system]
2+
requires = [ "setuptools" ]
3+
build-backend = "mybuildsys" # setuptools.build_meta
4+
backend-path = ["."]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[metadata]
2+
name = pep517-wrapper-buildsys
3+
version = 1.0
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from setuptools import setup
2+
3+
setup()

tests/functional/test_install.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,21 @@ def test_install_no_binary_builds_pep_517_wheel(script, data, with_wheel):
13391339
assert "Running setup.py install for pep517-set" not in str(res), str(res)
13401340

13411341

1342+
@pytest.mark.network
1343+
def test_install_no_binary_uses_local_backend(
1344+
script, data, with_wheel, tmpdir):
1345+
to_install = data.packages.joinpath('pep517_wrapper_buildsys')
1346+
script.environ['PIP_TEST_MARKER_FILE'] = marker = str(tmpdir / 'marker')
1347+
res = script.pip(
1348+
'install', '--no-binary=:all:', '-f', data.find_links, to_install
1349+
)
1350+
expected = "Successfully installed pep517-wrapper-buildsys"
1351+
# Must have installed the package
1352+
assert expected in str(res), str(res)
1353+
1354+
assert os.path.isfile(marker), "Local PEP 517 backend not used"
1355+
1356+
13421357
def test_install_no_binary_disables_cached_wheels(script, data, with_wheel):
13431358
# Seed the cache
13441359
script.pip(

tests/functional/test_pep517.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
from tests.lib import make_test_finder, path_to_url
77

88

9-
def make_project(tmpdir, requires=[], backend=None):
9+
def make_project(tmpdir, requires=[], backend=None, backend_path=None):
1010
project_dir = tmpdir / 'project'
1111
project_dir.mkdir()
1212
buildsys = {'requires': requires}
1313
if backend:
1414
buildsys['build-backend'] = backend
15+
if backend_path:
16+
buildsys['backend-path'] = backend_path
1517
data = pytoml.dumps({'build-system': buildsys})
1618
project_dir.joinpath('pyproject.toml').write_text(data)
1719
return project_dir
@@ -32,6 +34,50 @@ def test_backend(tmpdir, data):
3234
assert req.pep517_backend.build_wheel("dir") == "Backend called"
3335

3436

37+
dummy_backend_code = """\
38+
def build_wheel(
39+
wheel_directory,
40+
config_settings=None,
41+
metadata_directory=None
42+
):
43+
return "Backend called"
44+
"""
45+
46+
47+
def test_backend_path(tmpdir, data):
48+
"""Check we can call a backend inside the project"""
49+
project_dir = make_project(
50+
tmpdir, backend="dummy_backend", backend_path=['.']
51+
)
52+
(project_dir / 'dummy_backend.py').write_text(dummy_backend_code)
53+
req = InstallRequirement(None, None, source_dir=project_dir)
54+
req.load_pyproject_toml()
55+
56+
env = BuildEnvironment()
57+
assert hasattr(req.pep517_backend, 'build_wheel')
58+
with env:
59+
assert req.pep517_backend.build_wheel("dir") == "Backend called"
60+
61+
62+
def test_backend_path_and_dep(tmpdir, data):
63+
"""Check we can call a requirement's backend successfully"""
64+
project_dir = make_project(
65+
tmpdir, backend="dummy_internal_backend", backend_path=['.']
66+
)
67+
(project_dir / 'dummy_internal_backend.py').write_text(
68+
"from dummy_backend import build_wheel"
69+
)
70+
req = InstallRequirement(None, None, source_dir=project_dir)
71+
req.load_pyproject_toml()
72+
env = BuildEnvironment()
73+
finder = make_test_finder(find_links=[data.backends])
74+
env.install_requirements(finder, ["dummy_backend"], 'normal', "Installing")
75+
76+
assert hasattr(req.pep517_backend, 'build_wheel')
77+
with env:
78+
assert req.pep517_backend.build_wheel("dir") == "Backend called"
79+
80+
3581
def test_pep517_install(script, tmpdir, data):
3682
"""Check we can build with a custom backend"""
3783
project_dir = make_project(

0 commit comments

Comments
 (0)