Skip to content

Commit

Permalink
Report detailed information about invalid requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
sbidoul authored and pradyunsg committed Jun 10, 2024
1 parent d929bfe commit c3434bb
Show file tree
Hide file tree
Showing 5 changed files with 18 additions and 10 deletions.
1 change: 1 addition & 0 deletions news/12713.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Report informative messages about invalid requirements.
12 changes: 6 additions & 6 deletions src/pip/_internal/req/constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ def parse_req_from_editable(editable_req: str) -> RequirementParts:
if name is not None:
try:
req: Optional[Requirement] = Requirement(name)
except InvalidRequirement:
raise InstallationError(f"Invalid requirement: '{name}'")
except InvalidRequirement as exc:
raise InstallationError(f"Invalid requirement: {name!r}: {exc}")
else:
req = None

Expand Down Expand Up @@ -360,7 +360,7 @@ def with_source(text: str) -> str:
def _parse_req_string(req_as_string: str) -> Requirement:
try:
return get_requirement(req_as_string)
except InvalidRequirement:
except InvalidRequirement as exc:
if os.path.sep in req_as_string:
add_msg = "It looks like a path."
add_msg += deduce_helpful_msg(req_as_string)
Expand All @@ -370,7 +370,7 @@ def _parse_req_string(req_as_string: str) -> Requirement:
add_msg = "= is not a valid operator. Did you mean == ?"
else:
add_msg = ""
msg = with_source(f"Invalid requirement: {req_as_string!r}")
msg = with_source(f"Invalid requirement: {req_as_string!r}: {exc}")
if add_msg:
msg += f"\nHint: {add_msg}"
raise InstallationError(msg)
Expand Down Expand Up @@ -429,8 +429,8 @@ def install_req_from_req_string(
) -> InstallRequirement:
try:
req = get_requirement(req_string)
except InvalidRequirement:
raise InstallationError(f"Invalid requirement: '{req_string}'")
except InvalidRequirement as exc:
raise InstallationError(f"Invalid requirement: {req_string!r}: {exc}")

domains_not_allowed = [
PyPI.file_storage_domain,
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 @@ -734,7 +734,7 @@ def test_unidentifiable_name(self) -> None:
with pytest.raises(InstallationError) as e:
install_req_from_line(test_name)
err_msg = e.value.args[0]
assert f"Invalid requirement: '{test_name}'" == err_msg
assert err_msg.startswith(f"Invalid requirement: '{test_name}'")

def test_requirement_file(self) -> None:
req_file_path = os.path.join(self.tempdir, "test.txt")
Expand Down
6 changes: 4 additions & 2 deletions tests/unit/test_req_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,10 @@ def test_error_message(self, line_processor: LineProcessor) -> None:
)

expected = (
"Invalid requirement: 'my-package=1.0' "
"(from line 3 of path/requirements.txt)\n"
"Invalid requirement: 'my-package=1.0': "
"Expected end or semicolon (after name and no valid version specifier)\n"
" my-package=1.0\n"
" ^ (from line 3 of path/requirements.txt)\n"
"Hint: = is not a valid operator. Did you mean == ?"
)
assert str(exc.value) == expected
Expand Down
7 changes: 6 additions & 1 deletion tests/unit/test_req_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ def test_install_req_from_string_invalid_requirement(self) -> None:
with pytest.raises(InstallationError) as excinfo:
install_req_from_req_string("http:/this/is/invalid")

assert str(excinfo.value) == ("Invalid requirement: 'http:/this/is/invalid'")
assert str(excinfo.value) == (
"Invalid requirement: 'http:/this/is/invalid': "
"Expected end or semicolon (after name and no valid version specifier)\n"
" http:/this/is/invalid\n"
" ^"
)

def test_install_req_from_string_without_comes_from(self) -> None:
"""
Expand Down

0 comments on commit c3434bb

Please sign in to comment.