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):