From 5d9b4c90b34aa07efa80f89f66f389c4de6c7fe1 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Thu, 30 Mar 2023 16:43:37 +0800 Subject: [PATCH] Remove URL validation from requirement parsing The scheme and path validation logic limits how users of the library can provide URL support. This limitation is lifted, and dependants now need to implement their own URL validation logic they see fit. --- CHANGELOG.rst | 2 +- docs/requirements.rst | 7 +++++++ src/packaging/requirements.py | 14 +------------- tests/test_requirements.py | 22 +++------------------- 4 files changed, 12 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bb6e885d..37ea19df 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,7 +4,7 @@ Changelog *unreleased* ~~~~~~~~~~~~ -No unreleased changes. +* Requirement parsing no longer automatically validates the URL (:issue:`120`) 23.0 - 2023-01-08 ~~~~~~~~~~~~~~~~~ diff --git a/docs/requirements.rst b/docs/requirements.rst index 4b4813a1..0ec58e8c 100644 --- a/docs/requirements.rst +++ b/docs/requirements.rst @@ -56,6 +56,13 @@ Usage >>> requirements1 == requirements2 True +.. versionchanged:: 23.1 + + 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"