pip 25.1.1__py3-none-any.whl → 25.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pip/__init__.py +3 -3
- pip/_internal/__init__.py +2 -2
- pip/_internal/build_env.py +186 -94
- pip/_internal/cache.py +17 -15
- pip/_internal/cli/autocompletion.py +13 -4
- pip/_internal/cli/base_command.py +18 -7
- pip/_internal/cli/cmdoptions.py +57 -80
- pip/_internal/cli/command_context.py +4 -3
- pip/_internal/cli/index_command.py +11 -9
- pip/_internal/cli/main.py +3 -2
- pip/_internal/cli/main_parser.py +4 -3
- pip/_internal/cli/parser.py +24 -20
- pip/_internal/cli/progress_bars.py +19 -12
- pip/_internal/cli/req_command.py +57 -33
- pip/_internal/cli/spinners.py +81 -5
- pip/_internal/commands/__init__.py +5 -3
- pip/_internal/commands/cache.py +18 -15
- pip/_internal/commands/check.py +1 -2
- pip/_internal/commands/completion.py +1 -2
- pip/_internal/commands/configuration.py +26 -18
- pip/_internal/commands/debug.py +8 -6
- pip/_internal/commands/download.py +6 -10
- pip/_internal/commands/freeze.py +2 -3
- pip/_internal/commands/hash.py +1 -2
- pip/_internal/commands/help.py +1 -2
- pip/_internal/commands/index.py +15 -9
- pip/_internal/commands/inspect.py +4 -4
- pip/_internal/commands/install.py +63 -53
- pip/_internal/commands/list.py +35 -26
- pip/_internal/commands/lock.py +4 -8
- pip/_internal/commands/search.py +14 -12
- pip/_internal/commands/show.py +14 -11
- pip/_internal/commands/uninstall.py +1 -2
- pip/_internal/commands/wheel.py +7 -13
- pip/_internal/configuration.py +40 -27
- pip/_internal/distributions/base.py +6 -4
- pip/_internal/distributions/installed.py +8 -4
- pip/_internal/distributions/sdist.py +33 -27
- pip/_internal/distributions/wheel.py +6 -4
- pip/_internal/exceptions.py +78 -42
- pip/_internal/index/collector.py +24 -29
- pip/_internal/index/package_finder.py +73 -64
- pip/_internal/index/sources.py +17 -14
- pip/_internal/locations/__init__.py +18 -16
- pip/_internal/locations/_distutils.py +12 -11
- pip/_internal/locations/_sysconfig.py +5 -4
- pip/_internal/locations/base.py +4 -3
- pip/_internal/main.py +2 -2
- pip/_internal/metadata/__init__.py +14 -7
- pip/_internal/metadata/_json.py +5 -4
- pip/_internal/metadata/base.py +22 -27
- pip/_internal/metadata/importlib/_compat.py +6 -4
- pip/_internal/metadata/importlib/_dists.py +20 -19
- pip/_internal/metadata/importlib/_envs.py +9 -6
- pip/_internal/metadata/pkg_resources.py +11 -14
- pip/_internal/models/direct_url.py +24 -21
- pip/_internal/models/format_control.py +5 -5
- pip/_internal/models/installation_report.py +4 -3
- pip/_internal/models/link.py +39 -34
- pip/_internal/models/pylock.py +27 -22
- pip/_internal/models/search_scope.py +6 -7
- pip/_internal/models/selection_prefs.py +3 -3
- pip/_internal/models/target_python.py +10 -9
- pip/_internal/models/wheel.py +12 -71
- pip/_internal/network/auth.py +20 -22
- pip/_internal/network/cache.py +28 -17
- pip/_internal/network/download.py +169 -141
- pip/_internal/network/lazy_wheel.py +15 -10
- pip/_internal/network/session.py +32 -27
- pip/_internal/network/utils.py +2 -2
- pip/_internal/network/xmlrpc.py +2 -2
- pip/_internal/operations/build/build_tracker.py +10 -8
- pip/_internal/operations/build/wheel.py +7 -6
- pip/_internal/operations/build/wheel_editable.py +7 -6
- pip/_internal/operations/check.py +21 -26
- pip/_internal/operations/freeze.py +12 -9
- pip/_internal/operations/install/wheel.py +49 -41
- pip/_internal/operations/prepare.py +42 -31
- pip/_internal/pyproject.py +7 -69
- pip/_internal/req/__init__.py +12 -12
- pip/_internal/req/constructors.py +68 -62
- pip/_internal/req/req_dependency_group.py +7 -11
- pip/_internal/req/req_file.py +32 -36
- pip/_internal/req/req_install.py +64 -170
- pip/_internal/req/req_set.py +4 -5
- pip/_internal/req/req_uninstall.py +20 -17
- pip/_internal/resolution/base.py +3 -3
- pip/_internal/resolution/legacy/resolver.py +21 -20
- pip/_internal/resolution/resolvelib/base.py +16 -13
- pip/_internal/resolution/resolvelib/candidates.py +49 -37
- pip/_internal/resolution/resolvelib/factory.py +72 -50
- pip/_internal/resolution/resolvelib/found_candidates.py +11 -9
- pip/_internal/resolution/resolvelib/provider.py +24 -20
- pip/_internal/resolution/resolvelib/reporter.py +26 -11
- pip/_internal/resolution/resolvelib/requirements.py +8 -6
- pip/_internal/resolution/resolvelib/resolver.py +41 -29
- pip/_internal/self_outdated_check.py +19 -9
- pip/_internal/utils/appdirs.py +1 -2
- pip/_internal/utils/compat.py +7 -1
- pip/_internal/utils/compatibility_tags.py +17 -16
- pip/_internal/utils/deprecation.py +11 -9
- pip/_internal/utils/direct_url_helpers.py +2 -2
- pip/_internal/utils/egg_link.py +6 -5
- pip/_internal/utils/entrypoints.py +3 -2
- pip/_internal/utils/filesystem.py +20 -5
- pip/_internal/utils/filetypes.py +4 -6
- pip/_internal/utils/glibc.py +6 -5
- pip/_internal/utils/hashes.py +9 -6
- pip/_internal/utils/logging.py +8 -5
- pip/_internal/utils/misc.py +37 -45
- pip/_internal/utils/packaging.py +3 -2
- pip/_internal/utils/retry.py +7 -4
- pip/_internal/utils/subprocess.py +20 -17
- pip/_internal/utils/temp_dir.py +10 -12
- pip/_internal/utils/unpacking.py +31 -4
- pip/_internal/utils/urls.py +1 -1
- pip/_internal/utils/virtualenv.py +3 -2
- pip/_internal/utils/wheel.py +3 -4
- pip/_internal/vcs/bazaar.py +26 -8
- pip/_internal/vcs/git.py +59 -24
- pip/_internal/vcs/mercurial.py +34 -11
- pip/_internal/vcs/subversion.py +27 -16
- pip/_internal/vcs/versioncontrol.py +56 -51
- pip/_internal/wheel_builder.py +30 -101
- pip/_vendor/README.rst +180 -0
- pip/_vendor/cachecontrol/LICENSE.txt +13 -0
- pip/_vendor/cachecontrol/__init__.py +1 -1
- pip/_vendor/certifi/LICENSE +20 -0
- pip/_vendor/certifi/__init__.py +1 -1
- pip/_vendor/certifi/cacert.pem +164 -261
- pip/_vendor/certifi/core.py +1 -32
- pip/_vendor/dependency_groups/LICENSE.txt +9 -0
- pip/_vendor/distlib/LICENSE.txt +284 -0
- pip/_vendor/distlib/__init__.py +2 -2
- pip/_vendor/distlib/scripts.py +1 -1
- pip/_vendor/distro/LICENSE +202 -0
- pip/_vendor/idna/LICENSE.md +31 -0
- pip/_vendor/msgpack/COPYING +14 -0
- pip/_vendor/msgpack/__init__.py +2 -2
- pip/_vendor/packaging/LICENSE +3 -0
- pip/_vendor/packaging/LICENSE.APACHE +177 -0
- pip/_vendor/packaging/LICENSE.BSD +23 -0
- pip/_vendor/pkg_resources/LICENSE +17 -0
- pip/_vendor/pkg_resources/__init__.py +1 -1
- pip/_vendor/platformdirs/LICENSE +21 -0
- pip/_vendor/platformdirs/api.py +1 -1
- pip/_vendor/platformdirs/macos.py +10 -8
- pip/_vendor/platformdirs/version.py +16 -3
- pip/_vendor/pygments/LICENSE +25 -0
- pip/_vendor/pygments/__init__.py +1 -1
- pip/_vendor/pyproject_hooks/LICENSE +21 -0
- pip/_vendor/requests/LICENSE +175 -0
- pip/_vendor/requests/__version__.py +2 -2
- pip/_vendor/requests/adapters.py +17 -40
- pip/_vendor/requests/compat.py +12 -0
- pip/_vendor/requests/models.py +3 -1
- pip/_vendor/requests/sessions.py +1 -1
- pip/_vendor/requests/utils.py +6 -16
- pip/_vendor/resolvelib/LICENSE +13 -0
- pip/_vendor/resolvelib/__init__.py +3 -3
- pip/_vendor/resolvelib/reporters.py +1 -1
- pip/_vendor/resolvelib/resolvers/__init__.py +4 -4
- pip/_vendor/resolvelib/resolvers/abstract.py +3 -3
- pip/_vendor/resolvelib/resolvers/resolution.py +96 -10
- pip/_vendor/rich/LICENSE +19 -0
- pip/_vendor/rich/__main__.py +12 -40
- pip/_vendor/rich/_inspect.py +1 -1
- pip/_vendor/rich/_ratio.py +1 -7
- pip/_vendor/rich/align.py +1 -7
- pip/_vendor/rich/box.py +1 -7
- pip/_vendor/rich/console.py +25 -20
- pip/_vendor/rich/control.py +1 -7
- pip/_vendor/rich/diagnose.py +1 -0
- pip/_vendor/rich/emoji.py +1 -6
- pip/_vendor/rich/live.py +32 -7
- pip/_vendor/rich/live_render.py +1 -7
- pip/_vendor/rich/logging.py +1 -1
- pip/_vendor/rich/panel.py +3 -4
- pip/_vendor/rich/progress.py +15 -15
- pip/_vendor/rich/spinner.py +7 -13
- pip/_vendor/rich/style.py +7 -11
- pip/_vendor/rich/syntax.py +24 -5
- pip/_vendor/rich/traceback.py +32 -17
- pip/_vendor/tomli/LICENSE +21 -0
- pip/_vendor/tomli/__init__.py +1 -1
- pip/_vendor/tomli/_parser.py +28 -21
- pip/_vendor/tomli/_re.py +8 -5
- pip/_vendor/tomli_w/LICENSE +21 -0
- pip/_vendor/truststore/LICENSE +21 -0
- pip/_vendor/truststore/__init__.py +1 -1
- pip/_vendor/truststore/_api.py +15 -7
- pip/_vendor/truststore/_openssl.py +3 -1
- pip/_vendor/urllib3/LICENSE.txt +21 -0
- pip/_vendor/vendor.txt +11 -12
- {pip-25.1.1.dist-info → pip-25.3.dist-info}/METADATA +32 -11
- {pip-25.1.1.dist-info → pip-25.3.dist-info}/RECORD +221 -192
- {pip-25.1.1.dist-info → pip-25.3.dist-info}/WHEEL +1 -2
- pip-25.3.dist-info/entry_points.txt +4 -0
- {pip-25.1.1.dist-info → pip-25.3.dist-info}/licenses/AUTHORS.txt +21 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
- pip-25.3.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
- pip/_internal/operations/build/metadata_legacy.py +0 -73
- pip/_internal/operations/build/wheel_legacy.py +0 -118
- pip/_internal/operations/install/editable_legacy.py +0 -46
- pip/_internal/utils/setuptools_build.py +0 -147
- pip/_vendor/distlib/database.py +0 -1329
- pip/_vendor/distlib/index.py +0 -508
- pip/_vendor/distlib/locators.py +0 -1295
- pip/_vendor/distlib/manifest.py +0 -384
- pip/_vendor/distlib/markers.py +0 -162
- pip/_vendor/distlib/metadata.py +0 -1031
- pip/_vendor/distlib/version.py +0 -750
- pip/_vendor/distlib/wheel.py +0 -1100
- pip/_vendor/typing_extensions.py +0 -4584
- pip-25.1.1.dist-info/entry_points.txt +0 -3
- pip-25.1.1.dist-info/top_level.txt +0 -1
- {pip-25.1.1.dist-info → pip-25.3.dist-info}/licenses/LICENSE.txt +0 -0
pip/_internal/utils/hashes.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import hashlib
|
|
2
|
-
from
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from typing import TYPE_CHECKING, BinaryIO, NoReturn
|
|
3
6
|
|
|
4
7
|
from pip._internal.exceptions import HashMismatch, HashMissing, InstallationError
|
|
5
8
|
from pip._internal.utils.misc import read_chunks
|
|
@@ -24,7 +27,7 @@ class Hashes:
|
|
|
24
27
|
|
|
25
28
|
"""
|
|
26
29
|
|
|
27
|
-
def __init__(self, hashes:
|
|
30
|
+
def __init__(self, hashes: dict[str, list[str]] | None = None) -> None:
|
|
28
31
|
"""
|
|
29
32
|
:param hashes: A dict of algorithm names pointing to lists of allowed
|
|
30
33
|
hex digests
|
|
@@ -36,7 +39,7 @@ class Hashes:
|
|
|
36
39
|
allowed[alg] = [k.lower() for k in sorted(keys)]
|
|
37
40
|
self._allowed = allowed
|
|
38
41
|
|
|
39
|
-
def __and__(self, other:
|
|
42
|
+
def __and__(self, other: Hashes) -> Hashes:
|
|
40
43
|
if not isinstance(other, Hashes):
|
|
41
44
|
return NotImplemented
|
|
42
45
|
|
|
@@ -86,7 +89,7 @@ class Hashes:
|
|
|
86
89
|
return
|
|
87
90
|
self._raise(gots)
|
|
88
91
|
|
|
89
|
-
def _raise(self, gots:
|
|
92
|
+
def _raise(self, gots: dict[str, _Hash]) -> NoReturn:
|
|
90
93
|
raise HashMismatch(self._allowed, gots)
|
|
91
94
|
|
|
92
95
|
def check_against_file(self, file: BinaryIO) -> None:
|
|
@@ -101,7 +104,7 @@ class Hashes:
|
|
|
101
104
|
with open(path, "rb") as file:
|
|
102
105
|
return self.check_against_file(file)
|
|
103
106
|
|
|
104
|
-
def has_one_of(self, hashes:
|
|
107
|
+
def has_one_of(self, hashes: dict[str, str]) -> bool:
|
|
105
108
|
"""Return whether any of the given hashes are allowed."""
|
|
106
109
|
for hash_name, hex_digest in hashes.items():
|
|
107
110
|
if self.is_hash_allowed(hash_name, hex_digest):
|
|
@@ -143,5 +146,5 @@ class MissingHashes(Hashes):
|
|
|
143
146
|
# empty list, it will never match, so an error will always raise.
|
|
144
147
|
super().__init__(hashes={FAVORITE_HASH: []})
|
|
145
148
|
|
|
146
|
-
def _raise(self, gots:
|
|
149
|
+
def _raise(self, gots: dict[str, _Hash]) -> NoReturn:
|
|
147
150
|
raise HashMissing(gots[FAVORITE_HASH].hexdigest())
|
pip/_internal/utils/logging.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import contextlib
|
|
2
4
|
import errno
|
|
3
5
|
import logging
|
|
@@ -5,10 +7,11 @@ import logging.handlers
|
|
|
5
7
|
import os
|
|
6
8
|
import sys
|
|
7
9
|
import threading
|
|
10
|
+
from collections.abc import Generator
|
|
8
11
|
from dataclasses import dataclass
|
|
9
12
|
from io import TextIOWrapper
|
|
10
13
|
from logging import Filter
|
|
11
|
-
from typing import Any, ClassVar
|
|
14
|
+
from typing import Any, ClassVar
|
|
12
15
|
|
|
13
16
|
from pip._vendor.rich.console import (
|
|
14
17
|
Console,
|
|
@@ -40,7 +43,7 @@ class BrokenStdoutLoggingError(Exception):
|
|
|
40
43
|
"""
|
|
41
44
|
|
|
42
45
|
|
|
43
|
-
def _is_broken_pipe_error(exc_class:
|
|
46
|
+
def _is_broken_pipe_error(exc_class: type[BaseException], exc: BaseException) -> bool:
|
|
44
47
|
if exc_class is BrokenPipeError:
|
|
45
48
|
return True
|
|
46
49
|
|
|
@@ -156,7 +159,7 @@ def get_console(*, stderr: bool = False) -> Console:
|
|
|
156
159
|
|
|
157
160
|
|
|
158
161
|
class RichPipStreamHandler(RichHandler):
|
|
159
|
-
KEYWORDS: ClassVar[
|
|
162
|
+
KEYWORDS: ClassVar[list[str] | None] = []
|
|
160
163
|
|
|
161
164
|
def __init__(self, console: Console) -> None:
|
|
162
165
|
super().__init__(
|
|
@@ -169,7 +172,7 @@ class RichPipStreamHandler(RichHandler):
|
|
|
169
172
|
|
|
170
173
|
# Our custom override on Rich's logger, to make things work as we need them to.
|
|
171
174
|
def emit(self, record: logging.LogRecord) -> None:
|
|
172
|
-
style:
|
|
175
|
+
style: Style | None = None
|
|
173
176
|
|
|
174
177
|
# If we are given a diagnostic error to present, present it with indentation.
|
|
175
178
|
if getattr(record, "rich", False):
|
|
@@ -240,7 +243,7 @@ class ExcludeLoggerFilter(Filter):
|
|
|
240
243
|
return not super().filter(record)
|
|
241
244
|
|
|
242
245
|
|
|
243
|
-
def setup_logging(verbosity: int, no_color: bool, user_log_file:
|
|
246
|
+
def setup_logging(verbosity: int, no_color: bool, user_log_file: str | None) -> int:
|
|
244
247
|
"""Configures and sets up all of the logging
|
|
245
248
|
|
|
246
249
|
Returns the requested logging level, as its integer value.
|
pip/_internal/utils/misc.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import errno
|
|
2
4
|
import getpass
|
|
3
5
|
import hashlib
|
|
@@ -9,6 +11,7 @@ import stat
|
|
|
9
11
|
import sys
|
|
10
12
|
import sysconfig
|
|
11
13
|
import urllib.parse
|
|
14
|
+
from collections.abc import Generator, Iterable, Iterator, Mapping, Sequence
|
|
12
15
|
from dataclasses import dataclass
|
|
13
16
|
from functools import partial
|
|
14
17
|
from io import StringIO
|
|
@@ -19,18 +22,9 @@ from typing import (
|
|
|
19
22
|
Any,
|
|
20
23
|
BinaryIO,
|
|
21
24
|
Callable,
|
|
22
|
-
Generator,
|
|
23
|
-
Iterable,
|
|
24
|
-
Iterator,
|
|
25
|
-
List,
|
|
26
|
-
Mapping,
|
|
27
25
|
Optional,
|
|
28
|
-
Sequence,
|
|
29
26
|
TextIO,
|
|
30
|
-
Tuple,
|
|
31
|
-
Type,
|
|
32
27
|
TypeVar,
|
|
33
|
-
Union,
|
|
34
28
|
cast,
|
|
35
29
|
)
|
|
36
30
|
|
|
@@ -64,9 +58,9 @@ __all__ = [
|
|
|
64
58
|
logger = logging.getLogger(__name__)
|
|
65
59
|
|
|
66
60
|
T = TypeVar("T")
|
|
67
|
-
ExcInfo =
|
|
68
|
-
VersionInfo =
|
|
69
|
-
NetlocTuple =
|
|
61
|
+
ExcInfo = tuple[type[BaseException], BaseException, TracebackType]
|
|
62
|
+
VersionInfo = tuple[int, int, int]
|
|
63
|
+
NetlocTuple = tuple[str, tuple[Optional[str], Optional[str]]]
|
|
70
64
|
OnExc = Callable[[FunctionType, Path, BaseException], Any]
|
|
71
65
|
OnErr = Callable[[FunctionType, Path, ExcInfo], Any]
|
|
72
66
|
|
|
@@ -80,7 +74,7 @@ def get_pip_version() -> str:
|
|
|
80
74
|
return f"pip {__version__} from {pip_pkg_dir} (python {get_major_minor_version()})"
|
|
81
75
|
|
|
82
76
|
|
|
83
|
-
def normalize_version_info(py_version_info:
|
|
77
|
+
def normalize_version_info(py_version_info: tuple[int, ...]) -> tuple[int, int, int]:
|
|
84
78
|
"""
|
|
85
79
|
Convert a tuple of ints representing a Python version to one of length
|
|
86
80
|
three.
|
|
@@ -123,9 +117,7 @@ def get_prog() -> str:
|
|
|
123
117
|
|
|
124
118
|
# Retry every half second for up to 3 seconds
|
|
125
119
|
@retry(stop_after_delay=3, wait=0.5)
|
|
126
|
-
def rmtree(
|
|
127
|
-
dir: str, ignore_errors: bool = False, onexc: Optional[OnExc] = None
|
|
128
|
-
) -> None:
|
|
120
|
+
def rmtree(dir: str, ignore_errors: bool = False, onexc: OnExc | None = None) -> None:
|
|
129
121
|
if ignore_errors:
|
|
130
122
|
onexc = _onerror_ignore
|
|
131
123
|
if onexc is None:
|
|
@@ -149,7 +141,7 @@ def _onerror_reraise(*_args: Any) -> None:
|
|
|
149
141
|
def rmtree_errorhandler(
|
|
150
142
|
func: FunctionType,
|
|
151
143
|
path: Path,
|
|
152
|
-
exc_info:
|
|
144
|
+
exc_info: ExcInfo | BaseException,
|
|
153
145
|
*,
|
|
154
146
|
onexc: OnExc = _onerror_reraise,
|
|
155
147
|
) -> None:
|
|
@@ -276,7 +268,7 @@ def format_size(bytes: float) -> str:
|
|
|
276
268
|
return f"{int(bytes)} bytes"
|
|
277
269
|
|
|
278
270
|
|
|
279
|
-
def tabulate(rows: Iterable[Iterable[Any]]) ->
|
|
271
|
+
def tabulate(rows: Iterable[Iterable[Any]]) -> tuple[list[str], list[int]]:
|
|
280
272
|
"""Return a list of formatted rows and a list of column sizes.
|
|
281
273
|
|
|
282
274
|
For example::
|
|
@@ -331,7 +323,7 @@ def normalize_path(path: str, resolve_symlinks: bool = True) -> str:
|
|
|
331
323
|
return os.path.normcase(path)
|
|
332
324
|
|
|
333
325
|
|
|
334
|
-
def splitext(path: str) ->
|
|
326
|
+
def splitext(path: str) -> tuple[str, str]:
|
|
335
327
|
"""Like os.path.splitext, but take off .tar too"""
|
|
336
328
|
base, ext = posixpath.splitext(path)
|
|
337
329
|
if base.lower().endswith(".tar"):
|
|
@@ -379,7 +371,7 @@ class StreamWrapper(StringIO):
|
|
|
379
371
|
orig_stream: TextIO
|
|
380
372
|
|
|
381
373
|
@classmethod
|
|
382
|
-
def from_stream(cls, orig_stream: TextIO) ->
|
|
374
|
+
def from_stream(cls, orig_stream: TextIO) -> StreamWrapper:
|
|
383
375
|
ret = cls()
|
|
384
376
|
ret.orig_stream = orig_stream
|
|
385
377
|
return ret
|
|
@@ -392,14 +384,14 @@ class StreamWrapper(StringIO):
|
|
|
392
384
|
|
|
393
385
|
|
|
394
386
|
# Simulates an enum
|
|
395
|
-
def enum(*sequential: Any, **named: Any) ->
|
|
387
|
+
def enum(*sequential: Any, **named: Any) -> type[Any]:
|
|
396
388
|
enums = dict(zip(sequential, range(len(sequential))), **named)
|
|
397
389
|
reverse = {value: key for key, value in enums.items()}
|
|
398
390
|
enums["reverse_mapping"] = reverse
|
|
399
391
|
return type("Enum", (), enums)
|
|
400
392
|
|
|
401
393
|
|
|
402
|
-
def build_netloc(host: str, port:
|
|
394
|
+
def build_netloc(host: str, port: int | None) -> str:
|
|
403
395
|
"""
|
|
404
396
|
Build a netloc from a host-port pair
|
|
405
397
|
"""
|
|
@@ -421,7 +413,7 @@ def build_url_from_netloc(netloc: str, scheme: str = "https") -> str:
|
|
|
421
413
|
return f"{scheme}://{netloc}"
|
|
422
414
|
|
|
423
415
|
|
|
424
|
-
def parse_netloc(netloc: str) ->
|
|
416
|
+
def parse_netloc(netloc: str) -> tuple[str | None, int | None]:
|
|
425
417
|
"""
|
|
426
418
|
Return the host-port pair from a netloc.
|
|
427
419
|
"""
|
|
@@ -443,7 +435,7 @@ def split_auth_from_netloc(netloc: str) -> NetlocTuple:
|
|
|
443
435
|
# behaves if more than one @ is present (which can be checked using
|
|
444
436
|
# the password attribute of urlsplit()'s return value).
|
|
445
437
|
auth, netloc = netloc.rsplit("@", 1)
|
|
446
|
-
pw:
|
|
438
|
+
pw: str | None = None
|
|
447
439
|
if ":" in auth:
|
|
448
440
|
# Split from the left because that's how urllib.parse.urlsplit()
|
|
449
441
|
# behaves if more than one : is present (which again can be checked
|
|
@@ -480,8 +472,8 @@ def redact_netloc(netloc: str) -> str:
|
|
|
480
472
|
|
|
481
473
|
|
|
482
474
|
def _transform_url(
|
|
483
|
-
url: str, transform_netloc: Callable[[str],
|
|
484
|
-
) ->
|
|
475
|
+
url: str, transform_netloc: Callable[[str], tuple[Any, ...]]
|
|
476
|
+
) -> tuple[str, NetlocTuple]:
|
|
485
477
|
"""Transform and replace netloc in a url.
|
|
486
478
|
|
|
487
479
|
transform_netloc is a function taking the netloc and returning a
|
|
@@ -503,13 +495,13 @@ def _get_netloc(netloc: str) -> NetlocTuple:
|
|
|
503
495
|
return split_auth_from_netloc(netloc)
|
|
504
496
|
|
|
505
497
|
|
|
506
|
-
def _redact_netloc(netloc: str) ->
|
|
498
|
+
def _redact_netloc(netloc: str) -> tuple[str]:
|
|
507
499
|
return (redact_netloc(netloc),)
|
|
508
500
|
|
|
509
501
|
|
|
510
502
|
def split_auth_netloc_from_url(
|
|
511
503
|
url: str,
|
|
512
|
-
) ->
|
|
504
|
+
) -> tuple[str, str, tuple[str | None, str | None]]:
|
|
513
505
|
"""
|
|
514
506
|
Parse a url into separate netloc, auth, and url with no auth.
|
|
515
507
|
|
|
@@ -614,7 +606,7 @@ def is_console_interactive() -> bool:
|
|
|
614
606
|
return sys.stdin is not None and sys.stdin.isatty()
|
|
615
607
|
|
|
616
608
|
|
|
617
|
-
def hash_file(path: str, blocksize: int = 1 << 20) ->
|
|
609
|
+
def hash_file(path: str, blocksize: int = 1 << 20) -> tuple[Any, int]:
|
|
618
610
|
"""Return (hash, length) for path using hashlib.sha256()"""
|
|
619
611
|
|
|
620
612
|
h = hashlib.sha256()
|
|
@@ -626,7 +618,7 @@ def hash_file(path: str, blocksize: int = 1 << 20) -> Tuple[Any, int]:
|
|
|
626
618
|
return h, length
|
|
627
619
|
|
|
628
620
|
|
|
629
|
-
def pairwise(iterable: Iterable[Any]) -> Iterator[
|
|
621
|
+
def pairwise(iterable: Iterable[Any]) -> Iterator[tuple[Any, Any]]:
|
|
630
622
|
"""
|
|
631
623
|
Return paired elements.
|
|
632
624
|
|
|
@@ -639,7 +631,7 @@ def pairwise(iterable: Iterable[Any]) -> Iterator[Tuple[Any, Any]]:
|
|
|
639
631
|
|
|
640
632
|
def partition(
|
|
641
633
|
pred: Callable[[T], bool], iterable: Iterable[T]
|
|
642
|
-
) ->
|
|
634
|
+
) -> tuple[Iterable[T], Iterable[T]]:
|
|
643
635
|
"""
|
|
644
636
|
Use a predicate to partition entries into false entries and true entries,
|
|
645
637
|
like
|
|
@@ -656,9 +648,9 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
|
|
656
648
|
config_holder: Any,
|
|
657
649
|
source_dir: str,
|
|
658
650
|
build_backend: str,
|
|
659
|
-
backend_path:
|
|
660
|
-
runner:
|
|
661
|
-
python_executable:
|
|
651
|
+
backend_path: str | None = None,
|
|
652
|
+
runner: Callable[..., None] | None = None,
|
|
653
|
+
python_executable: str | None = None,
|
|
662
654
|
):
|
|
663
655
|
super().__init__(
|
|
664
656
|
source_dir, build_backend, backend_path, runner, python_executable
|
|
@@ -668,8 +660,8 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
|
|
668
660
|
def build_wheel(
|
|
669
661
|
self,
|
|
670
662
|
wheel_directory: str,
|
|
671
|
-
config_settings:
|
|
672
|
-
metadata_directory:
|
|
663
|
+
config_settings: Mapping[str, Any] | None = None,
|
|
664
|
+
metadata_directory: str | None = None,
|
|
673
665
|
) -> str:
|
|
674
666
|
cs = self.config_holder.config_settings
|
|
675
667
|
return super().build_wheel(
|
|
@@ -679,7 +671,7 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
|
|
679
671
|
def build_sdist(
|
|
680
672
|
self,
|
|
681
673
|
sdist_directory: str,
|
|
682
|
-
config_settings:
|
|
674
|
+
config_settings: Mapping[str, Any] | None = None,
|
|
683
675
|
) -> str:
|
|
684
676
|
cs = self.config_holder.config_settings
|
|
685
677
|
return super().build_sdist(sdist_directory, config_settings=cs)
|
|
@@ -687,8 +679,8 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
|
|
687
679
|
def build_editable(
|
|
688
680
|
self,
|
|
689
681
|
wheel_directory: str,
|
|
690
|
-
config_settings:
|
|
691
|
-
metadata_directory:
|
|
682
|
+
config_settings: Mapping[str, Any] | None = None,
|
|
683
|
+
metadata_directory: str | None = None,
|
|
692
684
|
) -> str:
|
|
693
685
|
cs = self.config_holder.config_settings
|
|
694
686
|
return super().build_editable(
|
|
@@ -696,19 +688,19 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
|
|
696
688
|
)
|
|
697
689
|
|
|
698
690
|
def get_requires_for_build_wheel(
|
|
699
|
-
self, config_settings:
|
|
691
|
+
self, config_settings: Mapping[str, Any] | None = None
|
|
700
692
|
) -> Sequence[str]:
|
|
701
693
|
cs = self.config_holder.config_settings
|
|
702
694
|
return super().get_requires_for_build_wheel(config_settings=cs)
|
|
703
695
|
|
|
704
696
|
def get_requires_for_build_sdist(
|
|
705
|
-
self, config_settings:
|
|
697
|
+
self, config_settings: Mapping[str, Any] | None = None
|
|
706
698
|
) -> Sequence[str]:
|
|
707
699
|
cs = self.config_holder.config_settings
|
|
708
700
|
return super().get_requires_for_build_sdist(config_settings=cs)
|
|
709
701
|
|
|
710
702
|
def get_requires_for_build_editable(
|
|
711
|
-
self, config_settings:
|
|
703
|
+
self, config_settings: Mapping[str, Any] | None = None
|
|
712
704
|
) -> Sequence[str]:
|
|
713
705
|
cs = self.config_holder.config_settings
|
|
714
706
|
return super().get_requires_for_build_editable(config_settings=cs)
|
|
@@ -716,7 +708,7 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
|
|
716
708
|
def prepare_metadata_for_build_wheel(
|
|
717
709
|
self,
|
|
718
710
|
metadata_directory: str,
|
|
719
|
-
config_settings:
|
|
711
|
+
config_settings: Mapping[str, Any] | None = None,
|
|
720
712
|
_allow_fallback: bool = True,
|
|
721
713
|
) -> str:
|
|
722
714
|
cs = self.config_holder.config_settings
|
|
@@ -729,9 +721,9 @@ class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller):
|
|
|
729
721
|
def prepare_metadata_for_build_editable(
|
|
730
722
|
self,
|
|
731
723
|
metadata_directory: str,
|
|
732
|
-
config_settings:
|
|
724
|
+
config_settings: Mapping[str, Any] | None = None,
|
|
733
725
|
_allow_fallback: bool = True,
|
|
734
|
-
) ->
|
|
726
|
+
) -> str | None:
|
|
735
727
|
cs = self.config_holder.config_settings
|
|
736
728
|
return super().prepare_metadata_for_build_editable(
|
|
737
729
|
metadata_directory=metadata_directory,
|
pip/_internal/utils/packaging.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import functools
|
|
2
4
|
import logging
|
|
3
|
-
from typing import Optional, Tuple
|
|
4
5
|
|
|
5
6
|
from pip._vendor.packaging import specifiers, version
|
|
6
7
|
from pip._vendor.packaging.requirements import Requirement
|
|
@@ -10,7 +11,7 @@ logger = logging.getLogger(__name__)
|
|
|
10
11
|
|
|
11
12
|
@functools.lru_cache(maxsize=32)
|
|
12
13
|
def check_requires_python(
|
|
13
|
-
requires_python:
|
|
14
|
+
requires_python: str | None, version_info: tuple[int, ...]
|
|
14
15
|
) -> bool:
|
|
15
16
|
"""
|
|
16
17
|
Check if the given Python version matches a "Requires-Python" specifier.
|
pip/_internal/utils/retry.py
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import functools
|
|
2
4
|
from time import perf_counter, sleep
|
|
3
|
-
from typing import Callable, TypeVar
|
|
5
|
+
from typing import TYPE_CHECKING, Callable, TypeVar
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from typing_extensions import ParamSpec
|
|
6
9
|
|
|
7
|
-
T = TypeVar("T")
|
|
8
|
-
P = ParamSpec("P")
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
P = ParamSpec("P")
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
def retry(
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import logging
|
|
2
4
|
import os
|
|
3
5
|
import shlex
|
|
4
6
|
import subprocess
|
|
5
|
-
from
|
|
7
|
+
from collections.abc import Iterable, Mapping
|
|
8
|
+
from typing import Any, Callable, Literal, Union
|
|
6
9
|
|
|
7
10
|
from pip._vendor.rich.markup import escape
|
|
8
11
|
|
|
@@ -11,10 +14,10 @@ from pip._internal.exceptions import InstallationSubprocessError
|
|
|
11
14
|
from pip._internal.utils.logging import VERBOSE, subprocess_logger
|
|
12
15
|
from pip._internal.utils.misc import HiddenText
|
|
13
16
|
|
|
14
|
-
CommandArgs =
|
|
17
|
+
CommandArgs = list[Union[str, HiddenText]]
|
|
15
18
|
|
|
16
19
|
|
|
17
|
-
def make_command(*args:
|
|
20
|
+
def make_command(*args: str | HiddenText | CommandArgs) -> CommandArgs:
|
|
18
21
|
"""
|
|
19
22
|
Create a CommandArgs object.
|
|
20
23
|
"""
|
|
@@ -31,7 +34,7 @@ def make_command(*args: Union[str, HiddenText, CommandArgs]) -> CommandArgs:
|
|
|
31
34
|
return command_args
|
|
32
35
|
|
|
33
36
|
|
|
34
|
-
def format_command_args(args:
|
|
37
|
+
def format_command_args(args: list[str] | CommandArgs) -> str:
|
|
35
38
|
"""
|
|
36
39
|
Format command arguments for display.
|
|
37
40
|
"""
|
|
@@ -46,7 +49,7 @@ def format_command_args(args: Union[List[str], CommandArgs]) -> str:
|
|
|
46
49
|
)
|
|
47
50
|
|
|
48
51
|
|
|
49
|
-
def reveal_command_args(args:
|
|
52
|
+
def reveal_command_args(args: list[str] | CommandArgs) -> list[str]:
|
|
50
53
|
"""
|
|
51
54
|
Return the arguments in their raw, unredacted form.
|
|
52
55
|
"""
|
|
@@ -54,16 +57,16 @@ def reveal_command_args(args: Union[List[str], CommandArgs]) -> List[str]:
|
|
|
54
57
|
|
|
55
58
|
|
|
56
59
|
def call_subprocess(
|
|
57
|
-
cmd:
|
|
60
|
+
cmd: list[str] | CommandArgs,
|
|
58
61
|
show_stdout: bool = False,
|
|
59
|
-
cwd:
|
|
60
|
-
on_returncode:
|
|
61
|
-
extra_ok_returncodes:
|
|
62
|
-
extra_environ:
|
|
63
|
-
unset_environ:
|
|
64
|
-
spinner:
|
|
65
|
-
log_failed_cmd:
|
|
66
|
-
stdout_only:
|
|
62
|
+
cwd: str | None = None,
|
|
63
|
+
on_returncode: Literal["raise", "warn", "ignore"] = "raise",
|
|
64
|
+
extra_ok_returncodes: Iterable[int] | None = None,
|
|
65
|
+
extra_environ: Mapping[str, Any] | None = None,
|
|
66
|
+
unset_environ: Iterable[str] | None = None,
|
|
67
|
+
spinner: SpinnerInterface | None = None,
|
|
68
|
+
log_failed_cmd: bool | None = True,
|
|
69
|
+
stdout_only: bool | None = False,
|
|
67
70
|
*,
|
|
68
71
|
command_desc: str,
|
|
69
72
|
) -> str:
|
|
@@ -229,9 +232,9 @@ def runner_with_spinner_message(message: str) -> Callable[..., None]:
|
|
|
229
232
|
"""
|
|
230
233
|
|
|
231
234
|
def runner(
|
|
232
|
-
cmd:
|
|
233
|
-
cwd:
|
|
234
|
-
extra_environ:
|
|
235
|
+
cmd: list[str],
|
|
236
|
+
cwd: str | None = None,
|
|
237
|
+
extra_environ: Mapping[str, Any] | None = None,
|
|
235
238
|
) -> None:
|
|
236
239
|
with open_spinner(message) as spinner:
|
|
237
240
|
call_subprocess(
|
pip/_internal/utils/temp_dir.py
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import errno
|
|
2
4
|
import itertools
|
|
3
5
|
import logging
|
|
4
6
|
import os.path
|
|
5
7
|
import tempfile
|
|
6
8
|
import traceback
|
|
9
|
+
from collections.abc import Generator
|
|
7
10
|
from contextlib import ExitStack, contextmanager
|
|
8
11
|
from pathlib import Path
|
|
9
12
|
from typing import (
|
|
10
13
|
Any,
|
|
11
14
|
Callable,
|
|
12
|
-
Dict,
|
|
13
|
-
Generator,
|
|
14
|
-
List,
|
|
15
|
-
Optional,
|
|
16
15
|
TypeVar,
|
|
17
|
-
Union,
|
|
18
16
|
)
|
|
19
17
|
|
|
20
18
|
from pip._internal.utils.misc import enum, rmtree
|
|
@@ -33,7 +31,7 @@ tempdir_kinds = enum(
|
|
|
33
31
|
)
|
|
34
32
|
|
|
35
33
|
|
|
36
|
-
_tempdir_manager:
|
|
34
|
+
_tempdir_manager: ExitStack | None = None
|
|
37
35
|
|
|
38
36
|
|
|
39
37
|
@contextmanager
|
|
@@ -51,7 +49,7 @@ class TempDirectoryTypeRegistry:
|
|
|
51
49
|
"""Manages temp directory behavior"""
|
|
52
50
|
|
|
53
51
|
def __init__(self) -> None:
|
|
54
|
-
self._should_delete:
|
|
52
|
+
self._should_delete: dict[str, bool] = {}
|
|
55
53
|
|
|
56
54
|
def set_delete(self, kind: str, value: bool) -> None:
|
|
57
55
|
"""Indicate whether a TempDirectory of the given kind should be
|
|
@@ -66,7 +64,7 @@ class TempDirectoryTypeRegistry:
|
|
|
66
64
|
return self._should_delete.get(kind, True)
|
|
67
65
|
|
|
68
66
|
|
|
69
|
-
_tempdir_registry:
|
|
67
|
+
_tempdir_registry: TempDirectoryTypeRegistry | None = None
|
|
70
68
|
|
|
71
69
|
|
|
72
70
|
@contextmanager
|
|
@@ -113,8 +111,8 @@ class TempDirectory:
|
|
|
113
111
|
|
|
114
112
|
def __init__(
|
|
115
113
|
self,
|
|
116
|
-
path:
|
|
117
|
-
delete:
|
|
114
|
+
path: str | None = None,
|
|
115
|
+
delete: bool | None | _Default = _default,
|
|
118
116
|
kind: str = "temp",
|
|
119
117
|
globally_managed: bool = False,
|
|
120
118
|
ignore_cleanup_errors: bool = True,
|
|
@@ -184,7 +182,7 @@ class TempDirectory:
|
|
|
184
182
|
if not os.path.exists(self._path):
|
|
185
183
|
return
|
|
186
184
|
|
|
187
|
-
errors:
|
|
185
|
+
errors: list[BaseException] = []
|
|
188
186
|
|
|
189
187
|
def onerror(
|
|
190
188
|
func: Callable[..., Any],
|
|
@@ -245,7 +243,7 @@ class AdjacentTempDirectory(TempDirectory):
|
|
|
245
243
|
# with leading '-' and invalid metadata
|
|
246
244
|
LEADING_CHARS = "-~.=%0123456789"
|
|
247
245
|
|
|
248
|
-
def __init__(self, original: str, delete:
|
|
246
|
+
def __init__(self, original: str, delete: bool | None = None) -> None:
|
|
249
247
|
self.original = original.rstrip("/\\")
|
|
250
248
|
super().__init__(delete=delete)
|
|
251
249
|
|
pip/_internal/utils/unpacking.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Utilities related archives."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import logging
|
|
4
6
|
import os
|
|
5
7
|
import shutil
|
|
@@ -7,7 +9,7 @@ import stat
|
|
|
7
9
|
import sys
|
|
8
10
|
import tarfile
|
|
9
11
|
import zipfile
|
|
10
|
-
from
|
|
12
|
+
from collections.abc import Iterable
|
|
11
13
|
from zipfile import ZipInfo
|
|
12
14
|
|
|
13
15
|
from pip._internal.exceptions import InstallationError
|
|
@@ -47,7 +49,7 @@ def current_umask() -> int:
|
|
|
47
49
|
return mask
|
|
48
50
|
|
|
49
51
|
|
|
50
|
-
def split_leading_dir(path: str) ->
|
|
52
|
+
def split_leading_dir(path: str) -> list[str]:
|
|
51
53
|
path = path.lstrip("/").lstrip("\\")
|
|
52
54
|
if "/" in path and (
|
|
53
55
|
("\\" in path and path.find("/") < path.find("\\")) or "\\" not in path
|
|
@@ -131,7 +133,7 @@ def unzip_file(filename: str, location: str, flatten: bool = True) -> None:
|
|
|
131
133
|
"outside target directory ({})"
|
|
132
134
|
)
|
|
133
135
|
raise InstallationError(message.format(filename, fn, location))
|
|
134
|
-
if fn.endswith("/"
|
|
136
|
+
if fn.endswith(("/", "\\")):
|
|
135
137
|
# A directory
|
|
136
138
|
ensure_dir(fn)
|
|
137
139
|
else:
|
|
@@ -246,6 +248,20 @@ def untar_file(filename: str, location: str) -> None:
|
|
|
246
248
|
tar.close()
|
|
247
249
|
|
|
248
250
|
|
|
251
|
+
def is_symlink_target_in_tar(tar: tarfile.TarFile, tarinfo: tarfile.TarInfo) -> bool:
|
|
252
|
+
"""Check if the file pointed to by the symbolic link is in the tar archive"""
|
|
253
|
+
linkname = os.path.join(os.path.dirname(tarinfo.name), tarinfo.linkname)
|
|
254
|
+
|
|
255
|
+
linkname = os.path.normpath(linkname)
|
|
256
|
+
linkname = linkname.replace("\\", "/")
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
tar.getmember(linkname)
|
|
260
|
+
return True
|
|
261
|
+
except KeyError:
|
|
262
|
+
return False
|
|
263
|
+
|
|
264
|
+
|
|
249
265
|
def _untar_without_filter(
|
|
250
266
|
filename: str,
|
|
251
267
|
location: str,
|
|
@@ -253,6 +269,9 @@ def _untar_without_filter(
|
|
|
253
269
|
leading: bool,
|
|
254
270
|
) -> None:
|
|
255
271
|
"""Fallback for Python without tarfile.data_filter"""
|
|
272
|
+
# NOTE: This function can be removed once pip requires CPython ≥ 3.12.
|
|
273
|
+
# PEP 706 added tarfile.data_filter, made tarfile extraction operations more secure.
|
|
274
|
+
# This feature is fully supported from CPython 3.12 onward.
|
|
256
275
|
for member in tar.getmembers():
|
|
257
276
|
fn = member.name
|
|
258
277
|
if leading:
|
|
@@ -267,6 +286,14 @@ def _untar_without_filter(
|
|
|
267
286
|
if member.isdir():
|
|
268
287
|
ensure_dir(path)
|
|
269
288
|
elif member.issym():
|
|
289
|
+
if not is_symlink_target_in_tar(tar, member):
|
|
290
|
+
message = (
|
|
291
|
+
"The tar file ({}) has a file ({}) trying to install "
|
|
292
|
+
"outside target directory ({})"
|
|
293
|
+
)
|
|
294
|
+
raise InstallationError(
|
|
295
|
+
message.format(filename, member.name, member.linkname)
|
|
296
|
+
)
|
|
270
297
|
try:
|
|
271
298
|
tar._extract_member(member, path)
|
|
272
299
|
except Exception as exc:
|
|
@@ -307,7 +334,7 @@ def _untar_without_filter(
|
|
|
307
334
|
def unpack_file(
|
|
308
335
|
filename: str,
|
|
309
336
|
location: str,
|
|
310
|
-
content_type:
|
|
337
|
+
content_type: str | None = None,
|
|
311
338
|
) -> None:
|
|
312
339
|
filename = os.path.realpath(filename)
|
|
313
340
|
if (
|
pip/_internal/utils/urls.py
CHANGED
|
@@ -12,7 +12,7 @@ def path_to_url(path: str) -> str:
|
|
|
12
12
|
quoted path parts.
|
|
13
13
|
"""
|
|
14
14
|
path = os.path.normpath(os.path.abspath(path))
|
|
15
|
-
url = urllib.parse.urljoin("file
|
|
15
|
+
url = urllib.parse.urljoin("file://", urllib.request.pathname2url(path))
|
|
16
16
|
return url
|
|
17
17
|
|
|
18
18
|
|