Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove URL validation from requirement parsing #684

Merged
merged 2 commits into from Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Expand Up @@ -5,6 +5,7 @@ Changelog
~~~~~~~~~~~~

* Enforce that the entire marker string is parsed (:issue:`687`)
* Requirement parsing no longer automatically validates the URL (:issue:`120`)

23.1 - 2023-04-12
~~~~~~~~~~~~~~~~~
Expand Down
7 changes: 7 additions & 0 deletions docs/requirements.rst
Expand Up @@ -56,6 +56,13 @@ Usage
>>> requirements1 == requirements2
True

.. versionchanged:: 23.2

When a requirement is specified with a URL, the :class:`Requirement` class
used to check the URL and reject values containing invalid scheme and
netloc combinations. This is no longer performed since PEP 508 does not
specify such rules, and the check incorrectly disallows valid requirement
strings from being parsed.

Reference
---------
Expand Down
14 changes: 1 addition & 13 deletions src/packaging/requirements.py
Expand Up @@ -2,7 +2,6 @@
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

import urllib.parse
from typing import Any, List, Optional, Set

from ._parser import parse_requirement as _parse_requirement
Expand Down Expand Up @@ -37,18 +36,7 @@ def __init__(self, requirement_string: str) -> None:
raise InvalidRequirement(str(e)) from e

self.name: str = parsed.name
if parsed.url:
parsed_url = urllib.parse.urlparse(parsed.url)
if parsed_url.scheme == "file":
if urllib.parse.urlunparse(parsed_url) != parsed.url:
raise InvalidRequirement("Invalid URL given")
elif not (parsed_url.scheme and parsed_url.netloc) or (
not parsed_url.scheme and not parsed_url.netloc
):
raise InvalidRequirement(f"Invalid URL: {parsed.url}")
self.url: Optional[str] = parsed.url
else:
self.url = None
self.url: Optional[str] = parsed.url or None
self.extras: Set[str] = set(parsed.extras if parsed.extras else [])
self.specifier: SpecifierSet = SpecifierSet(parsed.specifier)
self.marker: Optional[Marker] = None
Expand Down
22 changes: 3 additions & 19 deletions tests/test_requirements.py
Expand Up @@ -78,6 +78,7 @@
("git+https://git.example.com/MyProject.git@master", ""),
("git+https://git.example.com/MyProject.git@v1.0", ""),
("git+https://git.example.com/MyProject.git@refs/pull/123/head", ""),
("gopher:/foo/com", ""),
(None, "==={ws}arbitrarystring"),
(None, "({ws}==={ws}arbitrarystring{ws})"),
(None, "=={ws}1.0"),
Expand Down Expand Up @@ -164,6 +165,8 @@ def test_valid_marker(self, marker: str) -> None:
[
"file:///absolute/path",
"file://.",
"file:.",
"file:/.",
],
)
def test_file_url(self, url: str) -> None:
Expand Down Expand Up @@ -503,25 +506,6 @@ def test_error_invalid_marker_with_invalid_op(self) -> None:
" ^"
)

@pytest.mark.parametrize(
"url",
[
"gopher:/foo/com",
"file:.",
"file:/.",
],
)
def test_error_on_invalid_url(self, url: str) -> None:
# GIVEN
to_parse = f"name @ {url}"

# WHEN
with pytest.raises(InvalidRequirement) as ctx:
Requirement(to_parse)

# THEN
assert "Invalid URL" in ctx.exconly()

def test_error_on_legacy_version_outside_triple_equals(self) -> None:
# GIVEN
to_parse = "name==1.0.org1"
Expand Down