diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8bf68c6e..17ac85d5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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 ~~~~~~~~~~~~~~~~~ diff --git a/docs/requirements.rst b/docs/requirements.rst index 4b4813a1..2c71ec2b 100644 --- a/docs/requirements.rst +++ b/docs/requirements.rst @@ -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 --------- diff --git a/src/packaging/requirements.py b/src/packaging/requirements.py index f34bfa85..e828c61f 100644 --- a/src/packaging/requirements.py +++ b/src/packaging/requirements.py @@ -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 @@ -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 diff --git a/tests/test_requirements.py b/tests/test_requirements.py index 4ccc4770..45d3937e 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -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"), @@ -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: @@ -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"