Skip to content

Commit

Permalink
Remove URL validation from requirement parsing
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
uranusjr committed Apr 13, 2023
1 parent 55584fb commit d09588c
Show file tree
Hide file tree
Showing 4 changed files with 12 additions and 32 deletions.
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.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
---------
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

0 comments on commit d09588c

Please sign in to comment.