pip 25.3__py3-none-any.whl → 26.0__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 +1 -1
- pip/_internal/build_env.py +194 -5
- pip/_internal/cli/base_command.py +11 -0
- pip/_internal/cli/cmdoptions.py +157 -0
- pip/_internal/cli/index_command.py +20 -0
- pip/_internal/cli/main.py +11 -6
- pip/_internal/cli/main_parser.py +3 -1
- pip/_internal/cli/parser.py +93 -33
- pip/_internal/cli/progress_bars.py +4 -2
- pip/_internal/cli/req_command.py +99 -23
- pip/_internal/commands/cache.py +24 -0
- pip/_internal/commands/completion.py +2 -1
- pip/_internal/commands/download.py +8 -4
- pip/_internal/commands/index.py +13 -6
- pip/_internal/commands/install.py +36 -29
- pip/_internal/commands/list.py +14 -16
- pip/_internal/commands/lock.py +16 -8
- pip/_internal/commands/wheel.py +8 -13
- pip/_internal/exceptions.py +76 -3
- pip/_internal/index/collector.py +2 -3
- pip/_internal/index/package_finder.py +84 -18
- pip/_internal/locations/__init__.py +1 -2
- pip/_internal/locations/_sysconfig.py +4 -1
- pip/_internal/models/link.py +18 -14
- pip/_internal/models/release_control.py +92 -0
- pip/_internal/models/selection_prefs.py +6 -3
- pip/_internal/network/auth.py +6 -2
- pip/_internal/network/download.py +4 -5
- pip/_internal/network/session.py +14 -10
- pip/_internal/operations/install/wheel.py +1 -2
- pip/_internal/operations/prepare.py +2 -3
- pip/_internal/req/constructors.py +3 -1
- pip/_internal/req/pep723.py +41 -0
- pip/_internal/req/req_file.py +10 -1
- pip/_internal/resolution/resolvelib/factory.py +12 -1
- pip/_internal/resolution/resolvelib/requirements.py +7 -3
- pip/_internal/self_outdated_check.py +6 -13
- pip/_internal/utils/datetime.py +18 -0
- pip/_internal/utils/filesystem.py +40 -1
- pip/_internal/utils/logging.py +34 -2
- pip/_internal/utils/misc.py +18 -12
- pip/_internal/utils/pylock.py +116 -0
- pip/_internal/utils/unpacking.py +1 -1
- pip/_internal/vcs/versioncontrol.py +3 -1
- pip/_vendor/cachecontrol/__init__.py +6 -3
- pip/_vendor/cachecontrol/adapter.py +0 -1
- pip/_vendor/cachecontrol/controller.py +1 -1
- pip/_vendor/cachecontrol/filewrapper.py +3 -1
- pip/_vendor/certifi/__init__.py +1 -1
- pip/_vendor/certifi/cacert.pem +0 -332
- pip/_vendor/idna/LICENSE.md +1 -1
- pip/_vendor/idna/codec.py +1 -1
- pip/_vendor/idna/core.py +1 -1
- pip/_vendor/idna/idnadata.py +72 -6
- pip/_vendor/idna/package_data.py +1 -1
- pip/_vendor/idna/uts46data.py +891 -731
- pip/_vendor/packaging/__init__.py +1 -1
- pip/_vendor/packaging/_elffile.py +0 -1
- pip/_vendor/packaging/_manylinux.py +36 -36
- pip/_vendor/packaging/_musllinux.py +1 -1
- pip/_vendor/packaging/_parser.py +22 -10
- pip/_vendor/packaging/_structures.py +8 -0
- pip/_vendor/packaging/_tokenizer.py +23 -25
- pip/_vendor/packaging/licenses/__init__.py +13 -11
- pip/_vendor/packaging/licenses/_spdx.py +41 -1
- pip/_vendor/packaging/markers.py +64 -38
- pip/_vendor/packaging/metadata.py +143 -27
- pip/_vendor/packaging/pylock.py +635 -0
- pip/_vendor/packaging/requirements.py +5 -10
- pip/_vendor/packaging/specifiers.py +219 -170
- pip/_vendor/packaging/tags.py +15 -20
- pip/_vendor/packaging/utils.py +19 -24
- pip/_vendor/packaging/version.py +315 -105
- pip/_vendor/platformdirs/version.py +2 -2
- pip/_vendor/platformdirs/windows.py +7 -1
- pip/_vendor/vendor.txt +5 -5
- {pip-25.3.dist-info → pip-26.0.dist-info}/METADATA +2 -2
- {pip-25.3.dist-info → pip-26.0.dist-info}/RECORD +103 -100
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/AUTHORS.txt +18 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/idna/LICENSE.md +1 -1
- pip/_internal/models/pylock.py +0 -188
- {pip-25.3.dist-info → pip-26.0.dist-info}/WHEEL +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/entry_points.txt +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/LICENSE.txt +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/certifi/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/distlib/LICENSE.txt +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/distro/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/msgpack/COPYING +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.BSD +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pkg_resources/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/platformdirs/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pygments/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/requests/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/resolvelib/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/rich/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/tomli/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/tomli_w/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/truststore/LICENSE +0 -0
- {pip-25.3.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/urllib3/LICENSE.txt +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import datetime
|
|
5
6
|
import enum
|
|
6
7
|
import functools
|
|
7
8
|
import itertools
|
|
@@ -18,19 +19,22 @@ from typing import (
|
|
|
18
19
|
from pip._vendor.packaging import specifiers
|
|
19
20
|
from pip._vendor.packaging.tags import Tag
|
|
20
21
|
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
|
|
21
|
-
from pip._vendor.packaging.version import InvalidVersion, _BaseVersion
|
|
22
|
+
from pip._vendor.packaging.version import InvalidVersion, Version, _BaseVersion
|
|
22
23
|
from pip._vendor.packaging.version import parse as parse_version
|
|
23
24
|
|
|
24
25
|
from pip._internal.exceptions import (
|
|
25
26
|
BestVersionAlreadyInstalled,
|
|
26
27
|
DistributionNotFound,
|
|
28
|
+
InstallationError,
|
|
27
29
|
InvalidWheelFilename,
|
|
28
30
|
UnsupportedWheel,
|
|
29
31
|
)
|
|
30
32
|
from pip._internal.index.collector import LinkCollector, parse_links
|
|
33
|
+
from pip._internal.metadata import select_backend
|
|
31
34
|
from pip._internal.models.candidate import InstallationCandidate
|
|
32
35
|
from pip._internal.models.format_control import FormatControl
|
|
33
36
|
from pip._internal.models.link import Link
|
|
37
|
+
from pip._internal.models.release_control import ReleaseControl
|
|
34
38
|
from pip._internal.models.search_scope import SearchScope
|
|
35
39
|
from pip._internal.models.selection_prefs import SelectionPreferences
|
|
36
40
|
from pip._internal.models.target_python import TargetPython
|
|
@@ -111,6 +115,8 @@ class LinkType(enum.Enum):
|
|
|
111
115
|
format_invalid = enum.auto()
|
|
112
116
|
platform_mismatch = enum.auto()
|
|
113
117
|
requires_python_mismatch = enum.auto()
|
|
118
|
+
upload_too_late = enum.auto()
|
|
119
|
+
upload_time_missing = enum.auto()
|
|
114
120
|
|
|
115
121
|
|
|
116
122
|
class LinkEvaluator:
|
|
@@ -132,6 +138,7 @@ class LinkEvaluator:
|
|
|
132
138
|
target_python: TargetPython,
|
|
133
139
|
allow_yanked: bool,
|
|
134
140
|
ignore_requires_python: bool | None = None,
|
|
141
|
+
uploaded_prior_to: datetime.datetime | None = None,
|
|
135
142
|
) -> None:
|
|
136
143
|
"""
|
|
137
144
|
:param project_name: The user supplied package name.
|
|
@@ -149,6 +156,8 @@ class LinkEvaluator:
|
|
|
149
156
|
:param ignore_requires_python: Whether to ignore incompatible
|
|
150
157
|
PEP 503 "data-requires-python" values in HTML links. Defaults
|
|
151
158
|
to False.
|
|
159
|
+
:param uploaded_prior_to: If set, only allow links uploaded prior to
|
|
160
|
+
the given datetime.
|
|
152
161
|
"""
|
|
153
162
|
if ignore_requires_python is None:
|
|
154
163
|
ignore_requires_python = False
|
|
@@ -158,6 +167,7 @@ class LinkEvaluator:
|
|
|
158
167
|
self._ignore_requires_python = ignore_requires_python
|
|
159
168
|
self._formats = formats
|
|
160
169
|
self._target_python = target_python
|
|
170
|
+
self._uploaded_prior_to = uploaded_prior_to
|
|
161
171
|
|
|
162
172
|
self.project_name = project_name
|
|
163
173
|
|
|
@@ -218,6 +228,27 @@ class LinkEvaluator:
|
|
|
218
228
|
|
|
219
229
|
version = wheel.version
|
|
220
230
|
|
|
231
|
+
# Check upload-time filter after verifying the link is a package file.
|
|
232
|
+
# Skip this check for local files, as --uploaded-prior-to only applies
|
|
233
|
+
# to packages from indexes.
|
|
234
|
+
if self._uploaded_prior_to is not None and not link.is_file:
|
|
235
|
+
if link.upload_time is None:
|
|
236
|
+
if link.comes_from:
|
|
237
|
+
index_info = f"Index {link.comes_from}"
|
|
238
|
+
else:
|
|
239
|
+
index_info = "Index"
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
LinkType.upload_time_missing,
|
|
243
|
+
f"{index_info} does not provide upload-time metadata.",
|
|
244
|
+
)
|
|
245
|
+
elif link.upload_time >= self._uploaded_prior_to:
|
|
246
|
+
return (
|
|
247
|
+
LinkType.upload_too_late,
|
|
248
|
+
f"Upload time {link.upload_time} not "
|
|
249
|
+
f"prior to {self._uploaded_prior_to}",
|
|
250
|
+
)
|
|
251
|
+
|
|
221
252
|
# This should be up by the self.ok_binary check, but see issue 2700.
|
|
222
253
|
if "source" not in self._formats and ext != WHEEL_EXTENSION:
|
|
223
254
|
reason = f"No sources permitted for {self.project_name}"
|
|
@@ -350,7 +381,7 @@ class CandidatePreferences:
|
|
|
350
381
|
"""
|
|
351
382
|
|
|
352
383
|
prefer_binary: bool = False
|
|
353
|
-
|
|
384
|
+
release_control: ReleaseControl | None = None
|
|
354
385
|
|
|
355
386
|
|
|
356
387
|
@dataclass(frozen=True)
|
|
@@ -391,7 +422,7 @@ class CandidateEvaluator:
|
|
|
391
422
|
project_name: str,
|
|
392
423
|
target_python: TargetPython | None = None,
|
|
393
424
|
prefer_binary: bool = False,
|
|
394
|
-
|
|
425
|
+
release_control: ReleaseControl | None = None,
|
|
395
426
|
specifier: specifiers.BaseSpecifier | None = None,
|
|
396
427
|
hashes: Hashes | None = None,
|
|
397
428
|
) -> CandidateEvaluator:
|
|
@@ -417,7 +448,7 @@ class CandidateEvaluator:
|
|
|
417
448
|
supported_tags=supported_tags,
|
|
418
449
|
specifier=specifier,
|
|
419
450
|
prefer_binary=prefer_binary,
|
|
420
|
-
|
|
451
|
+
release_control=release_control,
|
|
421
452
|
hashes=hashes,
|
|
422
453
|
)
|
|
423
454
|
|
|
@@ -427,14 +458,14 @@ class CandidateEvaluator:
|
|
|
427
458
|
supported_tags: list[Tag],
|
|
428
459
|
specifier: specifiers.BaseSpecifier,
|
|
429
460
|
prefer_binary: bool = False,
|
|
430
|
-
|
|
461
|
+
release_control: ReleaseControl | None = None,
|
|
431
462
|
hashes: Hashes | None = None,
|
|
432
463
|
) -> None:
|
|
433
464
|
"""
|
|
434
465
|
:param supported_tags: The PEP 425 tags supported by the target
|
|
435
466
|
Python in order of preference (most preferred first).
|
|
436
467
|
"""
|
|
437
|
-
self.
|
|
468
|
+
self._release_control = release_control
|
|
438
469
|
self._hashes = hashes
|
|
439
470
|
self._prefer_binary = prefer_binary
|
|
440
471
|
self._project_name = project_name
|
|
@@ -455,17 +486,27 @@ class CandidateEvaluator:
|
|
|
455
486
|
Return the applicable candidates from a list of candidates.
|
|
456
487
|
"""
|
|
457
488
|
# Using None infers from the specifier instead.
|
|
458
|
-
|
|
489
|
+
if self._release_control is not None:
|
|
490
|
+
allow_prereleases = self._release_control.allows_prereleases(
|
|
491
|
+
canonicalize_name(self._project_name)
|
|
492
|
+
)
|
|
493
|
+
else:
|
|
494
|
+
allow_prereleases = None
|
|
459
495
|
specifier = self._specifier
|
|
460
496
|
|
|
461
|
-
#
|
|
462
|
-
# when we're debundled but setuptools isn't,
|
|
463
|
-
# packaging.version.Version and
|
|
497
|
+
# When using the pkg_resources backend we turn the version object into
|
|
498
|
+
# a str here because otherwise when we're debundled but setuptools isn't,
|
|
499
|
+
# Python will see packaging.version.Version and
|
|
464
500
|
# pkg_resources._vendor.packaging.version.Version as different
|
|
465
501
|
# types. This way we'll use a str as a common data interchange
|
|
466
502
|
# format. If we stop using the pkg_resources provided specifier
|
|
467
503
|
# and start using our own, we can drop the cast to str().
|
|
468
|
-
|
|
504
|
+
if select_backend().NAME == "pkg_resources":
|
|
505
|
+
candidates_and_versions: list[
|
|
506
|
+
tuple[InstallationCandidate, str | Version]
|
|
507
|
+
] = [(c, str(c.version)) for c in candidates]
|
|
508
|
+
else:
|
|
509
|
+
candidates_and_versions = [(c, c.version) for c in candidates]
|
|
469
510
|
versions = set(
|
|
470
511
|
specifier.filter(
|
|
471
512
|
(v for _, v in candidates_and_versions),
|
|
@@ -593,6 +634,7 @@ class PackageFinder:
|
|
|
593
634
|
format_control: FormatControl | None = None,
|
|
594
635
|
candidate_prefs: CandidatePreferences | None = None,
|
|
595
636
|
ignore_requires_python: bool | None = None,
|
|
637
|
+
uploaded_prior_to: datetime.datetime | None = None,
|
|
596
638
|
) -> None:
|
|
597
639
|
"""
|
|
598
640
|
This constructor is primarily meant to be used by the create() class
|
|
@@ -614,6 +656,7 @@ class PackageFinder:
|
|
|
614
656
|
self._ignore_requires_python = ignore_requires_python
|
|
615
657
|
self._link_collector = link_collector
|
|
616
658
|
self._target_python = target_python
|
|
659
|
+
self._uploaded_prior_to = uploaded_prior_to
|
|
617
660
|
|
|
618
661
|
self.format_control = format_control
|
|
619
662
|
|
|
@@ -637,6 +680,7 @@ class PackageFinder:
|
|
|
637
680
|
link_collector: LinkCollector,
|
|
638
681
|
selection_prefs: SelectionPreferences,
|
|
639
682
|
target_python: TargetPython | None = None,
|
|
683
|
+
uploaded_prior_to: datetime.datetime | None = None,
|
|
640
684
|
) -> PackageFinder:
|
|
641
685
|
"""Create a PackageFinder.
|
|
642
686
|
|
|
@@ -645,13 +689,15 @@ class PackageFinder:
|
|
|
645
689
|
:param target_python: The target Python interpreter to use when
|
|
646
690
|
checking compatibility. If None (the default), a TargetPython
|
|
647
691
|
object will be constructed from the running Python.
|
|
692
|
+
:param uploaded_prior_to: If set, only find links uploaded prior
|
|
693
|
+
to the given datetime.
|
|
648
694
|
"""
|
|
649
695
|
if target_python is None:
|
|
650
696
|
target_python = TargetPython()
|
|
651
697
|
|
|
652
698
|
candidate_prefs = CandidatePreferences(
|
|
653
699
|
prefer_binary=selection_prefs.prefer_binary,
|
|
654
|
-
|
|
700
|
+
release_control=selection_prefs.release_control,
|
|
655
701
|
)
|
|
656
702
|
|
|
657
703
|
return cls(
|
|
@@ -661,6 +707,7 @@ class PackageFinder:
|
|
|
661
707
|
allow_yanked=selection_prefs.allow_yanked,
|
|
662
708
|
format_control=selection_prefs.format_control,
|
|
663
709
|
ignore_requires_python=selection_prefs.ignore_requires_python,
|
|
710
|
+
uploaded_prior_to=uploaded_prior_to,
|
|
664
711
|
)
|
|
665
712
|
|
|
666
713
|
@property
|
|
@@ -707,11 +754,11 @@ class PackageFinder:
|
|
|
707
754
|
return cert
|
|
708
755
|
|
|
709
756
|
@property
|
|
710
|
-
def
|
|
711
|
-
return self._candidate_prefs.
|
|
757
|
+
def release_control(self) -> ReleaseControl | None:
|
|
758
|
+
return self._candidate_prefs.release_control
|
|
712
759
|
|
|
713
|
-
def
|
|
714
|
-
self._candidate_prefs.
|
|
760
|
+
def set_release_control(self, release_control: ReleaseControl) -> None:
|
|
761
|
+
self._candidate_prefs.release_control = release_control
|
|
715
762
|
|
|
716
763
|
@property
|
|
717
764
|
def prefer_binary(self) -> bool:
|
|
@@ -720,6 +767,10 @@ class PackageFinder:
|
|
|
720
767
|
def set_prefer_binary(self) -> None:
|
|
721
768
|
self._candidate_prefs.prefer_binary = True
|
|
722
769
|
|
|
770
|
+
@property
|
|
771
|
+
def uploaded_prior_to(self) -> datetime.datetime | None:
|
|
772
|
+
return self._uploaded_prior_to
|
|
773
|
+
|
|
723
774
|
def requires_python_skipped_reasons(self) -> list[str]:
|
|
724
775
|
reasons = {
|
|
725
776
|
detail
|
|
@@ -739,6 +790,7 @@ class PackageFinder:
|
|
|
739
790
|
target_python=self._target_python,
|
|
740
791
|
allow_yanked=self._allow_yanked,
|
|
741
792
|
ignore_requires_python=self._ignore_requires_python,
|
|
793
|
+
uploaded_prior_to=self._uploaded_prior_to,
|
|
742
794
|
)
|
|
743
795
|
|
|
744
796
|
def _sort_links(self, links: Iterable[Link]) -> list[Link]:
|
|
@@ -773,6 +825,10 @@ class PackageFinder:
|
|
|
773
825
|
InstallationCandidate and return it. Otherwise, return None.
|
|
774
826
|
"""
|
|
775
827
|
result, detail = link_evaluator.evaluate_link(link)
|
|
828
|
+
if result == LinkType.upload_time_missing:
|
|
829
|
+
# Fail immediately if the index doesn't provide upload-time
|
|
830
|
+
# when --uploaded-prior-to is specified
|
|
831
|
+
raise InstallationError(detail)
|
|
776
832
|
if result != LinkType.candidate:
|
|
777
833
|
self._log_skipped_link(link, result, detail)
|
|
778
834
|
return None
|
|
@@ -890,7 +946,7 @@ class PackageFinder:
|
|
|
890
946
|
project_name=project_name,
|
|
891
947
|
target_python=self._target_python,
|
|
892
948
|
prefer_binary=candidate_prefs.prefer_binary,
|
|
893
|
-
|
|
949
|
+
release_control=candidate_prefs.release_control,
|
|
894
950
|
specifier=specifier,
|
|
895
951
|
hashes=hashes,
|
|
896
952
|
)
|
|
@@ -964,9 +1020,19 @@ class PackageFinder:
|
|
|
964
1020
|
)
|
|
965
1021
|
|
|
966
1022
|
if installed_version is None and best_candidate is None:
|
|
1023
|
+
# Check if only final releases are allowed for this package
|
|
1024
|
+
version_type = "version"
|
|
1025
|
+
if self.release_control is not None:
|
|
1026
|
+
allows_pre = self.release_control.allows_prereleases(
|
|
1027
|
+
canonicalize_name(name)
|
|
1028
|
+
)
|
|
1029
|
+
if allows_pre is False:
|
|
1030
|
+
version_type = "final version"
|
|
1031
|
+
|
|
967
1032
|
logger.critical(
|
|
968
|
-
"Could not find a
|
|
1033
|
+
"Could not find a %s that satisfies the requirement %s "
|
|
969
1034
|
"(from versions: %s)",
|
|
1035
|
+
version_type,
|
|
970
1036
|
req,
|
|
971
1037
|
_format_versions(best_candidate_result.all_candidates),
|
|
972
1038
|
)
|
|
@@ -6,7 +6,6 @@ import os
|
|
|
6
6
|
import pathlib
|
|
7
7
|
import sys
|
|
8
8
|
import sysconfig
|
|
9
|
-
from typing import Any
|
|
10
9
|
|
|
11
10
|
from pip._internal.models.scheme import SCHEME_KEYS, Scheme
|
|
12
11
|
from pip._internal.utils.compat import WINDOWS
|
|
@@ -134,7 +133,7 @@ def _looks_like_red_hat_scheme() -> bool:
|
|
|
134
133
|
from distutils.command.install import install
|
|
135
134
|
from distutils.dist import Distribution
|
|
136
135
|
|
|
137
|
-
cmd
|
|
136
|
+
cmd = install(Distribution())
|
|
138
137
|
cmd.finalize_options()
|
|
139
138
|
return (
|
|
140
139
|
cmd.exec_prefix == f"{os.path.normpath(sys.exec_prefix)}/local"
|
|
@@ -4,6 +4,7 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
6
6
|
import sysconfig
|
|
7
|
+
from typing import Callable
|
|
7
8
|
|
|
8
9
|
from pip._internal.exceptions import InvalidSchemeCombination, UserInstallationInvalid
|
|
9
10
|
from pip._internal.models.scheme import SCHEME_KEYS, Scheme
|
|
@@ -24,7 +25,9 @@ logger = logging.getLogger(__name__)
|
|
|
24
25
|
|
|
25
26
|
_AVAILABLE_SCHEMES = set(sysconfig.get_scheme_names())
|
|
26
27
|
|
|
27
|
-
_PREFERRED_SCHEME_API = getattr(
|
|
28
|
+
_PREFERRED_SCHEME_API: Callable[[str], str] | None = getattr(
|
|
29
|
+
sysconfig, "get_preferred_scheme", None
|
|
30
|
+
)
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
def _should_use_osx_framework_prefix() -> bool:
|
pip/_internal/models/link.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import datetime
|
|
3
4
|
import functools
|
|
4
5
|
import itertools
|
|
5
6
|
import logging
|
|
@@ -7,15 +8,16 @@ import os
|
|
|
7
8
|
import posixpath
|
|
8
9
|
import re
|
|
9
10
|
import urllib.parse
|
|
11
|
+
import urllib.request
|
|
10
12
|
from collections.abc import Mapping
|
|
11
13
|
from dataclasses import dataclass
|
|
12
14
|
from typing import (
|
|
13
|
-
TYPE_CHECKING,
|
|
14
15
|
Any,
|
|
15
16
|
NamedTuple,
|
|
16
17
|
)
|
|
17
18
|
|
|
18
|
-
from pip._internal.
|
|
19
|
+
from pip._internal.exceptions import InvalidEggFragment
|
|
20
|
+
from pip._internal.utils.datetime import parse_iso_datetime
|
|
19
21
|
from pip._internal.utils.filetypes import WHEEL_EXTENSION
|
|
20
22
|
from pip._internal.utils.hashes import Hashes
|
|
21
23
|
from pip._internal.utils.misc import (
|
|
@@ -26,9 +28,6 @@ from pip._internal.utils.misc import (
|
|
|
26
28
|
)
|
|
27
29
|
from pip._internal.utils.urls import path_to_url, url_to_path
|
|
28
30
|
|
|
29
|
-
if TYPE_CHECKING:
|
|
30
|
-
from pip._internal.index.collector import IndexContent
|
|
31
|
-
|
|
32
31
|
logger = logging.getLogger(__name__)
|
|
33
32
|
|
|
34
33
|
|
|
@@ -207,6 +206,7 @@ class Link:
|
|
|
207
206
|
"requires_python",
|
|
208
207
|
"yanked_reason",
|
|
209
208
|
"metadata_file_data",
|
|
209
|
+
"upload_time",
|
|
210
210
|
"cache_link_parsing",
|
|
211
211
|
"egg_fragment",
|
|
212
212
|
]
|
|
@@ -214,17 +214,17 @@ class Link:
|
|
|
214
214
|
def __init__(
|
|
215
215
|
self,
|
|
216
216
|
url: str,
|
|
217
|
-
comes_from: str |
|
|
217
|
+
comes_from: str | None = None,
|
|
218
218
|
requires_python: str | None = None,
|
|
219
219
|
yanked_reason: str | None = None,
|
|
220
220
|
metadata_file_data: MetadataFile | None = None,
|
|
221
|
+
upload_time: datetime.datetime | None = None,
|
|
221
222
|
cache_link_parsing: bool = True,
|
|
222
223
|
hashes: Mapping[str, str] | None = None,
|
|
223
224
|
) -> None:
|
|
224
225
|
"""
|
|
225
226
|
:param url: url of the resource pointed to (href of the link)
|
|
226
|
-
:param comes_from:
|
|
227
|
-
or string.
|
|
227
|
+
:param comes_from: URL or string indicating where the link was found.
|
|
228
228
|
:param requires_python: String containing the `Requires-Python`
|
|
229
229
|
metadata field, specified in PEP 345. This may be specified by
|
|
230
230
|
a data-requires-python attribute in the HTML link tag, as
|
|
@@ -239,6 +239,8 @@ class Link:
|
|
|
239
239
|
no such metadata is provided. This argument, if not None, indicates
|
|
240
240
|
that a separate metadata file exists, and also optionally supplies
|
|
241
241
|
hashes for that file.
|
|
242
|
+
:param upload_time: upload time of the file, or None if the information
|
|
243
|
+
is not available from the server.
|
|
242
244
|
:param cache_link_parsing: A flag that is used elsewhere to determine
|
|
243
245
|
whether resources retrieved from this link should be cached. PyPI
|
|
244
246
|
URLs should generally have this set to False, for example.
|
|
@@ -272,6 +274,7 @@ class Link:
|
|
|
272
274
|
self.requires_python = requires_python if requires_python else None
|
|
273
275
|
self.yanked_reason = yanked_reason
|
|
274
276
|
self.metadata_file_data = metadata_file_data
|
|
277
|
+
self.upload_time = upload_time
|
|
275
278
|
|
|
276
279
|
self.cache_link_parsing = cache_link_parsing
|
|
277
280
|
self.egg_fragment = self._egg_fragment()
|
|
@@ -300,6 +303,11 @@ class Link:
|
|
|
300
303
|
if metadata_info is None:
|
|
301
304
|
metadata_info = file_data.get("dist-info-metadata")
|
|
302
305
|
|
|
306
|
+
if upload_time_data := file_data.get("upload-time"):
|
|
307
|
+
upload_time = parse_iso_datetime(upload_time_data)
|
|
308
|
+
else:
|
|
309
|
+
upload_time = None
|
|
310
|
+
|
|
303
311
|
# The metadata info value may be a boolean, or a dict of hashes.
|
|
304
312
|
if isinstance(metadata_info, dict):
|
|
305
313
|
# The file exists, and hashes have been supplied
|
|
@@ -325,6 +333,7 @@ class Link:
|
|
|
325
333
|
yanked_reason=yanked_reason,
|
|
326
334
|
hashes=hashes,
|
|
327
335
|
metadata_file_data=metadata_file_data,
|
|
336
|
+
upload_time=upload_time,
|
|
328
337
|
)
|
|
329
338
|
|
|
330
339
|
@classmethod
|
|
@@ -474,12 +483,7 @@ class Link:
|
|
|
474
483
|
# an optional extras specifier. Anything else is invalid.
|
|
475
484
|
project_name = match.group(1)
|
|
476
485
|
if not self._project_name_re.match(project_name):
|
|
477
|
-
|
|
478
|
-
reason=f"{self} contains an egg fragment with a non-PEP 508 name.",
|
|
479
|
-
replacement="to use the req @ url syntax, and remove the egg fragment",
|
|
480
|
-
gone_in="26.0",
|
|
481
|
-
issue=13157,
|
|
482
|
-
)
|
|
486
|
+
raise InvalidEggFragment(self, project_name)
|
|
483
487
|
|
|
484
488
|
return project_name
|
|
485
489
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
|
|
6
|
+
|
|
7
|
+
from pip._internal.exceptions import CommandError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# TODO: add slots=True when Python 3.9 is dropped
|
|
11
|
+
@dataclass
|
|
12
|
+
class ReleaseControl:
|
|
13
|
+
"""Helper for managing which release types can be installed."""
|
|
14
|
+
|
|
15
|
+
all_releases: set[str] = field(default_factory=set)
|
|
16
|
+
only_final: set[str] = field(default_factory=set)
|
|
17
|
+
_order: list[tuple[str, str]] = field(
|
|
18
|
+
init=False, default_factory=list, compare=False, repr=False
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
def handle_mutual_excludes(
|
|
22
|
+
self, value: str, target: set[str], other: set[str], attr_name: str
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Parse and apply release control option value.
|
|
25
|
+
|
|
26
|
+
Processes comma-separated package names or special values `:all:` and `:none:`.
|
|
27
|
+
|
|
28
|
+
When adding packages to target, they're removed from other to maintain mutual
|
|
29
|
+
exclusivity between all_releases and only_final. All operations are tracked in
|
|
30
|
+
order so that the original command-line argument sequence can be reconstructed
|
|
31
|
+
when passing options to build subprocesses.
|
|
32
|
+
"""
|
|
33
|
+
if value.startswith("-"):
|
|
34
|
+
raise CommandError(
|
|
35
|
+
"--all-releases / --only-final option requires 1 argument."
|
|
36
|
+
)
|
|
37
|
+
new = value.split(",")
|
|
38
|
+
while ":all:" in new:
|
|
39
|
+
other.clear()
|
|
40
|
+
target.clear()
|
|
41
|
+
target.add(":all:")
|
|
42
|
+
# Track :all: in order
|
|
43
|
+
self._order.append((attr_name, ":all:"))
|
|
44
|
+
del new[: new.index(":all:") + 1]
|
|
45
|
+
# Without a none, we want to discard everything as :all: covers it
|
|
46
|
+
if ":none:" not in new:
|
|
47
|
+
return
|
|
48
|
+
for name in new:
|
|
49
|
+
if name == ":none:":
|
|
50
|
+
target.clear()
|
|
51
|
+
# Track :none: in order
|
|
52
|
+
self._order.append((attr_name, ":none:"))
|
|
53
|
+
continue
|
|
54
|
+
name = canonicalize_name(name)
|
|
55
|
+
other.discard(name)
|
|
56
|
+
target.add(name)
|
|
57
|
+
# Track package-specific setting in order
|
|
58
|
+
self._order.append((attr_name, name))
|
|
59
|
+
|
|
60
|
+
def get_ordered_args(self) -> list[tuple[str, str]]:
|
|
61
|
+
"""
|
|
62
|
+
Get ordered list of (flag_name, value) tuples for reconstructing CLI args.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
List of tuples where each tuple is (attribute_name, value).
|
|
66
|
+
The attribute_name is either 'all_releases' or 'only_final'.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
[("all_releases", ":all:"), ("only_final", "simple")]
|
|
70
|
+
would be reconstructed as:
|
|
71
|
+
["--all-releases", ":all:", "--only-final", "simple"]
|
|
72
|
+
"""
|
|
73
|
+
return self._order[:]
|
|
74
|
+
|
|
75
|
+
def allows_prereleases(self, canonical_name: NormalizedName) -> bool | None:
|
|
76
|
+
"""
|
|
77
|
+
Determine if pre-releases are allowed for a package.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
True: Pre-releases are allowed (package in all_releases)
|
|
81
|
+
False: Only final releases allowed (package in only_final)
|
|
82
|
+
None: No specific setting, use default behavior
|
|
83
|
+
"""
|
|
84
|
+
if canonical_name in self.all_releases:
|
|
85
|
+
return True
|
|
86
|
+
elif canonical_name in self.only_final:
|
|
87
|
+
return False
|
|
88
|
+
elif ":all:" in self.all_releases:
|
|
89
|
+
return True
|
|
90
|
+
elif ":all:" in self.only_final:
|
|
91
|
+
return False
|
|
92
|
+
return None
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from pip._internal.models.format_control import FormatControl
|
|
4
|
+
from pip._internal.models.release_control import ReleaseControl
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
# TODO: This needs Python 3.10's improved slots support for dataclasses
|
|
@@ -13,7 +14,7 @@ class SelectionPreferences:
|
|
|
13
14
|
|
|
14
15
|
__slots__ = [
|
|
15
16
|
"allow_yanked",
|
|
16
|
-
"
|
|
17
|
+
"release_control",
|
|
17
18
|
"format_control",
|
|
18
19
|
"prefer_binary",
|
|
19
20
|
"ignore_requires_python",
|
|
@@ -26,7 +27,7 @@ class SelectionPreferences:
|
|
|
26
27
|
def __init__(
|
|
27
28
|
self,
|
|
28
29
|
allow_yanked: bool,
|
|
29
|
-
|
|
30
|
+
release_control: ReleaseControl | None = None,
|
|
30
31
|
format_control: FormatControl | None = None,
|
|
31
32
|
prefer_binary: bool = False,
|
|
32
33
|
ignore_requires_python: bool | None = None,
|
|
@@ -35,6 +36,8 @@ class SelectionPreferences:
|
|
|
35
36
|
|
|
36
37
|
:param allow_yanked: Whether files marked as yanked (in the sense
|
|
37
38
|
of PEP 592) are permitted to be candidates for install.
|
|
39
|
+
:param release_control: A ReleaseControl object or None. Used to control
|
|
40
|
+
whether pre-releases are allowed for specific packages.
|
|
38
41
|
:param format_control: A FormatControl object or None. Used to control
|
|
39
42
|
the selection of source packages / binary packages when consulting
|
|
40
43
|
the index and links.
|
|
@@ -47,7 +50,7 @@ class SelectionPreferences:
|
|
|
47
50
|
ignore_requires_python = False
|
|
48
51
|
|
|
49
52
|
self.allow_yanked = allow_yanked
|
|
50
|
-
self.
|
|
53
|
+
self.release_control = release_control
|
|
51
54
|
self.format_control = format_control
|
|
52
55
|
self.prefer_binary = prefer_binary
|
|
53
56
|
self.ignore_requires_python = ignore_requires_python
|
pip/_internal/network/auth.py
CHANGED
|
@@ -20,7 +20,6 @@ from pathlib import Path
|
|
|
20
20
|
from typing import Any, NamedTuple
|
|
21
21
|
|
|
22
22
|
from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth
|
|
23
|
-
from pip._vendor.requests.models import Request, Response
|
|
24
23
|
from pip._vendor.requests.utils import get_netrc_auth
|
|
25
24
|
|
|
26
25
|
from pip._internal.utils.logging import getLogger
|
|
@@ -33,6 +32,10 @@ from pip._internal.utils.misc import (
|
|
|
33
32
|
)
|
|
34
33
|
from pip._internal.vcs.versioncontrol import AuthInfo
|
|
35
34
|
|
|
35
|
+
if typing.TYPE_CHECKING:
|
|
36
|
+
from pip._vendor.requests import PreparedRequest
|
|
37
|
+
from pip._vendor.requests.models import Response
|
|
38
|
+
|
|
36
39
|
logger = getLogger(__name__)
|
|
37
40
|
|
|
38
41
|
KEYRING_DISABLED = False
|
|
@@ -437,8 +440,9 @@ class MultiDomainBasicAuth(AuthBase):
|
|
|
437
440
|
|
|
438
441
|
return url, username, password
|
|
439
442
|
|
|
440
|
-
def __call__(self, req:
|
|
443
|
+
def __call__(self, req: PreparedRequest) -> PreparedRequest:
|
|
441
444
|
# Get credentials for this request
|
|
445
|
+
assert req.url is not None
|
|
442
446
|
url, username, password = self._get_url_and_credentials(req.url)
|
|
443
447
|
|
|
444
448
|
# Set the url of the request to the url without any credentials
|
|
@@ -167,14 +167,13 @@ class Downloader:
|
|
|
167
167
|
self,
|
|
168
168
|
session: PipSession,
|
|
169
169
|
progress_bar: BarType,
|
|
170
|
-
resume_retries: int,
|
|
171
170
|
) -> None:
|
|
172
|
-
assert (
|
|
173
|
-
resume_retries >= 0
|
|
174
|
-
), "Number of max resume retries must be bigger or equal to zero"
|
|
175
171
|
self._session = session
|
|
176
172
|
self._progress_bar = progress_bar
|
|
177
|
-
self._resume_retries = resume_retries
|
|
173
|
+
self._resume_retries = session.resume_retries
|
|
174
|
+
assert (
|
|
175
|
+
self._resume_retries >= 0
|
|
176
|
+
), "Number of max resume retries must be bigger or equal to zero"
|
|
178
177
|
|
|
179
178
|
def batch(
|
|
180
179
|
self, links: Iterable[Link], location: str
|