mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Provide a better error message for a pyproject.toml editable install.
The message looks like this: File "setup.py" not found. Directory cannot be installed in editable mode: <absolute-dir-path> (A "pyproject.toml" file was found, but editable mode currently requires a setup.py based build.)
This commit is contained in:
parent
e5f4bbb7dd
commit
d619aba150
5 changed files with 59 additions and 14 deletions
2
news/6170.feature
Normal file
2
news/6170.feature
Normal file
|
@ -0,0 +1,2 @@
|
|||
Provide a better error message if attempting an editable install of a
|
||||
directory with a ``pyproject.toml`` but no ``setup.py``.
|
|
@ -2,6 +2,7 @@ from __future__ import absolute_import
|
|||
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pip._vendor import pytoml, six
|
||||
|
||||
|
@ -20,6 +21,17 @@ def _is_list_of_str(obj):
|
|||
)
|
||||
|
||||
|
||||
def make_pyproject_path(setup_py_dir):
|
||||
# type: (str) -> str
|
||||
path = os.path.join(setup_py_dir, 'pyproject.toml')
|
||||
|
||||
# Python2 __file__ should not be unicode
|
||||
if six.PY2 and isinstance(path, six.text_type):
|
||||
path = path.encode(sys.getfilesystemencoding())
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def load_pyproject_toml(
|
||||
use_pep517, # type: Optional[bool]
|
||||
pyproject_toml, # type: str
|
||||
|
|
|
@ -23,6 +23,7 @@ from pip._internal.download import (
|
|||
from pip._internal.exceptions import InstallationError
|
||||
from pip._internal.models.index import PyPI, TestPyPI
|
||||
from pip._internal.models.link import Link
|
||||
from pip._internal.pyproject import make_pyproject_path
|
||||
from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.utils.misc import is_installable_dir
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
@ -77,10 +78,18 @@ def parse_editable(editable_req):
|
|||
|
||||
if os.path.isdir(url_no_extras):
|
||||
if not os.path.exists(os.path.join(url_no_extras, 'setup.py')):
|
||||
raise InstallationError(
|
||||
"Directory %r is not installable. File 'setup.py' not found." %
|
||||
url_no_extras
|
||||
msg = (
|
||||
'File "setup.py" not found. Directory cannot be installed '
|
||||
'in editable mode: {}'.format(os.path.abspath(url_no_extras))
|
||||
)
|
||||
pyproject_path = make_pyproject_path(url_no_extras)
|
||||
if os.path.isfile(pyproject_path):
|
||||
msg += (
|
||||
'\n(A "pyproject.toml" file was found, but editable '
|
||||
'mode currently requires a setup.py based build.)'
|
||||
)
|
||||
raise InstallationError(msg)
|
||||
|
||||
# Treating it as code that has already been checked out
|
||||
url_no_extras = path_to_url(url_no_extras)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ from pip._internal.locations import (
|
|||
PIP_DELETE_MARKER_FILENAME, running_under_virtualenv,
|
||||
)
|
||||
from pip._internal.models.link import Link
|
||||
from pip._internal.pyproject import load_pyproject_toml
|
||||
from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path
|
||||
from pip._internal.req.req_uninstall import UninstallPathSet
|
||||
from pip._internal.utils.compat import native_str
|
||||
from pip._internal.utils.hashes import Hashes
|
||||
|
@ -471,13 +471,7 @@ class InstallRequirement(object):
|
|||
# type: () -> str
|
||||
assert self.source_dir, "No source dir for %s" % self
|
||||
|
||||
pp_toml = os.path.join(self.setup_py_dir, 'pyproject.toml')
|
||||
|
||||
# Python2 __file__ should not be unicode
|
||||
if six.PY2 and isinstance(pp_toml, six.text_type):
|
||||
pp_toml = pp_toml.encode(sys.getfilesystemencoding())
|
||||
|
||||
return pp_toml
|
||||
return make_pyproject_path(self.setup_py_dir)
|
||||
|
||||
def load_pyproject_toml(self):
|
||||
# type: () -> None
|
||||
|
|
|
@ -491,13 +491,41 @@ def test_install_from_local_directory_with_no_setup_py(script, data):
|
|||
assert "Neither 'setup.py' nor 'pyproject.toml' found." in result.stderr
|
||||
|
||||
|
||||
def test_editable_install_from_local_directory_with_no_setup_py(script, data):
|
||||
def test_editable_install__local_dir_no_setup_py(
|
||||
script, data, deprecated_python):
|
||||
"""
|
||||
Test installing from a local directory with no 'setup.py'.
|
||||
Test installing in editable mode from a local directory with no setup.py.
|
||||
"""
|
||||
result = script.pip('install', '-e', data.root, expect_error=True)
|
||||
assert not result.files_created
|
||||
assert "is not installable. File 'setup.py' not found." in result.stderr
|
||||
|
||||
msg = result.stderr
|
||||
if deprecated_python:
|
||||
assert 'File "setup.py" not found. ' in msg
|
||||
else:
|
||||
assert msg.startswith('File "setup.py" not found. ')
|
||||
assert 'pyproject.toml' not in msg
|
||||
|
||||
|
||||
def test_editable_install__local_dir_no_setup_py_with_pyproject(
|
||||
script, deprecated_python):
|
||||
"""
|
||||
Test installing in editable mode from a local directory with no setup.py
|
||||
but that does have pyproject.toml.
|
||||
"""
|
||||
local_dir = script.scratch_path.join('temp').mkdir()
|
||||
pyproject_path = local_dir.join('pyproject.toml')
|
||||
pyproject_path.write('')
|
||||
|
||||
result = script.pip('install', '-e', local_dir, expect_error=True)
|
||||
assert not result.files_created
|
||||
|
||||
msg = result.stderr
|
||||
if deprecated_python:
|
||||
assert 'File "setup.py" not found. ' in msg
|
||||
else:
|
||||
assert msg.startswith('File "setup.py" not found. ')
|
||||
assert 'A "pyproject.toml" file was found' in msg
|
||||
|
||||
|
||||
@pytest.mark.skipif("sys.version_info >= (3,4)")
|
||||
|
|
Loading…
Reference in a new issue