pip 25.1__py3-none-any.whl → 25.2__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 +118 -94
- pip/_internal/cache.py +16 -14
- pip/_internal/cli/autocompletion.py +13 -4
- pip/_internal/cli/base_command.py +18 -7
- pip/_internal/cli/cmdoptions.py +14 -9
- 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 +26 -22
- pip/_internal/cli/progress_bars.py +19 -12
- pip/_internal/cli/req_command.py +16 -12
- 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 +2 -3
- 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 +45 -40
- pip/_internal/commands/list.py +35 -26
- pip/_internal/commands/lock.py +1 -2
- 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 +2 -3
- pip/_internal/configuration.py +39 -25
- pip/_internal/distributions/base.py +6 -4
- pip/_internal/distributions/installed.py +8 -4
- pip/_internal/distributions/sdist.py +20 -13
- pip/_internal/distributions/wheel.py +6 -4
- pip/_internal/exceptions.py +58 -39
- pip/_internal/index/collector.py +24 -29
- pip/_internal/index/package_finder.py +70 -61
- 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 +8 -6
- 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 +12 -17
- 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 +7 -5
- pip/_internal/network/auth.py +20 -22
- pip/_internal/network/cache.py +22 -6
- pip/_internal/network/download.py +169 -141
- pip/_internal/network/lazy_wheel.py +10 -7
- 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 +3 -2
- pip/_internal/operations/build/wheel_editable.py +3 -2
- pip/_internal/operations/build/wheel_legacy.py +9 -8
- pip/_internal/operations/check.py +21 -26
- pip/_internal/operations/freeze.py +12 -9
- pip/_internal/operations/install/editable_legacy.py +5 -3
- pip/_internal/operations/install/wheel.py +53 -44
- pip/_internal/operations/prepare.py +35 -30
- pip/_internal/pyproject.py +7 -10
- pip/_internal/req/__init__.py +12 -10
- pip/_internal/req/constructors.py +33 -31
- pip/_internal/req/req_dependency_group.py +9 -8
- pip/_internal/req/req_file.py +32 -35
- pip/_internal/req/req_install.py +37 -34
- 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 +29 -26
- pip/_internal/resolution/resolvelib/factory.py +41 -50
- pip/_internal/resolution/resolvelib/found_candidates.py +11 -9
- pip/_internal/resolution/resolvelib/provider.py +15 -20
- pip/_internal/resolution/resolvelib/reporter.py +5 -3
- pip/_internal/resolution/resolvelib/requirements.py +8 -6
- pip/_internal/resolution/resolvelib/resolver.py +39 -23
- pip/_internal/self_outdated_check.py +8 -6
- 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 +8 -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 +54 -44
- pip/_internal/utils/packaging.py +3 -2
- pip/_internal/utils/retry.py +7 -4
- pip/_internal/utils/setuptools_build.py +12 -10
- pip/_internal/utils/subprocess.py +20 -17
- pip/_internal/utils/temp_dir.py +10 -12
- pip/_internal/utils/unpacking.py +6 -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 +14 -12
- pip/_vendor/cachecontrol/__init__.py +1 -1
- pip/_vendor/certifi/__init__.py +1 -1
- pip/_vendor/certifi/cacert.pem +102 -221
- pip/_vendor/certifi/core.py +1 -32
- pip/_vendor/dependency_groups/_implementation.py +7 -11
- pip/_vendor/distlib/__init__.py +2 -2
- pip/_vendor/distlib/scripts.py +1 -1
- pip/_vendor/msgpack/__init__.py +2 -2
- pip/_vendor/pkg_resources/__init__.py +1 -1
- pip/_vendor/platformdirs/version.py +2 -2
- pip/_vendor/pygments/__init__.py +1 -1
- pip/_vendor/requests/__version__.py +2 -2
- pip/_vendor/requests/compat.py +12 -0
- pip/_vendor/requests/models.py +3 -1
- pip/_vendor/requests/utils.py +6 -16
- 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/resolution.py +91 -10
- 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/syntax.py +24 -5
- pip/_vendor/rich/traceback.py +32 -17
- pip/_vendor/truststore/_api.py +1 -1
- pip/_vendor/vendor.txt +10 -11
- {pip-25.1.dist-info → pip-25.2.dist-info}/METADATA +26 -4
- {pip-25.1.dist-info → pip-25.2.dist-info}/RECORD +194 -181
- {pip-25.1.dist-info → pip-25.2.dist-info}/WHEEL +1 -1
- {pip-25.1.dist-info → pip-25.2.dist-info}/licenses/AUTHORS.txt +12 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER +3 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
- pip-25.2.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
- 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.dist-info → pip-25.2.dist-info}/entry_points.txt +0 -0
- {pip-25.1.dist-info → pip-25.2.dist-info}/licenses/LICENSE.txt +0 -0
- {pip-25.1.dist-info → pip-25.2.dist-info}/top_level.txt +0 -0
|
@@ -6,12 +6,14 @@ so commands which don't always hit the network (e.g. list w/o --outdated or
|
|
|
6
6
|
--uptodate) don't need waste time importing PipSession and friends.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
9
11
|
import logging
|
|
10
12
|
import os
|
|
11
13
|
import sys
|
|
12
14
|
from functools import lru_cache
|
|
13
15
|
from optparse import Values
|
|
14
|
-
from typing import TYPE_CHECKING
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
15
17
|
|
|
16
18
|
from pip._vendor import certifi
|
|
17
19
|
|
|
@@ -27,7 +29,7 @@ logger = logging.getLogger(__name__)
|
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
@lru_cache
|
|
30
|
-
def _create_truststore_ssl_context() ->
|
|
32
|
+
def _create_truststore_ssl_context() -> SSLContext | None:
|
|
31
33
|
if sys.version_info < (3, 10):
|
|
32
34
|
logger.debug("Disabling truststore because Python version isn't 3.10+")
|
|
33
35
|
return None
|
|
@@ -56,10 +58,10 @@ class SessionCommandMixin(CommandContextMixIn):
|
|
|
56
58
|
|
|
57
59
|
def __init__(self) -> None:
|
|
58
60
|
super().__init__()
|
|
59
|
-
self._session:
|
|
61
|
+
self._session: PipSession | None = None
|
|
60
62
|
|
|
61
63
|
@classmethod
|
|
62
|
-
def _get_index_urls(cls, options: Values) ->
|
|
64
|
+
def _get_index_urls(cls, options: Values) -> list[str] | None:
|
|
63
65
|
"""Return a list of index urls from user-provided options."""
|
|
64
66
|
index_urls = []
|
|
65
67
|
if not getattr(options, "no_index", False):
|
|
@@ -72,7 +74,7 @@ class SessionCommandMixin(CommandContextMixIn):
|
|
|
72
74
|
# Return None rather than an empty list
|
|
73
75
|
return index_urls or None
|
|
74
76
|
|
|
75
|
-
def get_default_session(self, options: Values) ->
|
|
77
|
+
def get_default_session(self, options: Values) -> PipSession:
|
|
76
78
|
"""Get a default-managed session."""
|
|
77
79
|
if self._session is None:
|
|
78
80
|
self._session = self.enter_context(self._build_session(options))
|
|
@@ -85,9 +87,9 @@ class SessionCommandMixin(CommandContextMixIn):
|
|
|
85
87
|
def _build_session(
|
|
86
88
|
self,
|
|
87
89
|
options: Values,
|
|
88
|
-
retries:
|
|
89
|
-
timeout:
|
|
90
|
-
) ->
|
|
90
|
+
retries: int | None = None,
|
|
91
|
+
timeout: int | None = None,
|
|
92
|
+
) -> PipSession:
|
|
91
93
|
from pip._internal.network.session import PipSession
|
|
92
94
|
|
|
93
95
|
cache_dir = options.cache_dir
|
|
@@ -134,7 +136,7 @@ class SessionCommandMixin(CommandContextMixIn):
|
|
|
134
136
|
return session
|
|
135
137
|
|
|
136
138
|
|
|
137
|
-
def _pip_self_version_check(session:
|
|
139
|
+
def _pip_self_version_check(session: PipSession, options: Values) -> None:
|
|
138
140
|
from pip._internal.self_outdated_check import pip_self_version_check as check
|
|
139
141
|
|
|
140
142
|
check(session, options)
|
pip/_internal/cli/main.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"""Primary application entrypoint."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import locale
|
|
4
6
|
import logging
|
|
5
7
|
import os
|
|
6
8
|
import sys
|
|
7
9
|
import warnings
|
|
8
|
-
from typing import List, Optional
|
|
9
10
|
|
|
10
11
|
from pip._internal.cli.autocompletion import autocomplete
|
|
11
12
|
from pip._internal.cli.main_parser import parse_command
|
|
@@ -43,7 +44,7 @@ logger = logging.getLogger(__name__)
|
|
|
43
44
|
# main, this should not be an issue in practice.
|
|
44
45
|
|
|
45
46
|
|
|
46
|
-
def main(args:
|
|
47
|
+
def main(args: list[str] | None = None) -> int:
|
|
47
48
|
if args is None:
|
|
48
49
|
args = sys.argv[1:]
|
|
49
50
|
|
pip/_internal/cli/main_parser.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""A single place for constructing and exposing the main parser"""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import os
|
|
4
6
|
import subprocess
|
|
5
7
|
import sys
|
|
6
|
-
from typing import List, Optional, Tuple
|
|
7
8
|
|
|
8
9
|
from pip._internal.build_env import get_runnable_pip
|
|
9
10
|
from pip._internal.cli import cmdoptions
|
|
@@ -46,7 +47,7 @@ def create_main_parser() -> ConfigOptionParser:
|
|
|
46
47
|
return parser
|
|
47
48
|
|
|
48
49
|
|
|
49
|
-
def identify_python_interpreter(python: str) ->
|
|
50
|
+
def identify_python_interpreter(python: str) -> str | None:
|
|
50
51
|
# If the named file exists, use it.
|
|
51
52
|
# If it's a directory, assume it's a virtual environment and
|
|
52
53
|
# look for the environment's Python executable.
|
|
@@ -65,7 +66,7 @@ def identify_python_interpreter(python: str) -> Optional[str]:
|
|
|
65
66
|
return None
|
|
66
67
|
|
|
67
68
|
|
|
68
|
-
def parse_command(args:
|
|
69
|
+
def parse_command(args: list[str]) -> tuple[str, list[str]]:
|
|
69
70
|
parser = create_main_parser()
|
|
70
71
|
|
|
71
72
|
# Note: parser calls disable_interspersed_args(), so the result of this
|
pip/_internal/cli/parser.py
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"""Base option parser setup"""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import logging
|
|
4
6
|
import optparse
|
|
5
7
|
import shutil
|
|
6
8
|
import sys
|
|
7
9
|
import textwrap
|
|
10
|
+
from collections.abc import Generator
|
|
8
11
|
from contextlib import suppress
|
|
9
|
-
from typing import Any,
|
|
12
|
+
from typing import Any, NoReturn
|
|
10
13
|
|
|
11
14
|
from pip._internal.cli.status_codes import UNKNOWN_ERROR
|
|
12
15
|
from pip._internal.configuration import Configuration, ConfigurationError
|
|
@@ -67,7 +70,7 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
|
|
|
67
70
|
msg = "\nUsage: {}\n".format(self.indent_lines(textwrap.dedent(usage), " "))
|
|
68
71
|
return msg
|
|
69
72
|
|
|
70
|
-
def format_description(self, description:
|
|
73
|
+
def format_description(self, description: str | None) -> str:
|
|
71
74
|
# leave full control over description to us
|
|
72
75
|
if description:
|
|
73
76
|
if hasattr(self.parser, "main"):
|
|
@@ -85,7 +88,7 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
|
|
|
85
88
|
else:
|
|
86
89
|
return ""
|
|
87
90
|
|
|
88
|
-
def format_epilog(self, epilog:
|
|
91
|
+
def format_epilog(self, epilog: str | None) -> str:
|
|
89
92
|
# leave full control over epilog to us
|
|
90
93
|
if epilog:
|
|
91
94
|
return epilog
|
|
@@ -142,7 +145,7 @@ class CustomOptionParser(optparse.OptionParser):
|
|
|
142
145
|
return group
|
|
143
146
|
|
|
144
147
|
@property
|
|
145
|
-
def option_list_all(self) ->
|
|
148
|
+
def option_list_all(self) -> list[optparse.Option]:
|
|
146
149
|
"""Get a list of all options, including those in option groups."""
|
|
147
150
|
res = self.option_list[:]
|
|
148
151
|
for i in self.option_groups:
|
|
@@ -177,33 +180,34 @@ class ConfigOptionParser(CustomOptionParser):
|
|
|
177
180
|
|
|
178
181
|
def _get_ordered_configuration_items(
|
|
179
182
|
self,
|
|
180
|
-
) -> Generator[
|
|
183
|
+
) -> Generator[tuple[str, Any], None, None]:
|
|
181
184
|
# Configuration gives keys in an unordered manner. Order them.
|
|
182
185
|
override_order = ["global", self.name, ":env:"]
|
|
183
186
|
|
|
184
187
|
# Pool the options into different groups
|
|
185
|
-
section_items:
|
|
188
|
+
section_items: dict[str, list[tuple[str, Any]]] = {
|
|
186
189
|
name: [] for name in override_order
|
|
187
190
|
}
|
|
188
|
-
for section_key, val in self.config.items():
|
|
189
|
-
# ignore empty values
|
|
190
|
-
if not val:
|
|
191
|
-
logger.debug(
|
|
192
|
-
"Ignoring configuration key '%s' as it's value is empty.",
|
|
193
|
-
section_key,
|
|
194
|
-
)
|
|
195
|
-
continue
|
|
196
191
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
192
|
+
for _, value in self.config.items(): # noqa: PERF102
|
|
193
|
+
for section_key, val in value.items():
|
|
194
|
+
# ignore empty values
|
|
195
|
+
if not val:
|
|
196
|
+
logger.debug(
|
|
197
|
+
"Ignoring configuration key '%s' as its value is empty.",
|
|
198
|
+
section_key,
|
|
199
|
+
)
|
|
200
|
+
continue
|
|
201
|
+
|
|
202
|
+
section, key = section_key.split(".", 1)
|
|
203
|
+
if section in override_order:
|
|
204
|
+
section_items[section].append((key, val))
|
|
200
205
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
yield key, val
|
|
206
|
+
# Yield each group in their override order
|
|
207
|
+
for section in override_order:
|
|
208
|
+
yield from section_items[section]
|
|
205
209
|
|
|
206
|
-
def _update_defaults(self, defaults:
|
|
210
|
+
def _update_defaults(self, defaults: dict[str, Any]) -> dict[str, Any]:
|
|
207
211
|
"""Updates the given defaults with values from the config files and
|
|
208
212
|
the environ. Does a little special handling for certain types of
|
|
209
213
|
options (lists)."""
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import functools
|
|
2
4
|
import sys
|
|
3
|
-
from
|
|
5
|
+
from collections.abc import Generator, Iterable, Iterator
|
|
6
|
+
from typing import Callable, Literal, TypeVar
|
|
4
7
|
|
|
5
8
|
from pip._vendor.rich.progress import (
|
|
6
9
|
BarColumn,
|
|
@@ -22,20 +25,21 @@ from pip._internal.utils.logging import get_console, get_indentation
|
|
|
22
25
|
|
|
23
26
|
T = TypeVar("T")
|
|
24
27
|
ProgressRenderer = Callable[[Iterable[T]], Iterator[T]]
|
|
28
|
+
BarType = Literal["on", "off", "raw"]
|
|
25
29
|
|
|
26
30
|
|
|
27
31
|
def _rich_download_progress_bar(
|
|
28
32
|
iterable: Iterable[bytes],
|
|
29
33
|
*,
|
|
30
|
-
bar_type:
|
|
31
|
-
size:
|
|
32
|
-
initial_progress:
|
|
34
|
+
bar_type: BarType,
|
|
35
|
+
size: int | None,
|
|
36
|
+
initial_progress: int | None = None,
|
|
33
37
|
) -> Generator[bytes, None, None]:
|
|
34
38
|
assert bar_type == "on", "This should only be used in the default mode."
|
|
35
39
|
|
|
36
40
|
if not size:
|
|
37
41
|
total = float("inf")
|
|
38
|
-
columns:
|
|
42
|
+
columns: tuple[ProgressColumn, ...] = (
|
|
39
43
|
TextColumn("[progress.description]{task.description}"),
|
|
40
44
|
SpinnerColumn("line", speed=1.5),
|
|
41
45
|
FileSizeColumn(),
|
|
@@ -49,18 +53,21 @@ def _rich_download_progress_bar(
|
|
|
49
53
|
BarColumn(),
|
|
50
54
|
DownloadColumn(),
|
|
51
55
|
TransferSpeedColumn(),
|
|
52
|
-
TextColumn("
|
|
53
|
-
TimeRemainingColumn(),
|
|
56
|
+
TextColumn("{task.fields[time_description]}"),
|
|
57
|
+
TimeRemainingColumn(elapsed_when_finished=True),
|
|
54
58
|
)
|
|
55
59
|
|
|
56
60
|
progress = Progress(*columns, refresh_per_second=5)
|
|
57
|
-
task_id = progress.add_task(
|
|
61
|
+
task_id = progress.add_task(
|
|
62
|
+
" " * (get_indentation() + 2), total=total, time_description="eta"
|
|
63
|
+
)
|
|
58
64
|
if initial_progress is not None:
|
|
59
65
|
progress.update(task_id, advance=initial_progress)
|
|
60
66
|
with progress:
|
|
61
67
|
for chunk in iterable:
|
|
62
68
|
yield chunk
|
|
63
69
|
progress.update(task_id, advance=len(chunk))
|
|
70
|
+
progress.update(task_id, time_description="")
|
|
64
71
|
|
|
65
72
|
|
|
66
73
|
def _rich_install_progress_bar(
|
|
@@ -88,8 +95,8 @@ def _rich_install_progress_bar(
|
|
|
88
95
|
def _raw_progress_bar(
|
|
89
96
|
iterable: Iterable[bytes],
|
|
90
97
|
*,
|
|
91
|
-
size:
|
|
92
|
-
initial_progress:
|
|
98
|
+
size: int | None,
|
|
99
|
+
initial_progress: int | None = None,
|
|
93
100
|
) -> Generator[bytes, None, None]:
|
|
94
101
|
def write_progress(current: int, total: int) -> None:
|
|
95
102
|
sys.stdout.write(f"Progress {current} of {total}\n")
|
|
@@ -109,7 +116,7 @@ def _raw_progress_bar(
|
|
|
109
116
|
|
|
110
117
|
|
|
111
118
|
def get_download_progress_renderer(
|
|
112
|
-
*, bar_type:
|
|
119
|
+
*, bar_type: BarType, size: int | None = None, initial_progress: int | None = None
|
|
113
120
|
) -> ProgressRenderer[bytes]:
|
|
114
121
|
"""Get an object that can be used to render the download progress.
|
|
115
122
|
|
|
@@ -133,7 +140,7 @@ def get_download_progress_renderer(
|
|
|
133
140
|
|
|
134
141
|
|
|
135
142
|
def get_install_progress_renderer(
|
|
136
|
-
*, bar_type:
|
|
143
|
+
*, bar_type: BarType, total: int
|
|
137
144
|
) -> ProgressRenderer[InstallRequirement]:
|
|
138
145
|
"""Get an object that can be used to render the install progress.
|
|
139
146
|
Returns a callable, that takes an iterable to "wrap".
|
pip/_internal/cli/req_command.py
CHANGED
|
@@ -5,11 +5,14 @@ need PackageFinder capability don't unnecessarily import the
|
|
|
5
5
|
PackageFinder machinery and all its vendored dependencies, etc.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
8
10
|
import logging
|
|
9
11
|
from functools import partial
|
|
10
12
|
from optparse import Values
|
|
11
|
-
from typing import Any
|
|
13
|
+
from typing import Any
|
|
12
14
|
|
|
15
|
+
from pip._internal.build_env import SubprocessBuildEnvironmentInstaller
|
|
13
16
|
from pip._internal.cache import WheelCache
|
|
14
17
|
from pip._internal.cli import cmdoptions
|
|
15
18
|
from pip._internal.cli.index_command import IndexGroupCommand
|
|
@@ -58,8 +61,8 @@ def with_cleanup(func: Any) -> Any:
|
|
|
58
61
|
registry.set_delete(t, False)
|
|
59
62
|
|
|
60
63
|
def wrapper(
|
|
61
|
-
self: RequirementCommand, options: Values, args:
|
|
62
|
-
) ->
|
|
64
|
+
self: RequirementCommand, options: Values, args: list[Any]
|
|
65
|
+
) -> int | None:
|
|
63
66
|
assert self.tempdir_registry is not None
|
|
64
67
|
if options.no_clean:
|
|
65
68
|
configure_tempdir_registry(self.tempdir_registry)
|
|
@@ -100,7 +103,7 @@ class RequirementCommand(IndexGroupCommand):
|
|
|
100
103
|
session: PipSession,
|
|
101
104
|
finder: PackageFinder,
|
|
102
105
|
use_user_site: bool,
|
|
103
|
-
download_dir:
|
|
106
|
+
download_dir: str | None = None,
|
|
104
107
|
verbosity: int = 0,
|
|
105
108
|
) -> RequirementPreparer:
|
|
106
109
|
"""
|
|
@@ -134,6 +137,7 @@ class RequirementCommand(IndexGroupCommand):
|
|
|
134
137
|
src_dir=options.src_dir,
|
|
135
138
|
download_dir=download_dir,
|
|
136
139
|
build_isolation=options.build_isolation,
|
|
140
|
+
build_isolation_installer=SubprocessBuildEnvironmentInstaller(finder),
|
|
137
141
|
check_build_deps=options.check_build_deps,
|
|
138
142
|
build_tracker=build_tracker,
|
|
139
143
|
session=session,
|
|
@@ -153,14 +157,14 @@ class RequirementCommand(IndexGroupCommand):
|
|
|
153
157
|
preparer: RequirementPreparer,
|
|
154
158
|
finder: PackageFinder,
|
|
155
159
|
options: Values,
|
|
156
|
-
wheel_cache:
|
|
160
|
+
wheel_cache: WheelCache | None = None,
|
|
157
161
|
use_user_site: bool = False,
|
|
158
162
|
ignore_installed: bool = True,
|
|
159
163
|
ignore_requires_python: bool = False,
|
|
160
164
|
force_reinstall: bool = False,
|
|
161
165
|
upgrade_strategy: str = "to-satisfy-only",
|
|
162
|
-
use_pep517:
|
|
163
|
-
py_version_info:
|
|
166
|
+
use_pep517: bool | None = None,
|
|
167
|
+
py_version_info: tuple[int, ...] | None = None,
|
|
164
168
|
) -> BaseResolver:
|
|
165
169
|
"""
|
|
166
170
|
Create a Resolver instance for the given parameters.
|
|
@@ -208,15 +212,15 @@ class RequirementCommand(IndexGroupCommand):
|
|
|
208
212
|
|
|
209
213
|
def get_requirements(
|
|
210
214
|
self,
|
|
211
|
-
args:
|
|
215
|
+
args: list[str],
|
|
212
216
|
options: Values,
|
|
213
217
|
finder: PackageFinder,
|
|
214
218
|
session: PipSession,
|
|
215
|
-
) ->
|
|
219
|
+
) -> list[InstallRequirement]:
|
|
216
220
|
"""
|
|
217
221
|
Parse command-line arguments into the corresponding requirements.
|
|
218
222
|
"""
|
|
219
|
-
requirements:
|
|
223
|
+
requirements: list[InstallRequirement] = []
|
|
220
224
|
for filename in options.constraints:
|
|
221
225
|
for parsed_req in parse_requirements(
|
|
222
226
|
filename,
|
|
@@ -322,8 +326,8 @@ class RequirementCommand(IndexGroupCommand):
|
|
|
322
326
|
self,
|
|
323
327
|
options: Values,
|
|
324
328
|
session: PipSession,
|
|
325
|
-
target_python:
|
|
326
|
-
ignore_requires_python:
|
|
329
|
+
target_python: TargetPython | None = None,
|
|
330
|
+
ignore_requires_python: bool | None = None,
|
|
327
331
|
) -> PackageFinder:
|
|
328
332
|
"""
|
|
329
333
|
Create a package finder appropriate to this requirement command.
|
pip/_internal/cli/spinners.py
CHANGED
|
@@ -1,15 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import contextlib
|
|
2
4
|
import itertools
|
|
3
5
|
import logging
|
|
4
6
|
import sys
|
|
5
7
|
import time
|
|
6
|
-
from
|
|
8
|
+
from collections.abc import Generator
|
|
9
|
+
from typing import IO, Final
|
|
10
|
+
|
|
11
|
+
from pip._vendor.rich.console import (
|
|
12
|
+
Console,
|
|
13
|
+
ConsoleOptions,
|
|
14
|
+
RenderableType,
|
|
15
|
+
RenderResult,
|
|
16
|
+
)
|
|
17
|
+
from pip._vendor.rich.live import Live
|
|
18
|
+
from pip._vendor.rich.measure import Measurement
|
|
19
|
+
from pip._vendor.rich.text import Text
|
|
7
20
|
|
|
8
21
|
from pip._internal.utils.compat import WINDOWS
|
|
9
|
-
from pip._internal.utils.logging import get_indentation
|
|
22
|
+
from pip._internal.utils.logging import get_console, get_indentation
|
|
10
23
|
|
|
11
24
|
logger = logging.getLogger(__name__)
|
|
12
25
|
|
|
26
|
+
SPINNER_CHARS: Final = r"-\|/"
|
|
27
|
+
SPINS_PER_SECOND: Final = 8
|
|
28
|
+
|
|
13
29
|
|
|
14
30
|
class SpinnerInterface:
|
|
15
31
|
def spin(self) -> None:
|
|
@@ -23,10 +39,10 @@ class InteractiveSpinner(SpinnerInterface):
|
|
|
23
39
|
def __init__(
|
|
24
40
|
self,
|
|
25
41
|
message: str,
|
|
26
|
-
file:
|
|
27
|
-
spin_chars: str =
|
|
42
|
+
file: IO[str] | None = None,
|
|
43
|
+
spin_chars: str = SPINNER_CHARS,
|
|
28
44
|
# Empirically, 8 updates/second looks nice
|
|
29
|
-
min_update_interval_seconds: float =
|
|
45
|
+
min_update_interval_seconds: float = 1 / SPINS_PER_SECOND,
|
|
30
46
|
):
|
|
31
47
|
self._message = message
|
|
32
48
|
if file is None:
|
|
@@ -136,6 +152,66 @@ def open_spinner(message: str) -> Generator[SpinnerInterface, None, None]:
|
|
|
136
152
|
spinner.finish("done")
|
|
137
153
|
|
|
138
154
|
|
|
155
|
+
class _PipRichSpinner:
|
|
156
|
+
"""
|
|
157
|
+
Custom rich spinner that matches the style of the legacy spinners.
|
|
158
|
+
|
|
159
|
+
(*) Updates will be handled in a background thread by a rich live panel
|
|
160
|
+
which will call render() automatically at the appropriate time.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
def __init__(self, label: str) -> None:
|
|
164
|
+
self.label = label
|
|
165
|
+
self._spin_cycle = itertools.cycle(SPINNER_CHARS)
|
|
166
|
+
self._spinner_text = ""
|
|
167
|
+
self._finished = False
|
|
168
|
+
self._indent = get_indentation() * " "
|
|
169
|
+
|
|
170
|
+
def __rich_console__(
|
|
171
|
+
self, console: Console, options: ConsoleOptions
|
|
172
|
+
) -> RenderResult:
|
|
173
|
+
yield self.render()
|
|
174
|
+
|
|
175
|
+
def __rich_measure__(
|
|
176
|
+
self, console: Console, options: ConsoleOptions
|
|
177
|
+
) -> Measurement:
|
|
178
|
+
text = self.render()
|
|
179
|
+
return Measurement.get(console, options, text)
|
|
180
|
+
|
|
181
|
+
def render(self) -> RenderableType:
|
|
182
|
+
if not self._finished:
|
|
183
|
+
self._spinner_text = next(self._spin_cycle)
|
|
184
|
+
|
|
185
|
+
return Text.assemble(self._indent, self.label, " ... ", self._spinner_text)
|
|
186
|
+
|
|
187
|
+
def finish(self, status: str) -> None:
|
|
188
|
+
"""Stop spinning and set a final status message."""
|
|
189
|
+
self._spinner_text = status
|
|
190
|
+
self._finished = True
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@contextlib.contextmanager
|
|
194
|
+
def open_rich_spinner(label: str, console: Console | None = None) -> Generator[None]:
|
|
195
|
+
if not logger.isEnabledFor(logging.INFO):
|
|
196
|
+
# Don't show spinner if --quiet is given.
|
|
197
|
+
yield
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
console = console or get_console()
|
|
201
|
+
spinner = _PipRichSpinner(label)
|
|
202
|
+
with Live(spinner, refresh_per_second=SPINS_PER_SECOND, console=console):
|
|
203
|
+
try:
|
|
204
|
+
yield
|
|
205
|
+
except KeyboardInterrupt:
|
|
206
|
+
spinner.finish("canceled")
|
|
207
|
+
raise
|
|
208
|
+
except Exception:
|
|
209
|
+
spinner.finish("error")
|
|
210
|
+
raise
|
|
211
|
+
else:
|
|
212
|
+
spinner.finish("done")
|
|
213
|
+
|
|
214
|
+
|
|
139
215
|
HIDE_CURSOR = "\x1b[?25l"
|
|
140
216
|
SHOW_CURSOR = "\x1b[?25h"
|
|
141
217
|
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
Package containing all pip commands
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
5
7
|
import importlib
|
|
6
8
|
from collections import namedtuple
|
|
7
|
-
from typing import Any
|
|
9
|
+
from typing import Any
|
|
8
10
|
|
|
9
11
|
from pip._internal.cli.base_command import Command
|
|
10
12
|
|
|
@@ -17,7 +19,7 @@ CommandInfo = namedtuple("CommandInfo", "module_path, class_name, summary")
|
|
|
17
19
|
# Even though the module path starts with the same "pip._internal.commands"
|
|
18
20
|
# prefix, the full path makes testing easier (specifically when modifying
|
|
19
21
|
# `commands_dict` in test setup / teardown).
|
|
20
|
-
commands_dict:
|
|
22
|
+
commands_dict: dict[str, CommandInfo] = {
|
|
21
23
|
"install": CommandInfo(
|
|
22
24
|
"pip._internal.commands.install",
|
|
23
25
|
"InstallCommand",
|
|
@@ -123,7 +125,7 @@ def create_command(name: str, **kwargs: Any) -> Command:
|
|
|
123
125
|
return command
|
|
124
126
|
|
|
125
127
|
|
|
126
|
-
def get_similar_commands(name: str) ->
|
|
128
|
+
def get_similar_commands(name: str) -> str | None:
|
|
127
129
|
"""Command name auto-correct."""
|
|
128
130
|
from difflib import get_close_matches
|
|
129
131
|
|
pip/_internal/commands/cache.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import textwrap
|
|
3
3
|
from optparse import Values
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Callable
|
|
5
5
|
|
|
6
6
|
from pip._internal.cli.base_command import Command
|
|
7
7
|
from pip._internal.cli.status_codes import ERROR, SUCCESS
|
|
@@ -49,8 +49,8 @@ class CacheCommand(Command):
|
|
|
49
49
|
|
|
50
50
|
self.parser.insert_option_group(0, self.cmd_opts)
|
|
51
51
|
|
|
52
|
-
def
|
|
53
|
-
|
|
52
|
+
def handler_map(self) -> dict[str, Callable[[Values, list[str]], None]]:
|
|
53
|
+
return {
|
|
54
54
|
"dir": self.get_cache_dir,
|
|
55
55
|
"info": self.get_cache_info,
|
|
56
56
|
"list": self.list_cache_items,
|
|
@@ -58,15 +58,18 @@ class CacheCommand(Command):
|
|
|
58
58
|
"purge": self.purge_cache,
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
def run(self, options: Values, args: list[str]) -> int:
|
|
62
|
+
handler_map = self.handler_map()
|
|
63
|
+
|
|
61
64
|
if not options.cache_dir:
|
|
62
65
|
logger.error("pip cache commands can not function since cache is disabled.")
|
|
63
66
|
return ERROR
|
|
64
67
|
|
|
65
68
|
# Determine action
|
|
66
|
-
if not args or args[0] not in
|
|
69
|
+
if not args or args[0] not in handler_map:
|
|
67
70
|
logger.error(
|
|
68
71
|
"Need an action (%s) to perform.",
|
|
69
|
-
", ".join(sorted(
|
|
72
|
+
", ".join(sorted(handler_map)),
|
|
70
73
|
)
|
|
71
74
|
return ERROR
|
|
72
75
|
|
|
@@ -74,20 +77,20 @@ class CacheCommand(Command):
|
|
|
74
77
|
|
|
75
78
|
# Error handling happens here, not in the action-handlers.
|
|
76
79
|
try:
|
|
77
|
-
|
|
80
|
+
handler_map[action](options, args[1:])
|
|
78
81
|
except PipError as e:
|
|
79
82
|
logger.error(e.args[0])
|
|
80
83
|
return ERROR
|
|
81
84
|
|
|
82
85
|
return SUCCESS
|
|
83
86
|
|
|
84
|
-
def get_cache_dir(self, options: Values, args:
|
|
87
|
+
def get_cache_dir(self, options: Values, args: list[str]) -> None:
|
|
85
88
|
if args:
|
|
86
89
|
raise CommandError("Too many arguments")
|
|
87
90
|
|
|
88
91
|
logger.info(options.cache_dir)
|
|
89
92
|
|
|
90
|
-
def get_cache_info(self, options: Values, args:
|
|
93
|
+
def get_cache_info(self, options: Values, args: list[str]) -> None:
|
|
91
94
|
if args:
|
|
92
95
|
raise CommandError("Too many arguments")
|
|
93
96
|
|
|
@@ -129,7 +132,7 @@ class CacheCommand(Command):
|
|
|
129
132
|
|
|
130
133
|
logger.info(message)
|
|
131
134
|
|
|
132
|
-
def list_cache_items(self, options: Values, args:
|
|
135
|
+
def list_cache_items(self, options: Values, args: list[str]) -> None:
|
|
133
136
|
if len(args) > 1:
|
|
134
137
|
raise CommandError("Too many arguments")
|
|
135
138
|
|
|
@@ -144,7 +147,7 @@ class CacheCommand(Command):
|
|
|
144
147
|
else:
|
|
145
148
|
self.format_for_abspath(files)
|
|
146
149
|
|
|
147
|
-
def format_for_human(self, files:
|
|
150
|
+
def format_for_human(self, files: list[str]) -> None:
|
|
148
151
|
if not files:
|
|
149
152
|
logger.info("No locally built wheels cached.")
|
|
150
153
|
return
|
|
@@ -157,11 +160,11 @@ class CacheCommand(Command):
|
|
|
157
160
|
logger.info("Cache contents:\n")
|
|
158
161
|
logger.info("\n".join(sorted(results)))
|
|
159
162
|
|
|
160
|
-
def format_for_abspath(self, files:
|
|
163
|
+
def format_for_abspath(self, files: list[str]) -> None:
|
|
161
164
|
if files:
|
|
162
165
|
logger.info("\n".join(sorted(files)))
|
|
163
166
|
|
|
164
|
-
def remove_cache_items(self, options: Values, args:
|
|
167
|
+
def remove_cache_items(self, options: Values, args: list[str]) -> None:
|
|
165
168
|
if len(args) > 1:
|
|
166
169
|
raise CommandError("Too many arguments")
|
|
167
170
|
|
|
@@ -188,7 +191,7 @@ class CacheCommand(Command):
|
|
|
188
191
|
logger.verbose("Removed %s", filename)
|
|
189
192
|
logger.info("Files removed: %s (%s)", len(files), format_size(bytes_removed))
|
|
190
193
|
|
|
191
|
-
def purge_cache(self, options: Values, args:
|
|
194
|
+
def purge_cache(self, options: Values, args: list[str]) -> None:
|
|
192
195
|
if args:
|
|
193
196
|
raise CommandError("Too many arguments")
|
|
194
197
|
|
|
@@ -197,14 +200,14 @@ class CacheCommand(Command):
|
|
|
197
200
|
def _cache_dir(self, options: Values, subdir: str) -> str:
|
|
198
201
|
return os.path.join(options.cache_dir, subdir)
|
|
199
202
|
|
|
200
|
-
def _find_http_files(self, options: Values) ->
|
|
203
|
+
def _find_http_files(self, options: Values) -> list[str]:
|
|
201
204
|
old_http_dir = self._cache_dir(options, "http")
|
|
202
205
|
new_http_dir = self._cache_dir(options, "http-v2")
|
|
203
206
|
return filesystem.find_files(old_http_dir, "*") + filesystem.find_files(
|
|
204
207
|
new_http_dir, "*"
|
|
205
208
|
)
|
|
206
209
|
|
|
207
|
-
def _find_wheels(self, options: Values, pattern: str) ->
|
|
210
|
+
def _find_wheels(self, options: Values, pattern: str) -> list[str]:
|
|
208
211
|
wheel_dir = self._cache_dir(options, "wheels")
|
|
209
212
|
|
|
210
213
|
# The wheel filename format, as specified in PEP 427, is:
|
pip/_internal/commands/check.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from optparse import Values
|
|
3
|
-
from typing import List
|
|
4
3
|
|
|
5
4
|
from pip._internal.cli.base_command import Command
|
|
6
5
|
from pip._internal.cli.status_codes import ERROR, SUCCESS
|
|
@@ -23,7 +22,7 @@ class CheckCommand(Command):
|
|
|
23
22
|
usage = """
|
|
24
23
|
%prog [options]"""
|
|
25
24
|
|
|
26
|
-
def run(self, options: Values, args:
|
|
25
|
+
def run(self, options: Values, args: list[str]) -> int:
|
|
27
26
|
package_set, parsing_probs = create_package_set_from_installed()
|
|
28
27
|
missing, conflicting = check_package_set(package_set)
|
|
29
28
|
unsupported = list(
|