Skip to content

Commit 7a95720

Browse files
KOLANICHuranusjrpradyunsg
committed
Fixed --editable install for setuptools projects without setup.py.
Co-Authored-By: Tzu-ping Chung <[email protected]> Co-Authored-By: Pradyun Gedam <[email protected]>
1 parent d9d7790 commit 7a95720

File tree

4 files changed

+43
-17
lines changed

4 files changed

+43
-17
lines changed

Diff for: news/9547.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add support for editable installs for project with only setup.cfg files.

Diff for: src/pip/_internal/req/constructors.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,19 @@ def parse_editable(editable_req):
7777
url_no_extras, extras = _strip_extras(url)
7878

7979
if os.path.isdir(url_no_extras):
80-
if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
80+
setup_py = os.path.join(url_no_extras, 'setup.py')
81+
setup_cfg = os.path.join(url_no_extras, 'setup.cfg')
82+
if not os.path.exists(setup_py) and not os.path.exists(setup_cfg):
8183
msg = (
82-
'File "setup.py" not found. Directory cannot be installed '
83-
'in editable mode: {}'.format(os.path.abspath(url_no_extras))
84+
'File "setup.py" or "setup.cfg" not found. Directory cannot be '
85+
'installed in editable mode: {}'
86+
.format(os.path.abspath(url_no_extras))
8487
)
8588
pyproject_path = make_pyproject_path(url_no_extras)
8689
if os.path.isfile(pyproject_path):
8790
msg += (
8891
'\n(A "pyproject.toml" file was found, but editable '
89-
'mode currently requires a setup.py based build.)'
92+
'mode currently requires a setuptools-based build.)'
9093
)
9194
raise InstallationError(msg)
9295

Diff for: src/pip/_internal/utils/setuptools_build.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
# invoking via the shim. This avoids e.g. the following manifest_maker
99
# warning: "warning: manifest_maker: standard file '-c' not found".
1010
_SETUPTOOLS_SHIM = (
11-
"import sys, setuptools, tokenize; sys.argv[0] = {0!r}; __file__={0!r};"
12-
"f=getattr(tokenize, 'open', open)(__file__);"
13-
"code=f.read().replace('\\r\\n', '\\n');"
11+
"import io, os, sys, setuptools, tokenize; sys.argv[0] = {0!r}; __file__={0!r};"
12+
"f = getattr(tokenize, 'open', open)(__file__) "
13+
"if os.path.exists(__file__) "
14+
"else io.StringIO('from setuptools import setup; setup()');"
15+
"code = f.read().replace('\\r\\n', '\\n');"
1416
"f.close();"
1517
"exec(compile(code, __file__, 'exec'))"
1618
)

Diff for: tests/functional/test_install.py

+30-10
Original file line numberDiff line numberDiff line change
@@ -641,9 +641,9 @@ def test_editable_install__local_dir_no_setup_py(
641641

642642
msg = result.stderr
643643
if deprecated_python:
644-
assert 'File "setup.py" not found. ' in msg
644+
assert 'File "setup.py" or "setup.cfg" not found. ' in msg
645645
else:
646-
assert msg.startswith('ERROR: File "setup.py" not found. ')
646+
assert msg.startswith('ERROR: File "setup.py" or "setup.cfg" not found. ')
647647
assert 'pyproject.toml' not in msg
648648

649649

@@ -663,9 +663,9 @@ def test_editable_install__local_dir_no_setup_py_with_pyproject(
663663

664664
msg = result.stderr
665665
if deprecated_python:
666-
assert 'File "setup.py" not found. ' in msg
666+
assert 'File "setup.py" or "setup.cfg" not found. ' in msg
667667
else:
668-
assert msg.startswith('ERROR: File "setup.py" not found. ')
668+
assert msg.startswith('ERROR: File "setup.py" or "setup.cfg" not found. ')
669669
assert 'A "pyproject.toml" file was found' in msg
670670

671671

@@ -1034,15 +1034,13 @@ def test_install_package_with_prefix(script, data):
10341034
result.did_create(install_path)
10351035

10361036

1037-
def test_install_editable_with_prefix(script):
1037+
def _test_install_editable_with_prefix(script, files):
10381038
# make a dummy project
10391039
pkga_path = script.scratch_path / 'pkga'
10401040
pkga_path.mkdir()
1041-
pkga_path.joinpath("setup.py").write_text(textwrap.dedent("""
1042-
from setuptools import setup
1043-
setup(name='pkga',
1044-
version='0.1')
1045-
"""))
1041+
1042+
for fn, contents in files.items():
1043+
pkga_path.joinpath(fn).write_text(textwrap.dedent(contents))
10461044

10471045
if hasattr(sys, "pypy_version_info"):
10481046
site_packages = os.path.join(
@@ -1087,6 +1085,28 @@ def test_install_editable_with_target(script):
10871085
result.did_create(script.scratch / 'target' / 'watching_testrunner.py')
10881086

10891087

1088+
def test_install_editable_with_prefix_setup_py(script):
1089+
setup_py = """
1090+
from setuptools import setup
1091+
setup(name='pkga', version='0.1')
1092+
"""
1093+
_test_install_editable_with_prefix(script, {"setup.py": setup_py})
1094+
1095+
1096+
def test_install_editable_with_prefix_setup_cfg(script):
1097+
setup_cfg = """[metadata]
1098+
name = pkga
1099+
version = 0.1
1100+
"""
1101+
pyproject_toml = """[build-system]
1102+
requires = ["setuptools", "wheel"]
1103+
build-backend = "setuptools.build_meta"
1104+
"""
1105+
_test_install_editable_with_prefix(
1106+
script, {"setup.cfg": setup_cfg, "pyproject.toml": pyproject_toml}
1107+
)
1108+
1109+
10901110
def test_install_package_conflict_prefix_and_user(script, data):
10911111
"""
10921112
Test installing a package using pip install --prefix --user errors out

0 commit comments

Comments
 (0)