From 265b4e7e650f43c8d3381b2334764c781917c74a Mon Sep 17 00:00:00 2001 From: Nicolas Bock Date: Wed, 24 Apr 2019 15:33:43 -0600 Subject: [PATCH] Protect `@` as safe character when cleaning URLs Remote URLs that contain a link to a git repository and a tag reference will have the `@` character converted into `%40`. This is incorrect. Fixes: #6437 Signed-off-by: Nicolas Bock --- news/6440.bugfix | 2 ++ src/pip/_internal/index.py | 5 ++++- tests/unit/test_index.py | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 news/6440.bugfix diff --git a/news/6440.bugfix b/news/6440.bugfix new file mode 100644 index 000000000..b3a2c8518 --- /dev/null +++ b/news/6440.bugfix @@ -0,0 +1,2 @@ +Fix a regression that caused `@` to be quoted in pypiserver links. +This interfered with parsing the revision string from VCS urls. diff --git a/src/pip/_internal/index.py b/src/pip/_internal/index.py index 81278e8f6..ff614b346 100644 --- a/src/pip/_internal/index.py +++ b/src/pip/_internal/index.py @@ -336,6 +336,7 @@ class FoundCandidates(object): * `evaluator`: A CandidateEvaluator object to sort applicable candidates by order of preference. """ + def __init__( self, candidates, # type: List[InstallationCandidate] @@ -1061,7 +1062,9 @@ def _clean_link(url): path = urllib_request.pathname2url( urllib_request.url2pathname(result.path)) else: - path = urllib_parse.quote(urllib_parse.unquote(result.path)) + # In addition to the `/` character we protect `@` so that + # revision strings in VCS URLs are properly parsed. + path = urllib_parse.quote(urllib_parse.unquote(result.path), safe="/@") return urllib_parse.urlunparse(result._replace(path=path)) diff --git a/tests/unit/test_index.py b/tests/unit/test_index.py index bb12727e1..3bdd5695d 100644 --- a/tests/unit/test_index.py +++ b/tests/unit/test_index.py @@ -308,7 +308,10 @@ def test_request_retries(caplog): # URL with something that looks like a drive letter, but is # not. The `:` should be quoted. ("https://localhost.localdomain/T:/path/", - "https://localhost.localdomain/T%3A/path/") + "https://localhost.localdomain/T%3A/path/"), + # VCS URL containing revision string. + ("git+ssh://example.com/path to/repo.git@1.0#egg=my-package-1.0", + "git+ssh://example.com/path%20to/repo.git@1.0#egg=my-package-1.0") ] ) def test_clean_link(url, clean_url):