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
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import contextlib
|
|
2
4
|
import functools
|
|
3
5
|
import logging
|
|
4
6
|
import os
|
|
5
|
-
from typing import TYPE_CHECKING,
|
|
7
|
+
from typing import TYPE_CHECKING, cast
|
|
6
8
|
|
|
7
9
|
from pip._vendor.packaging.utils import canonicalize_name
|
|
8
10
|
from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible, ResolutionTooDeep
|
|
@@ -43,7 +45,7 @@ class Resolver(BaseResolver):
|
|
|
43
45
|
self,
|
|
44
46
|
preparer: RequirementPreparer,
|
|
45
47
|
finder: PackageFinder,
|
|
46
|
-
wheel_cache:
|
|
48
|
+
wheel_cache: WheelCache | None,
|
|
47
49
|
make_install_req: InstallRequirementProvider,
|
|
48
50
|
use_user_site: bool,
|
|
49
51
|
ignore_dependencies: bool,
|
|
@@ -51,7 +53,7 @@ class Resolver(BaseResolver):
|
|
|
51
53
|
ignore_requires_python: bool,
|
|
52
54
|
force_reinstall: bool,
|
|
53
55
|
upgrade_strategy: str,
|
|
54
|
-
py_version_info:
|
|
56
|
+
py_version_info: tuple[int, ...] | None = None,
|
|
55
57
|
):
|
|
56
58
|
super().__init__()
|
|
57
59
|
assert upgrade_strategy in self._allowed_strategies
|
|
@@ -69,10 +71,10 @@ class Resolver(BaseResolver):
|
|
|
69
71
|
)
|
|
70
72
|
self.ignore_dependencies = ignore_dependencies
|
|
71
73
|
self.upgrade_strategy = upgrade_strategy
|
|
72
|
-
self._result:
|
|
74
|
+
self._result: Result | None = None
|
|
73
75
|
|
|
74
76
|
def resolve(
|
|
75
|
-
self, root_reqs:
|
|
77
|
+
self, root_reqs: list[InstallRequirement], check_supported_wheels: bool
|
|
76
78
|
) -> RequirementSet:
|
|
77
79
|
collected = self.factory.collect_root_requirements(root_reqs)
|
|
78
80
|
provider = PipProvider(
|
|
@@ -85,7 +87,8 @@ class Resolver(BaseResolver):
|
|
|
85
87
|
if "PIP_RESOLVER_DEBUG" in os.environ:
|
|
86
88
|
reporter: BaseReporter[Requirement, Candidate, str] = PipDebuggingReporter()
|
|
87
89
|
else:
|
|
88
|
-
reporter = PipReporter()
|
|
90
|
+
reporter = PipReporter(constraints=provider.constraints)
|
|
91
|
+
|
|
89
92
|
resolver: RLResolver[Requirement, Candidate, str] = RLResolver(
|
|
90
93
|
provider,
|
|
91
94
|
reporter,
|
|
@@ -178,16 +181,11 @@ class Resolver(BaseResolver):
|
|
|
178
181
|
|
|
179
182
|
req_set.add_named_requirement(ireq)
|
|
180
183
|
|
|
181
|
-
reqs = req_set.all_requirements
|
|
182
|
-
self.factory.preparer.prepare_linked_requirements_more(reqs)
|
|
183
|
-
for req in reqs:
|
|
184
|
-
req.prepared = True
|
|
185
|
-
req.needs_more_preparation = False
|
|
186
184
|
return req_set
|
|
187
185
|
|
|
188
186
|
def get_installation_order(
|
|
189
187
|
self, req_set: RequirementSet
|
|
190
|
-
) ->
|
|
188
|
+
) -> list[InstallRequirement]:
|
|
191
189
|
"""Get order for installation of requirements in RequirementSet.
|
|
192
190
|
|
|
193
191
|
The returned list contains a requirement before another that depends on
|
|
@@ -218,8 +216,8 @@ class Resolver(BaseResolver):
|
|
|
218
216
|
|
|
219
217
|
|
|
220
218
|
def get_topological_weights(
|
|
221
|
-
graph:
|
|
222
|
-
) ->
|
|
219
|
+
graph: DirectedGraph[str | None], requirement_keys: set[str]
|
|
220
|
+
) -> dict[str | None, int]:
|
|
223
221
|
"""Assign weights to each node based on how "deep" they are.
|
|
224
222
|
|
|
225
223
|
This implementation may change at any point in the future without prior
|
|
@@ -245,14 +243,25 @@ def get_topological_weights(
|
|
|
245
243
|
We are only interested in the weights of packages that are in the
|
|
246
244
|
requirement_keys.
|
|
247
245
|
"""
|
|
248
|
-
path:
|
|
249
|
-
weights:
|
|
246
|
+
path: set[str | None] = set()
|
|
247
|
+
weights: dict[str | None, list[int]] = {}
|
|
250
248
|
|
|
251
|
-
def visit(node:
|
|
249
|
+
def visit(node: str | None) -> None:
|
|
252
250
|
if node in path:
|
|
253
251
|
# We hit a cycle, so we'll break it here.
|
|
254
252
|
return
|
|
255
253
|
|
|
254
|
+
# The walk is exponential and for pathologically connected graphs (which
|
|
255
|
+
# are the ones most likely to contain cycles in the first place) it can
|
|
256
|
+
# take until the heat-death of the universe. To counter this we limit
|
|
257
|
+
# the number of attempts to visit (i.e. traverse through) any given
|
|
258
|
+
# node. We choose a value here which gives decent enough coverage for
|
|
259
|
+
# fairly well behaved graphs, and still limits the walk complexity to be
|
|
260
|
+
# linear in nature.
|
|
261
|
+
cur_weights = weights.get(node, [])
|
|
262
|
+
if len(cur_weights) >= 5:
|
|
263
|
+
return
|
|
264
|
+
|
|
256
265
|
# Time to visit the children!
|
|
257
266
|
path.add(node)
|
|
258
267
|
for child in graph.iter_children(node):
|
|
@@ -262,14 +271,14 @@ def get_topological_weights(
|
|
|
262
271
|
if node not in requirement_keys:
|
|
263
272
|
return
|
|
264
273
|
|
|
265
|
-
|
|
266
|
-
weights[node] =
|
|
274
|
+
cur_weights.append(len(path))
|
|
275
|
+
weights[node] = cur_weights
|
|
267
276
|
|
|
268
|
-
# Simplify the graph, pruning leaves that have no dependencies.
|
|
269
|
-
#
|
|
270
|
-
#
|
|
277
|
+
# Simplify the graph, pruning leaves that have no dependencies. This is
|
|
278
|
+
# needed for large graphs (say over 200 packages) because the `visit`
|
|
279
|
+
# function is slower for large/densely connected graphs, taking minutes.
|
|
271
280
|
# See https://github.com/pypa/pip/issues/10557
|
|
272
|
-
# We
|
|
281
|
+
# We repeat the pruning step until we have no more leaves to remove.
|
|
273
282
|
while True:
|
|
274
283
|
leaves = set()
|
|
275
284
|
for key in graph:
|
|
@@ -289,12 +298,13 @@ def get_topological_weights(
|
|
|
289
298
|
for leaf in leaves:
|
|
290
299
|
if leaf not in requirement_keys:
|
|
291
300
|
continue
|
|
292
|
-
weights[leaf] = weight
|
|
301
|
+
weights[leaf] = [weight]
|
|
293
302
|
# Remove the leaves from the graph, making it simpler.
|
|
294
303
|
for leaf in leaves:
|
|
295
304
|
graph.remove(leaf)
|
|
296
305
|
|
|
297
|
-
# Visit the remaining graph
|
|
306
|
+
# Visit the remaining graph, this will only have nodes to handle if the
|
|
307
|
+
# graph had a cycle in it, which the pruning step above could not handle.
|
|
298
308
|
# `None` is guaranteed to be the root node by resolvelib.
|
|
299
309
|
visit(None)
|
|
300
310
|
|
|
@@ -303,13 +313,15 @@ def get_topological_weights(
|
|
|
303
313
|
difference = set(weights.keys()).difference(requirement_keys)
|
|
304
314
|
assert not difference, difference
|
|
305
315
|
|
|
306
|
-
|
|
316
|
+
# Now give back all the weights, choosing the largest ones from what we
|
|
317
|
+
# accumulated.
|
|
318
|
+
return {node: max(wgts) for (node, wgts) in weights.items()}
|
|
307
319
|
|
|
308
320
|
|
|
309
321
|
def _req_set_item_sorter(
|
|
310
|
-
item:
|
|
311
|
-
weights:
|
|
312
|
-
) ->
|
|
322
|
+
item: tuple[str, InstallRequirement],
|
|
323
|
+
weights: dict[str | None, int],
|
|
324
|
+
) -> tuple[int, str]:
|
|
313
325
|
"""Key function used to sort install requirements for installation.
|
|
314
326
|
|
|
315
327
|
Based on the "weight" mapping calculated in ``get_installation_order()``.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import datetime
|
|
2
4
|
import functools
|
|
3
5
|
import hashlib
|
|
@@ -7,7 +9,7 @@ import optparse
|
|
|
7
9
|
import os.path
|
|
8
10
|
import sys
|
|
9
11
|
from dataclasses import dataclass
|
|
10
|
-
from typing import Any, Callable
|
|
12
|
+
from typing import Any, Callable
|
|
11
13
|
|
|
12
14
|
from pip._vendor.packaging.version import Version
|
|
13
15
|
from pip._vendor.packaging.version import parse as parse_version
|
|
@@ -25,7 +27,12 @@ from pip._internal.utils.entrypoints import (
|
|
|
25
27
|
get_best_invocation_for_this_pip,
|
|
26
28
|
get_best_invocation_for_this_python,
|
|
27
29
|
)
|
|
28
|
-
from pip._internal.utils.filesystem import
|
|
30
|
+
from pip._internal.utils.filesystem import (
|
|
31
|
+
adjacent_tmp_file,
|
|
32
|
+
check_path_owner,
|
|
33
|
+
copy_directory_permissions,
|
|
34
|
+
replace,
|
|
35
|
+
)
|
|
29
36
|
from pip._internal.utils.misc import (
|
|
30
37
|
ExternallyManagedEnvironment,
|
|
31
38
|
check_externally_managed,
|
|
@@ -54,7 +61,7 @@ def _convert_date(isodate: str) -> datetime.datetime:
|
|
|
54
61
|
|
|
55
62
|
class SelfCheckState:
|
|
56
63
|
def __init__(self, cache_dir: str) -> None:
|
|
57
|
-
self._state:
|
|
64
|
+
self._state: dict[str, Any] = {}
|
|
58
65
|
self._statefile_path = None
|
|
59
66
|
|
|
60
67
|
# Try to load the existing state
|
|
@@ -74,7 +81,7 @@ class SelfCheckState:
|
|
|
74
81
|
def key(self) -> str:
|
|
75
82
|
return sys.prefix
|
|
76
83
|
|
|
77
|
-
def get(self, current_time: datetime.datetime) ->
|
|
84
|
+
def get(self, current_time: datetime.datetime) -> str | None:
|
|
78
85
|
"""Check if we have a not-outdated version loaded already."""
|
|
79
86
|
if not self._state:
|
|
80
87
|
return None
|
|
@@ -98,13 +105,15 @@ class SelfCheckState:
|
|
|
98
105
|
if not self._statefile_path:
|
|
99
106
|
return
|
|
100
107
|
|
|
108
|
+
statefile_directory = os.path.dirname(self._statefile_path)
|
|
109
|
+
|
|
101
110
|
# Check to make sure that we own the directory
|
|
102
|
-
if not check_path_owner(
|
|
111
|
+
if not check_path_owner(statefile_directory):
|
|
103
112
|
return
|
|
104
113
|
|
|
105
114
|
# Now that we've ensured the directory is owned by this user, we'll go
|
|
106
115
|
# ahead and make sure that all our directories are created.
|
|
107
|
-
ensure_dir(
|
|
116
|
+
ensure_dir(statefile_directory)
|
|
108
117
|
|
|
109
118
|
state = {
|
|
110
119
|
# Include the key so it's easy to tell which pip wrote the
|
|
@@ -118,6 +127,7 @@ class SelfCheckState:
|
|
|
118
127
|
|
|
119
128
|
with adjacent_tmp_file(self._statefile_path) as f:
|
|
120
129
|
f.write(text.encode())
|
|
130
|
+
copy_directory_permissions(statefile_directory, f)
|
|
121
131
|
|
|
122
132
|
try:
|
|
123
133
|
# Since we have a prefix-specific state file, we can just
|
|
@@ -165,7 +175,7 @@ def was_installed_by_pip(pkg: str) -> bool:
|
|
|
165
175
|
|
|
166
176
|
def _get_current_remote_pip_version(
|
|
167
177
|
session: PipSession, options: optparse.Values
|
|
168
|
-
) ->
|
|
178
|
+
) -> str | None:
|
|
169
179
|
# Lets use PackageFinder to see what the latest pip version is
|
|
170
180
|
link_collector = LinkCollector.create(
|
|
171
181
|
session,
|
|
@@ -196,8 +206,8 @@ def _self_version_check_logic(
|
|
|
196
206
|
state: SelfCheckState,
|
|
197
207
|
current_time: datetime.datetime,
|
|
198
208
|
local_version: Version,
|
|
199
|
-
get_remote_version: Callable[[],
|
|
200
|
-
) ->
|
|
209
|
+
get_remote_version: Callable[[], str | None],
|
|
210
|
+
) -> UpgradePrompt | None:
|
|
201
211
|
remote_version_str = state.get(current_time)
|
|
202
212
|
if remote_version_str is None:
|
|
203
213
|
remote_version_str = get_remote_version()
|
pip/_internal/utils/appdirs.py
CHANGED
|
@@ -8,7 +8,6 @@ and eventually drop this after all usages are changed.
|
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
10
|
import sys
|
|
11
|
-
from typing import List
|
|
12
11
|
|
|
13
12
|
from pip._vendor import platformdirs as _appdirs
|
|
14
13
|
|
|
@@ -40,7 +39,7 @@ def user_config_dir(appname: str, roaming: bool = True) -> str:
|
|
|
40
39
|
|
|
41
40
|
# for the discussion regarding site_config_dir locations
|
|
42
41
|
# see <https://github.com/pypa/pip/issues/1733>
|
|
43
|
-
def site_config_dirs(appname: str) ->
|
|
42
|
+
def site_config_dirs(appname: str) -> list[str]:
|
|
44
43
|
if sys.platform == "darwin":
|
|
45
44
|
dirval = _appdirs.site_data_dir(appname, appauthor=False, multipath=True)
|
|
46
45
|
return dirval.split(os.pathsep)
|
pip/_internal/utils/compat.py
CHANGED
|
@@ -7,7 +7,7 @@ import os
|
|
|
7
7
|
import sys
|
|
8
8
|
from typing import IO
|
|
9
9
|
|
|
10
|
-
__all__ = ["get_path_uid", "stdlib_pkgs", "WINDOWS"]
|
|
10
|
+
__all__ = ["get_path_uid", "stdlib_pkgs", "tomllib", "WINDOWS"]
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
logger = logging.getLogger(__name__)
|
|
@@ -67,6 +67,12 @@ else:
|
|
|
67
67
|
)
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
if sys.version_info >= (3, 11):
|
|
71
|
+
import tomllib
|
|
72
|
+
else:
|
|
73
|
+
from pip._vendor import tomli as tomllib
|
|
74
|
+
|
|
75
|
+
|
|
70
76
|
# packages in the stdlib that may have installation metadata, but should not be
|
|
71
77
|
# considered 'installed'. this theoretically could be determined based on
|
|
72
78
|
# dist.location (py27:`sysconfig.get_paths()['stdlib']`,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Generate and work with PEP 425 Compatibility Tags."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import re
|
|
4
|
-
from typing import List, Optional, Tuple
|
|
5
6
|
|
|
6
7
|
from pip._vendor.packaging.tags import (
|
|
7
8
|
PythonVersion,
|
|
@@ -19,12 +20,12 @@ from pip._vendor.packaging.tags import (
|
|
|
19
20
|
_apple_arch_pat = re.compile(r"(.+)_(\d+)_(\d+)_(.+)")
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def version_info_to_nodot(version_info:
|
|
23
|
+
def version_info_to_nodot(version_info: tuple[int, ...]) -> str:
|
|
23
24
|
# Only use up to the first two numbers.
|
|
24
25
|
return "".join(map(str, version_info[:2]))
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
def _mac_platforms(arch: str) ->
|
|
28
|
+
def _mac_platforms(arch: str) -> list[str]:
|
|
28
29
|
match = _apple_arch_pat.match(arch)
|
|
29
30
|
if match:
|
|
30
31
|
name, major, minor, actual_arch = match.groups()
|
|
@@ -44,7 +45,7 @@ def _mac_platforms(arch: str) -> List[str]:
|
|
|
44
45
|
return arches
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
def _ios_platforms(arch: str) ->
|
|
48
|
+
def _ios_platforms(arch: str) -> list[str]:
|
|
48
49
|
match = _apple_arch_pat.match(arch)
|
|
49
50
|
if match:
|
|
50
51
|
name, major, minor, actual_multiarch = match.groups()
|
|
@@ -64,7 +65,7 @@ def _ios_platforms(arch: str) -> List[str]:
|
|
|
64
65
|
return arches
|
|
65
66
|
|
|
66
67
|
|
|
67
|
-
def _android_platforms(arch: str) ->
|
|
68
|
+
def _android_platforms(arch: str) -> list[str]:
|
|
68
69
|
match = re.fullmatch(r"android_(\d+)_(.+)", arch)
|
|
69
70
|
if match:
|
|
70
71
|
api_level, abi = match.groups()
|
|
@@ -74,7 +75,7 @@ def _android_platforms(arch: str) -> List[str]:
|
|
|
74
75
|
return [arch]
|
|
75
76
|
|
|
76
77
|
|
|
77
|
-
def _custom_manylinux_platforms(arch: str) ->
|
|
78
|
+
def _custom_manylinux_platforms(arch: str) -> list[str]:
|
|
78
79
|
arches = [arch]
|
|
79
80
|
arch_prefix, arch_sep, arch_suffix = arch.partition("_")
|
|
80
81
|
if arch_prefix == "manylinux2014":
|
|
@@ -95,7 +96,7 @@ def _custom_manylinux_platforms(arch: str) -> List[str]:
|
|
|
95
96
|
return arches
|
|
96
97
|
|
|
97
98
|
|
|
98
|
-
def _get_custom_platforms(arch: str) ->
|
|
99
|
+
def _get_custom_platforms(arch: str) -> list[str]:
|
|
99
100
|
arch_prefix, arch_sep, arch_suffix = arch.partition("_")
|
|
100
101
|
if arch.startswith("macosx"):
|
|
101
102
|
arches = _mac_platforms(arch)
|
|
@@ -110,7 +111,7 @@ def _get_custom_platforms(arch: str) -> List[str]:
|
|
|
110
111
|
return arches
|
|
111
112
|
|
|
112
113
|
|
|
113
|
-
def _expand_allowed_platforms(platforms:
|
|
114
|
+
def _expand_allowed_platforms(platforms: list[str] | None) -> list[str] | None:
|
|
114
115
|
if not platforms:
|
|
115
116
|
return None
|
|
116
117
|
|
|
@@ -135,7 +136,7 @@ def _get_python_version(version: str) -> PythonVersion:
|
|
|
135
136
|
|
|
136
137
|
|
|
137
138
|
def _get_custom_interpreter(
|
|
138
|
-
implementation:
|
|
139
|
+
implementation: str | None = None, version: str | None = None
|
|
139
140
|
) -> str:
|
|
140
141
|
if implementation is None:
|
|
141
142
|
implementation = interpreter_name()
|
|
@@ -145,11 +146,11 @@ def _get_custom_interpreter(
|
|
|
145
146
|
|
|
146
147
|
|
|
147
148
|
def get_supported(
|
|
148
|
-
version:
|
|
149
|
-
platforms:
|
|
150
|
-
impl:
|
|
151
|
-
abis:
|
|
152
|
-
) ->
|
|
149
|
+
version: str | None = None,
|
|
150
|
+
platforms: list[str] | None = None,
|
|
151
|
+
impl: str | None = None,
|
|
152
|
+
abis: list[str] | None = None,
|
|
153
|
+
) -> list[Tag]:
|
|
153
154
|
"""Return a list of supported tags for each version specified in
|
|
154
155
|
`versions`.
|
|
155
156
|
|
|
@@ -162,9 +163,9 @@ def get_supported(
|
|
|
162
163
|
:param abis: specify a list of abis you want valid
|
|
163
164
|
tags for, or None. If None, use the local interpreter abi.
|
|
164
165
|
"""
|
|
165
|
-
supported:
|
|
166
|
+
supported: list[Tag] = []
|
|
166
167
|
|
|
167
|
-
python_version:
|
|
168
|
+
python_version: PythonVersion | None = None
|
|
168
169
|
if version is not None:
|
|
169
170
|
python_version = _get_python_version(version)
|
|
170
171
|
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
A module that implements tooling to enable easy warnings about deprecations.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
5
7
|
import logging
|
|
6
8
|
import warnings
|
|
7
|
-
from typing import Any,
|
|
9
|
+
from typing import Any, TextIO
|
|
8
10
|
|
|
9
11
|
from pip._vendor.packaging.version import parse
|
|
10
12
|
|
|
@@ -22,12 +24,12 @@ _original_showwarning: Any = None
|
|
|
22
24
|
|
|
23
25
|
# Warnings <-> Logging Integration
|
|
24
26
|
def _showwarning(
|
|
25
|
-
message:
|
|
26
|
-
category:
|
|
27
|
+
message: Warning | str,
|
|
28
|
+
category: type[Warning],
|
|
27
29
|
filename: str,
|
|
28
30
|
lineno: int,
|
|
29
|
-
file:
|
|
30
|
-
line:
|
|
31
|
+
file: TextIO | None = None,
|
|
32
|
+
line: str | None = None,
|
|
31
33
|
) -> None:
|
|
32
34
|
if file is not None:
|
|
33
35
|
if _original_showwarning is not None:
|
|
@@ -55,10 +57,10 @@ def install_warning_logger() -> None:
|
|
|
55
57
|
def deprecated(
|
|
56
58
|
*,
|
|
57
59
|
reason: str,
|
|
58
|
-
replacement:
|
|
59
|
-
gone_in:
|
|
60
|
-
feature_flag:
|
|
61
|
-
issue:
|
|
60
|
+
replacement: str | None,
|
|
61
|
+
gone_in: str | None,
|
|
62
|
+
feature_flag: str | None = None,
|
|
63
|
+
issue: int | None = None,
|
|
62
64
|
) -> None:
|
|
63
65
|
"""Helper to deprecate existing functionality.
|
|
64
66
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo
|
|
4
4
|
from pip._internal.models.link import Link
|
|
@@ -37,7 +37,7 @@ def direct_url_for_editable(source_dir: str) -> DirectUrl:
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def direct_url_from_link(
|
|
40
|
-
link: Link, source_dir:
|
|
40
|
+
link: Link, source_dir: str | None = None, link_is_in_wheel_cache: bool = False
|
|
41
41
|
) -> DirectUrl:
|
|
42
42
|
if link.is_vcs:
|
|
43
43
|
vcs_backend = vcs.get_backend_for_scheme(link.scheme)
|
pip/_internal/utils/egg_link.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import os
|
|
2
4
|
import re
|
|
3
5
|
import sys
|
|
4
|
-
from typing import List, Optional
|
|
5
6
|
|
|
6
7
|
from pip._internal.locations import site_packages, user_site
|
|
7
8
|
from pip._internal.utils.virtualenv import (
|
|
@@ -15,7 +16,7 @@ __all__ = [
|
|
|
15
16
|
]
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
def _egg_link_names(raw_name: str) ->
|
|
19
|
+
def _egg_link_names(raw_name: str) -> list[str]:
|
|
19
20
|
"""
|
|
20
21
|
Convert a Name metadata value to a .egg-link name, by applying
|
|
21
22
|
the same substitution as pkg_resources's safe_name function.
|
|
@@ -30,7 +31,7 @@ def _egg_link_names(raw_name: str) -> List[str]:
|
|
|
30
31
|
]
|
|
31
32
|
|
|
32
33
|
|
|
33
|
-
def egg_link_path_from_sys_path(raw_name: str) ->
|
|
34
|
+
def egg_link_path_from_sys_path(raw_name: str) -> str | None:
|
|
34
35
|
"""
|
|
35
36
|
Look for a .egg-link file for project name, by walking sys.path.
|
|
36
37
|
"""
|
|
@@ -43,7 +44,7 @@ def egg_link_path_from_sys_path(raw_name: str) -> Optional[str]:
|
|
|
43
44
|
return None
|
|
44
45
|
|
|
45
46
|
|
|
46
|
-
def egg_link_path_from_location(raw_name: str) ->
|
|
47
|
+
def egg_link_path_from_location(raw_name: str) -> str | None:
|
|
47
48
|
"""
|
|
48
49
|
Return the path for the .egg-link file if it exists, otherwise, None.
|
|
49
50
|
|
|
@@ -61,7 +62,7 @@ def egg_link_path_from_location(raw_name: str) -> Optional[str]:
|
|
|
61
62
|
|
|
62
63
|
This method will just return the first one found.
|
|
63
64
|
"""
|
|
64
|
-
sites:
|
|
65
|
+
sites: list[str] = []
|
|
65
66
|
if running_under_virtualenv():
|
|
66
67
|
sites.append(site_packages)
|
|
67
68
|
if not virtualenv_no_global() and user_site:
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import itertools
|
|
2
4
|
import os
|
|
3
5
|
import shutil
|
|
4
6
|
import sys
|
|
5
|
-
from typing import List, Optional
|
|
6
7
|
|
|
7
8
|
from pip._internal.cli.main import main
|
|
8
9
|
from pip._internal.utils.compat import WINDOWS
|
|
@@ -20,7 +21,7 @@ if WINDOWS:
|
|
|
20
21
|
]
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
def _wrapper(args:
|
|
24
|
+
def _wrapper(args: list[str] | None = None) -> int:
|
|
24
25
|
"""Central wrapper for all old entrypoints.
|
|
25
26
|
|
|
26
27
|
Historically pip has had several entrypoints defined. Because of issues
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import fnmatch
|
|
2
4
|
import os
|
|
3
5
|
import os.path
|
|
4
6
|
import random
|
|
5
7
|
import sys
|
|
8
|
+
from collections.abc import Generator
|
|
6
9
|
from contextlib import contextmanager
|
|
7
10
|
from tempfile import NamedTemporaryFile
|
|
8
|
-
from typing import Any, BinaryIO,
|
|
11
|
+
from typing import Any, BinaryIO, cast
|
|
9
12
|
|
|
10
13
|
from pip._internal.utils.compat import get_path_uid
|
|
11
14
|
from pip._internal.utils.misc import format_size
|
|
@@ -115,17 +118,17 @@ def _test_writable_dir_win(path: str) -> bool:
|
|
|
115
118
|
raise OSError("Unexpected condition testing for writable directory")
|
|
116
119
|
|
|
117
120
|
|
|
118
|
-
def find_files(path: str, pattern: str) ->
|
|
121
|
+
def find_files(path: str, pattern: str) -> list[str]:
|
|
119
122
|
"""Returns a list of absolute paths of files beneath path, recursively,
|
|
120
123
|
with filenames which match the UNIX-style shell glob pattern."""
|
|
121
|
-
result:
|
|
124
|
+
result: list[str] = []
|
|
122
125
|
for root, _, files in os.walk(path):
|
|
123
126
|
matches = fnmatch.filter(files, pattern)
|
|
124
127
|
result.extend(os.path.join(root, f) for f in matches)
|
|
125
128
|
return result
|
|
126
129
|
|
|
127
130
|
|
|
128
|
-
def file_size(path: str) ->
|
|
131
|
+
def file_size(path: str) -> int | float:
|
|
129
132
|
# If it's a symlink, return 0.
|
|
130
133
|
if os.path.islink(path):
|
|
131
134
|
return 0
|
|
@@ -136,7 +139,7 @@ def format_file_size(path: str) -> str:
|
|
|
136
139
|
return format_size(file_size(path))
|
|
137
140
|
|
|
138
141
|
|
|
139
|
-
def directory_size(path: str) ->
|
|
142
|
+
def directory_size(path: str) -> int | float:
|
|
140
143
|
size = 0.0
|
|
141
144
|
for root, _dirs, files in os.walk(path):
|
|
142
145
|
for filename in files:
|
|
@@ -147,3 +150,15 @@ def directory_size(path: str) -> Union[int, float]:
|
|
|
147
150
|
|
|
148
151
|
def format_directory_size(path: str) -> str:
|
|
149
152
|
return format_size(directory_size(path))
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def copy_directory_permissions(directory: str, target_file: BinaryIO) -> None:
|
|
156
|
+
mode = (
|
|
157
|
+
os.stat(directory).st_mode & 0o666 # select read/write permissions of directory
|
|
158
|
+
| 0o600 # set owner read/write permissions
|
|
159
|
+
)
|
|
160
|
+
# Change permissions only if there is no risk of following a symlink.
|
|
161
|
+
if os.chmod in os.supports_fd:
|
|
162
|
+
os.chmod(target_file.fileno(), mode)
|
|
163
|
+
elif os.chmod in os.supports_follow_symlinks:
|
|
164
|
+
os.chmod(target_file.name, mode, follow_symlinks=False)
|
pip/_internal/utils/filetypes.py
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
"""Filetype information."""
|
|
2
2
|
|
|
3
|
-
from typing import Tuple
|
|
4
|
-
|
|
5
3
|
from pip._internal.utils.misc import splitext
|
|
6
4
|
|
|
7
5
|
WHEEL_EXTENSION = ".whl"
|
|
8
|
-
BZ2_EXTENSIONS:
|
|
9
|
-
XZ_EXTENSIONS:
|
|
6
|
+
BZ2_EXTENSIONS: tuple[str, ...] = (".tar.bz2", ".tbz")
|
|
7
|
+
XZ_EXTENSIONS: tuple[str, ...] = (
|
|
10
8
|
".tar.xz",
|
|
11
9
|
".txz",
|
|
12
10
|
".tlz",
|
|
13
11
|
".tar.lz",
|
|
14
12
|
".tar.lzma",
|
|
15
13
|
)
|
|
16
|
-
ZIP_EXTENSIONS:
|
|
17
|
-
TAR_EXTENSIONS:
|
|
14
|
+
ZIP_EXTENSIONS: tuple[str, ...] = (".zip", WHEEL_EXTENSION)
|
|
15
|
+
TAR_EXTENSIONS: tuple[str, ...] = (".tar.gz", ".tgz", ".tar")
|
|
18
16
|
ARCHIVE_EXTENSIONS = ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS
|
|
19
17
|
|
|
20
18
|
|
pip/_internal/utils/glibc.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import os
|
|
2
4
|
import sys
|
|
3
|
-
from typing import Optional, Tuple
|
|
4
5
|
|
|
5
6
|
|
|
6
|
-
def glibc_version_string() ->
|
|
7
|
+
def glibc_version_string() -> str | None:
|
|
7
8
|
"Returns glibc version string, or None if not using glibc."
|
|
8
9
|
return glibc_version_string_confstr() or glibc_version_string_ctypes()
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
def glibc_version_string_confstr() ->
|
|
12
|
+
def glibc_version_string_confstr() -> str | None:
|
|
12
13
|
"Primary implementation of glibc_version_string using os.confstr."
|
|
13
14
|
# os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
|
|
14
15
|
# to be broken or missing. This strategy is used in the standard library
|
|
@@ -28,7 +29,7 @@ def glibc_version_string_confstr() -> Optional[str]:
|
|
|
28
29
|
return version
|
|
29
30
|
|
|
30
31
|
|
|
31
|
-
def glibc_version_string_ctypes() ->
|
|
32
|
+
def glibc_version_string_ctypes() -> str | None:
|
|
32
33
|
"Fallback implementation of glibc_version_string using ctypes."
|
|
33
34
|
|
|
34
35
|
try:
|
|
@@ -88,7 +89,7 @@ def glibc_version_string_ctypes() -> Optional[str]:
|
|
|
88
89
|
# versions that was generated by pip 8.1.2 and earlier is useless and
|
|
89
90
|
# misleading. Solution: instead of using platform, use our code that actually
|
|
90
91
|
# works.
|
|
91
|
-
def libc_ver() ->
|
|
92
|
+
def libc_ver() -> tuple[str, str]:
|
|
92
93
|
"""Try to determine the glibc version
|
|
93
94
|
|
|
94
95
|
Returns a tuple of strings (lib, version) which default to empty strings
|