Skip to content

Commit cc2d299

Browse files
committed
Error out if installing a pyproject.toml-style (PEP 517) project in editable mode.
1 parent f66c1f7 commit cc2d299

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed

news/6314.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Error out with an informative message if one tries to install a
2+
``pyproject.toml``-style (PEP 517) source tree using ``--editable`` mode.

src/pip/_internal/pyproject.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,27 @@ def read_pyproject_toml(path):
4949
return build_system
5050

5151

52+
def make_editable_error(req_name, reason):
53+
"""
54+
:param req_name: the name of the requirement.
55+
:param reason: the reason the requirement is being processed as
56+
pyproject.toml-style.
57+
"""
58+
message = (
59+
'Error installing {!r}: editable mode is not supported for '
60+
'pyproject.toml-style projects. This project is being processed '
61+
'as pyproject.toml-style because {}. '
62+
'See PEP 517 for the relevant specification.'
63+
).format(req_name, reason)
64+
return InstallationError(message)
65+
66+
5267
def resolve_pyproject_toml(
53-
build_system, # type: Optional[Dict[str, str]]
68+
build_system, # type: Optional[Dict[str, Any]]
5469
has_pyproject, # type: bool
5570
has_setup, # type: bool
5671
use_pep517, # type: Optional[bool]
72+
editable, # type: bool
5773
req_name, # type: str
5874
):
5975
# type: (...) -> Optional[Tuple[List[str], str, List[str]]]
@@ -67,6 +83,7 @@ def resolve_pyproject_toml(
6783
:param has_setup: whether the project has a setup.py file.
6884
:param use_pep517: whether the user requested PEP 517 processing. None
6985
means the user didn't explicitly specify.
86+
:param editable: whether editable mode was requested for the requirement.
7087
:param req_name: the name of the requirement we're processing (for
7188
error reporting).
7289
"""
@@ -82,6 +99,10 @@ def resolve_pyproject_toml(
8299
"Disabling PEP 517 processing is invalid: "
83100
"project does not have a setup.py"
84101
)
102+
if editable:
103+
raise make_editable_error(
104+
req_name, 'it has a pyproject.toml file and no setup.py'
105+
)
85106
use_pep517 = True
86107
elif build_system and "build-backend" in build_system:
87108
if use_pep517 is not None and not use_pep517:
@@ -92,7 +113,18 @@ def resolve_pyproject_toml(
92113
build_system["build-backend"]
93114
)
94115
)
116+
if editable:
117+
reason = (
118+
'it has a pyproject.toml file with a "build-backend" key '
119+
'in the "build_system" value'
120+
)
121+
raise make_editable_error(req_name, reason)
95122
use_pep517 = True
123+
elif use_pep517:
124+
if editable:
125+
raise make_editable_error(
126+
req_name, 'PEP 517 processing was explicitly requested'
127+
)
96128

97129
# If we haven't worked out whether to use PEP 517 yet,
98130
# and the user hasn't explicitly stated a preference,
@@ -175,6 +207,7 @@ def resolve_pyproject_toml(
175207

176208
def load_pyproject_toml(
177209
use_pep517, # type: Optional[bool]
210+
editable, # type: bool
178211
pyproject_toml, # type: str
179212
setup_py, # type: str
180213
req_name # type: str
@@ -185,6 +218,7 @@ def load_pyproject_toml(
185218
Parameters:
186219
use_pep517 - Has the user requested PEP 517 processing? None
187220
means the user hasn't explicitly specified.
221+
editable - Whether editable mode was requested for the requirement.
188222
pyproject_toml - Location of the project's pyproject.toml file
189223
setup_py - Location of the project's setup.py file
190224
req_name - The name of the requirement we're processing (for
@@ -212,5 +246,6 @@ def load_pyproject_toml(
212246
has_pyproject=has_pyproject,
213247
has_setup=has_setup,
214248
use_pep517=use_pep517,
249+
editable=editable,
215250
req_name=req_name,
216251
)

src/pip/_internal/req/req_install.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ def load_pyproject_toml(self):
484484
"""
485485
pep517_data = load_pyproject_toml(
486486
self.use_pep517,
487+
self.editable,
487488
self.pyproject_toml,
488489
self.setup_py,
489490
str(self)

tests/unit/test_pep517.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,69 @@
11
import pytest
22

33
from pip._internal.exceptions import InstallationError
4+
from pip._internal.pyproject import resolve_pyproject_toml
45
from pip._internal.req import InstallRequirement
56

67

8+
@pytest.mark.parametrize('editable', [False, True])
9+
def test_resolve_pyproject_toml__pep_517_optional(editable):
10+
"""
11+
Test resolve_pyproject_toml() when has_pyproject=True but the source
12+
tree isn't pyproject.toml-style per PEP 517.
13+
"""
14+
actual = resolve_pyproject_toml(
15+
build_system=None,
16+
has_pyproject=True,
17+
has_setup=True,
18+
use_pep517=None,
19+
editable=editable,
20+
req_name='my-package',
21+
)
22+
expected = (
23+
['setuptools>=40.8.0', 'wheel'],
24+
'setuptools.build_meta:__legacy__',
25+
[],
26+
)
27+
assert actual == expected
28+
29+
30+
@pytest.mark.parametrize(
31+
'has_pyproject, has_setup, use_pep517, build_system, expected_err', [
32+
# Test pyproject.toml with no setup.py.
33+
(True, False, None, None, 'has a pyproject.toml file and no setup.py'),
34+
# Test "build-backend" present.
35+
(True, True, None, {'build-backend': 'foo'},
36+
'has a pyproject.toml file with a "build-backend" key'),
37+
# Test explicitly requesting PEP 517 processing.
38+
(True, True, True, None,
39+
'PEP 517 processing was explicitly requested'),
40+
]
41+
)
42+
def test_resolve_pyproject_toml__editable_and_pep_517_required(
43+
has_pyproject, has_setup, use_pep517, build_system, expected_err,
44+
):
45+
"""
46+
Test that passing editable=True raises an error if PEP 517 processing
47+
is required.
48+
"""
49+
with pytest.raises(InstallationError) as excinfo:
50+
resolve_pyproject_toml(
51+
build_system=build_system,
52+
has_pyproject=has_pyproject,
53+
has_setup=has_setup,
54+
use_pep517=use_pep517,
55+
editable=True,
56+
req_name='my-package',
57+
)
58+
err_args = excinfo.value.args
59+
assert len(err_args) == 1
60+
msg = err_args[0]
61+
assert msg.startswith(
62+
"Error installing 'my-package': editable mode is not supported"
63+
)
64+
assert expected_err in msg, 'full message: {}'.format(msg)
65+
66+
767
@pytest.mark.parametrize(('source', 'expected'), [
868
("pep517_setup_and_pyproject", True),
969
("pep517_setup_only", False),

0 commit comments

Comments
 (0)