Skip to content

Commit ddfeaae

Browse files
committed
Reject projects that have neither a pyproject.toml nor a setup.py
1 parent d362431 commit ddfeaae

File tree

6 files changed

+33
-6
lines changed

6 files changed

+33
-6
lines changed

news/10531.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Always refuse installing or building projects that have no ``pyproject.toml`` nor
2+
``setup.py``.

src/pip/_internal/pyproject.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ def load_pyproject_toml(
4848
has_pyproject = os.path.isfile(pyproject_toml)
4949
has_setup = os.path.isfile(setup_py)
5050

51+
if not has_pyproject and not has_setup:
52+
raise InstallationError(
53+
f"{req_name} does not appear to be a Python project: "
54+
f"neither 'setup.py' nor 'pyproject.toml' found."
55+
)
56+
5157
if has_pyproject:
5258
with open(pyproject_toml, encoding="utf-8") as f:
5359
pp_toml = tomli.load(f)

src/pip/_internal/req/constructors.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ def _get_url_from_path(path: str, name: str) -> Optional[str]:
235235
if _looks_like_path(name) and os.path.isdir(path):
236236
if is_installable_dir(path):
237237
return path_to_url(path)
238+
# TODO: The is_installable_dir test here might not be necessary
239+
# now that it is done in load_pyproject_toml too.
238240
raise InstallationError(
239241
f"Directory {name!r} is not installable. Neither 'setup.py' "
240242
"nor 'pyproject.toml' found."
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[metadata]
2+
name = "dummy"
3+
version = "0.1"

tests/functional/test_install.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,6 @@ def test_install_from_local_directory_with_no_setup_py(script, data):
653653
"""
654654
result = script.pip("install", data.root, expect_error=True)
655655
assert not result.files_created
656-
assert "is not installable." in result.stderr
657656
assert "Neither 'setup.py' nor 'pyproject.toml' found." in result.stderr
658657

659658

@@ -663,11 +662,10 @@ def test_editable_install__local_dir_no_setup_py(script, data):
663662
"""
664663
result = script.pip("install", "-e", data.root, expect_error=True)
665664
assert not result.files_created
666-
667-
msg = result.stderr
668-
assert msg.startswith("ERROR: File 'setup.py' or 'setup.cfg' not found ")
669-
assert "cannot be installed in editable mode" in msg
670-
assert "pyproject.toml" not in msg
665+
assert (
666+
"does not appear to be a Python project: "
667+
"neither 'setup.py' nor 'pyproject.toml' found" in result.stderr
668+
)
671669

672670

673671
def test_editable_install__local_dir_no_setup_py_with_pyproject(script):

tests/unit/test_pep517.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@ def test_use_pep517(shared_data: TestData, source: str, expected: bool) -> None:
2727
assert req.use_pep517 is expected
2828

2929

30+
def test_use_pep517_rejects_setup_cfg_only(shared_data: TestData) -> None:
31+
"""
32+
Test that projects with setup.cfg but no pyproject.toml are rejected.
33+
"""
34+
src = shared_data.src.joinpath("pep517_setup_cfg_only")
35+
req = InstallRequirement(None, None)
36+
req.source_dir = src # make req believe it has been unpacked
37+
with pytest.raises(InstallationError) as e:
38+
req.load_pyproject_toml()
39+
err_msg = e.value.args[0]
40+
assert (
41+
"does not appear to be a Python project: "
42+
"neither 'setup.py' nor 'pyproject.toml' found" in err_msg
43+
)
44+
45+
3046
@pytest.mark.parametrize(
3147
("source", "msg"),
3248
[

0 commit comments

Comments
 (0)