1
1
from __future__ import annotations
2
2
3
3
import functools
4
- import itertools
5
4
import logging
6
5
import os
7
6
import posixpath
19
18
from pip ._internal .utils .filetypes import WHEEL_EXTENSION
20
19
from pip ._internal .utils .hashes import Hashes
21
20
from pip ._internal .utils .misc import (
22
- pairwise ,
23
21
redact_auth_from_url ,
24
22
split_auth_from_netloc ,
25
23
splitext ,
@@ -113,12 +111,12 @@ def supported_hashes(hashes: dict[str, str] | None) -> dict[str, str] | None:
113
111
return hashes
114
112
115
113
116
- def _clean_url_path_part (part : str ) -> str :
114
+ def _clean_url_path_part (part : str , safe : str = "/" ) -> str :
117
115
"""
118
116
Clean a "part" of a URL path (i.e. after splitting on "@" characters).
119
117
"""
120
118
# We unquote prior to quoting to make sure nothing is double quoted.
121
- return urllib .parse .quote (urllib .parse .unquote (part ))
119
+ return urllib .parse .quote (urllib .parse .unquote (part ), safe )
122
120
123
121
124
122
def _clean_file_url_path (part : str ) -> str :
@@ -140,6 +138,7 @@ def _clean_file_url_path(part: str) -> str:
140
138
141
139
# percent-encoded: /
142
140
_reserved_chars_re = re .compile ("(@|%2F)" , re .IGNORECASE )
141
+ _escaped_chars_re = re .compile ("---PIP-(%40|/)-PIP---" )
143
142
144
143
145
144
def _clean_url_path (path : str , is_local_path : bool ) -> str :
@@ -151,17 +150,12 @@ def _clean_url_path(path: str, is_local_path: bool) -> str:
151
150
else :
152
151
clean_func = _clean_url_path_part
153
152
154
- # Split on the reserved characters prior to cleaning so that
153
+ # Escape the reserved characters prior to cleaning so that
155
154
# revision strings in VCS URLs are properly preserved.
156
- parts = _reserved_chars_re .split (path )
157
-
158
- cleaned_parts = []
159
- for to_clean , reserved in pairwise (itertools .chain (parts , ["" ])):
160
- cleaned_parts .append (clean_func (to_clean ))
161
- # Normalize %xx escapes (e.g. %2f -> %2F)
162
- cleaned_parts .append (reserved .upper ())
163
-
164
- return "" .join (cleaned_parts )
155
+ path = _reserved_chars_re .sub (r"---PIP-\1-PIP---" , path )
156
+ path = clean_func (path )
157
+ path = _escaped_chars_re .sub (lambda m : _clean_url_path_part (m [1 ], safe = "@" ), path )
158
+ return path
165
159
166
160
167
161
def _ensure_quoted_url (url : str ) -> str :
0 commit comments