Skip to content

Commit

Permalink
Improve the error message when reading a bad requirements file.
Browse files Browse the repository at this point in the history
  • Loading branch information
cjerdonek committed Jun 15, 2019
1 parent a240a98 commit e5a730a
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 8 deletions.
2 changes: 2 additions & 0 deletions news/6527.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add the line number and file location to the error message when reading an
invalid requirements file in certain situations.
19 changes: 15 additions & 4 deletions src/pip/_internal/req/constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,15 @@ def install_req_from_line(
isolated=False, # type: bool
options=None, # type: Optional[Dict[str, Any]]
wheel_cache=None, # type: Optional[WheelCache]
constraint=False # type: bool
constraint=False, # type: bool
line_source=None, # type: Optional[str]
):
# type: (...) -> InstallRequirement
"""Creates an InstallRequirement from a name, which might be a
requirement, directory containing 'setup.py', filename, or URL.
:param line_source: An optional string describing where the line is from,
for logging purposes in case of an error.
"""
if is_url(name):
marker_sep = '; '
Expand Down Expand Up @@ -289,10 +293,17 @@ def install_req_from_line(
not any(op in req_as_string for op in operators)):
add_msg = "= is not a valid operator. Did you mean == ?"
else:
add_msg = ""
raise InstallationError(
"Invalid requirement: '%s'\n%s" % (req_as_string, add_msg)
add_msg = ''
if line_source is None:
source = ''
else:
source = ' (from {})'.format(line_source)
msg = (
'Invalid requirement: {!r}{}'.format(req_as_string, source)
)
if add_msg:
msg += '\nHint: {}'.format(add_msg)
raise InstallationError(msg)
else:
req = None

Expand Down
12 changes: 9 additions & 3 deletions src/pip/_internal/req/req_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def process_line(
session=None, # type: Optional[PipSession]
wheel_cache=None, # type: Optional[WheelCache]
use_pep517=None, # type: Optional[bool]
constraint=False # type: bool
constraint=False, # type: bool
):
# type: (...) -> Iterator[InstallRequirement]
"""Process a single requirements line; This can result in creating/yielding
Expand Down Expand Up @@ -187,10 +187,16 @@ def process_line(
for dest in SUPPORTED_OPTIONS_REQ_DEST:
if dest in opts.__dict__ and opts.__dict__[dest]:
req_options[dest] = opts.__dict__[dest]
line_source = 'line {} of {}'.format(line_number, filename)
yield install_req_from_line(
args_str, line_comes_from, constraint=constraint,
args_str,
comes_from=line_comes_from,
use_pep517=use_pep517,
isolated=isolated, options=req_options, wheel_cache=wheel_cache
isolated=isolated,
options=req_options,
wheel_cache=wheel_cache,
constraint=constraint,
line_source=line_source,
)

# yield an editable requirement
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_req.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ def test_unidentifiable_name(self):
with pytest.raises(InstallationError) as e:
install_req_from_line(test_name)
err_msg = e.value.args[0]
assert ("Invalid requirement: '%s'\n" % test_name) == err_msg
assert "Invalid requirement: '{}'".format(test_name) == err_msg

def test_requirement_file(self):
req_file_path = os.path.join(self.tempdir, 'test.txt')
Expand Down
20 changes: 20 additions & 0 deletions tests/unit/test_req_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,26 @@ def test_only_one_req_per_line(self):
with pytest.raises(InstallationError):
list(process_line("req1 req2", "file", 1))

def test_error_message(self):
"""
Test the error message if a parsing error occurs (all of path,
line number, and hint).
"""
iterator = process_line(
'my-package=1.0',
filename='path/requirements.txt',
line_number=3
)
with pytest.raises(InstallationError) as exc:
list(iterator)

expected = (
"Invalid requirement: 'my-package=1.0' "
'(from line 3 of path/requirements.txt)\n'
'Hint: = is not a valid operator. Did you mean == ?'
)
assert str(exc.value) == expected

def test_yield_line_requirement(self):
line = 'SomeProject'
filename = 'filename'
Expand Down

0 comments on commit e5a730a

Please sign in to comment.