pip 25.2__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/__init__.py +0 -0
- pip/_internal/build_env.py +265 -8
- pip/_internal/cache.py +1 -1
- pip/_internal/cli/base_command.py +11 -0
- pip/_internal/cli/cmdoptions.py +200 -71
- 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 +96 -36
- pip/_internal/cli/progress_bars.py +4 -2
- pip/_internal/cli/req_command.py +126 -30
- pip/_internal/commands/cache.py +24 -0
- pip/_internal/commands/completion.py +2 -1
- pip/_internal/commands/download.py +12 -11
- pip/_internal/commands/index.py +13 -6
- pip/_internal/commands/install.py +55 -43
- pip/_internal/commands/list.py +14 -16
- pip/_internal/commands/lock.py +19 -14
- pip/_internal/commands/wheel.py +13 -23
- pip/_internal/configuration.py +1 -2
- pip/_internal/distributions/sdist.py +13 -14
- pip/_internal/exceptions.py +96 -6
- pip/_internal/index/collector.py +2 -3
- pip/_internal/index/package_finder.py +87 -21
- pip/_internal/locations/__init__.py +1 -2
- pip/_internal/locations/_sysconfig.py +4 -1
- pip/_internal/metadata/__init__.py +7 -2
- pip/_internal/metadata/importlib/_dists.py +8 -2
- 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/models/wheel.py +5 -66
- pip/_internal/network/auth.py +6 -2
- pip/_internal/network/cache.py +6 -11
- pip/_internal/network/download.py +4 -5
- pip/_internal/network/lazy_wheel.py +5 -3
- pip/_internal/network/session.py +14 -10
- pip/_internal/operations/build/wheel.py +4 -4
- pip/_internal/operations/build/wheel_editable.py +4 -4
- pip/_internal/operations/install/wheel.py +1 -2
- pip/_internal/operations/prepare.py +9 -4
- pip/_internal/pyproject.py +2 -61
- pip/_internal/req/__init__.py +1 -3
- pip/_internal/req/constructors.py +45 -39
- pip/_internal/req/pep723.py +41 -0
- pip/_internal/req/req_file.py +10 -2
- pip/_internal/req/req_install.py +32 -141
- pip/_internal/resolution/resolvelib/candidates.py +20 -11
- pip/_internal/resolution/resolvelib/factory.py +43 -1
- pip/_internal/resolution/resolvelib/provider.py +9 -0
- pip/_internal/resolution/resolvelib/reporter.py +21 -8
- pip/_internal/resolution/resolvelib/requirements.py +7 -3
- pip/_internal/resolution/resolvelib/resolver.py +2 -6
- pip/_internal/self_outdated_check.py +17 -16
- pip/_internal/utils/datetime.py +18 -0
- pip/_internal/utils/filesystem.py +52 -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 +26 -1
- pip/_internal/vcs/versioncontrol.py +3 -1
- pip/_internal/wheel_builder.py +23 -96
- pip/_vendor/README.rst +180 -0
- pip/_vendor/cachecontrol/LICENSE.txt +13 -0
- 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/LICENSE +20 -0
- pip/_vendor/certifi/__init__.py +1 -1
- pip/_vendor/certifi/cacert.pem +62 -372
- pip/_vendor/dependency_groups/LICENSE.txt +9 -0
- pip/_vendor/distlib/LICENSE.txt +284 -0
- pip/_vendor/distro/LICENSE +202 -0
- pip/_vendor/idna/LICENSE.md +31 -0
- 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/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/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/pkg_resources/LICENSE +17 -0
- 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/platformdirs/windows.py +7 -1
- pip/_vendor/pygments/LICENSE +25 -0
- 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/sessions.py +1 -1
- pip/_vendor/resolvelib/LICENSE +13 -0
- pip/_vendor/resolvelib/__init__.py +1 -1
- pip/_vendor/resolvelib/resolvers/abstract.py +3 -3
- pip/_vendor/resolvelib/resolvers/resolution.py +5 -0
- pip/_vendor/rich/LICENSE +19 -0
- pip/_vendor/rich/style.py +7 -11
- 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 +14 -6
- pip/_vendor/truststore/_openssl.py +3 -1
- pip/_vendor/urllib3/LICENSE.txt +21 -0
- pip/_vendor/vendor.txt +11 -11
- {pip-25.2.dist-info → pip-26.0.dist-info}/METADATA +10 -11
- {pip-25.2.dist-info → pip-26.0.dist-info}/RECORD +158 -139
- {pip-25.2.dist-info → pip-26.0.dist-info}/WHEEL +1 -2
- pip-26.0.dist-info/entry_points.txt +4 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/AUTHORS.txt +27 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/idna/LICENSE.md +1 -1
- pip/_internal/models/pylock.py +0 -188
- pip/_internal/operations/build/metadata_legacy.py +0 -73
- pip/_internal/operations/build/wheel_legacy.py +0 -119
- pip/_internal/operations/install/editable_legacy.py +0 -48
- pip/_internal/utils/setuptools_build.py +0 -149
- pip-25.2.dist-info/entry_points.txt +0 -3
- pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER +0 -3
- pip-25.2.dist-info/top_level.txt +0 -1
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/certifi/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/distlib/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/distro/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/msgpack/COPYING +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.BSD +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pkg_resources/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/platformdirs/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pygments/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/requests/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/resolvelib/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/rich/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/tomli/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/tomli_w/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/truststore/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/urllib3/LICENSE.txt +0 -0
pip/_internal/cli/cmdoptions.py
CHANGED
|
@@ -11,7 +11,6 @@ pass on state. To be consistent, all options will follow this design.
|
|
|
11
11
|
# mypy: strict-optional=False
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
-
import importlib.util
|
|
15
14
|
import logging
|
|
16
15
|
import os
|
|
17
16
|
import pathlib
|
|
@@ -28,7 +27,9 @@ from pip._internal.exceptions import CommandError
|
|
|
28
27
|
from pip._internal.locations import USER_CACHE_DIR, get_src_prefix
|
|
29
28
|
from pip._internal.models.format_control import FormatControl
|
|
30
29
|
from pip._internal.models.index import PyPI
|
|
30
|
+
from pip._internal.models.release_control import ReleaseControl
|
|
31
31
|
from pip._internal.models.target_python import TargetPython
|
|
32
|
+
from pip._internal.utils.datetime import parse_iso_datetime
|
|
32
33
|
from pip._internal.utils.hashes import STRONG_HASHES
|
|
33
34
|
from pip._internal.utils.misc import strtobool
|
|
34
35
|
|
|
@@ -101,6 +102,29 @@ def check_dist_restriction(options: Values, check_target: bool = False) -> None:
|
|
|
101
102
|
)
|
|
102
103
|
|
|
103
104
|
|
|
105
|
+
def check_build_constraints(options: Values) -> None:
|
|
106
|
+
"""Function for validating build constraints options.
|
|
107
|
+
|
|
108
|
+
:param options: The OptionParser options.
|
|
109
|
+
"""
|
|
110
|
+
if hasattr(options, "build_constraints") and options.build_constraints:
|
|
111
|
+
if not options.build_isolation:
|
|
112
|
+
raise CommandError(
|
|
113
|
+
"--build-constraint cannot be used with --no-build-isolation."
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Import here to avoid circular imports
|
|
117
|
+
from pip._internal.network.session import PipSession
|
|
118
|
+
from pip._internal.req.req_file import get_file_content
|
|
119
|
+
|
|
120
|
+
# Eagerly check build constraints file contents
|
|
121
|
+
# is valid so that we don't fail in when trying
|
|
122
|
+
# to check constraints in isolated build process
|
|
123
|
+
with PipSession() as session:
|
|
124
|
+
for constraint_file in options.build_constraints:
|
|
125
|
+
get_file_content(constraint_file, session)
|
|
126
|
+
|
|
127
|
+
|
|
104
128
|
def _path_option_check(option: Option, opt: str, value: str) -> str:
|
|
105
129
|
return os.path.expanduser(value)
|
|
106
130
|
|
|
@@ -161,8 +185,7 @@ require_virtualenv: Callable[..., Option] = partial(
|
|
|
161
185
|
action="store_true",
|
|
162
186
|
default=False,
|
|
163
187
|
help=(
|
|
164
|
-
"Allow pip to only run in a virtual environment; "
|
|
165
|
-
"exit with an error otherwise."
|
|
188
|
+
"Allow pip to only run in a virtual environment; exit with an error otherwise."
|
|
166
189
|
),
|
|
167
190
|
)
|
|
168
191
|
|
|
@@ -405,6 +428,54 @@ def find_links() -> Option:
|
|
|
405
428
|
)
|
|
406
429
|
|
|
407
430
|
|
|
431
|
+
def _handle_uploaded_prior_to(
|
|
432
|
+
option: Option, opt: str, value: str, parser: OptionParser
|
|
433
|
+
) -> None:
|
|
434
|
+
"""
|
|
435
|
+
This is an optparse.Option callback for the --uploaded-prior-to option.
|
|
436
|
+
|
|
437
|
+
Parses an ISO 8601 datetime string. If no timezone is specified in the string,
|
|
438
|
+
local timezone is used.
|
|
439
|
+
|
|
440
|
+
Note: This option only works with indexes that provide upload-time metadata
|
|
441
|
+
as specified in the simple repository API:
|
|
442
|
+
https://packaging.python.org/en/latest/specifications/simple-repository-api/
|
|
443
|
+
"""
|
|
444
|
+
if value is None:
|
|
445
|
+
return None
|
|
446
|
+
|
|
447
|
+
try:
|
|
448
|
+
uploaded_prior_to = parse_iso_datetime(value)
|
|
449
|
+
# Use local timezone if no offset is given in the ISO string.
|
|
450
|
+
if uploaded_prior_to.tzinfo is None:
|
|
451
|
+
uploaded_prior_to = uploaded_prior_to.astimezone()
|
|
452
|
+
parser.values.uploaded_prior_to = uploaded_prior_to
|
|
453
|
+
except ValueError as exc:
|
|
454
|
+
msg = (
|
|
455
|
+
f"invalid value: {value!r}: {exc}. "
|
|
456
|
+
f"Expected an ISO 8601 datetime string, "
|
|
457
|
+
f"e.g '2023-01-01' or '2023-01-01T00:00:00Z'"
|
|
458
|
+
)
|
|
459
|
+
raise_option_error(parser, option=option, msg=msg)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def uploaded_prior_to() -> Option:
|
|
463
|
+
return Option(
|
|
464
|
+
"--uploaded-prior-to",
|
|
465
|
+
dest="uploaded_prior_to",
|
|
466
|
+
metavar="datetime",
|
|
467
|
+
action="callback",
|
|
468
|
+
callback=_handle_uploaded_prior_to,
|
|
469
|
+
type="str",
|
|
470
|
+
help=(
|
|
471
|
+
"Only consider packages uploaded prior to the given date time. "
|
|
472
|
+
"Accepts ISO 8601 strings (e.g., '2023-01-01T00:00:00Z'). "
|
|
473
|
+
"Uses local timezone if none specified. Only effective when "
|
|
474
|
+
"installing from indexes that provide upload-time metadata."
|
|
475
|
+
),
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
|
|
408
479
|
def trusted_host() -> Option:
|
|
409
480
|
return Option(
|
|
410
481
|
"--trusted-host",
|
|
@@ -430,6 +501,21 @@ def constraints() -> Option:
|
|
|
430
501
|
)
|
|
431
502
|
|
|
432
503
|
|
|
504
|
+
def build_constraints() -> Option:
|
|
505
|
+
return Option(
|
|
506
|
+
"--build-constraint",
|
|
507
|
+
dest="build_constraints",
|
|
508
|
+
action="append",
|
|
509
|
+
type="str",
|
|
510
|
+
default=[],
|
|
511
|
+
metavar="file",
|
|
512
|
+
help=(
|
|
513
|
+
"Constrain build dependencies using the given constraints file. "
|
|
514
|
+
"This option can be used multiple times."
|
|
515
|
+
),
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
|
|
433
519
|
def requirements() -> Option:
|
|
434
520
|
return Option(
|
|
435
521
|
"-r",
|
|
@@ -443,6 +529,18 @@ def requirements() -> Option:
|
|
|
443
529
|
)
|
|
444
530
|
|
|
445
531
|
|
|
532
|
+
def requirements_from_scripts() -> Option:
|
|
533
|
+
return Option(
|
|
534
|
+
"--requirements-from-script",
|
|
535
|
+
action="append",
|
|
536
|
+
default=[],
|
|
537
|
+
dest="requirements_from_scripts",
|
|
538
|
+
metavar="file",
|
|
539
|
+
help="Install dependencies of the given script file"
|
|
540
|
+
"as defined by PEP 723 inline metadata. ",
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
|
|
446
544
|
def editable() -> Option:
|
|
447
545
|
return Option(
|
|
448
546
|
"-e",
|
|
@@ -544,6 +642,86 @@ def only_binary() -> Option:
|
|
|
544
642
|
)
|
|
545
643
|
|
|
546
644
|
|
|
645
|
+
def _get_release_control(values: Values, option: Option) -> Any:
|
|
646
|
+
"""Get a release_control object."""
|
|
647
|
+
return getattr(values, option.dest)
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def _handle_all_releases(
|
|
651
|
+
option: Option, opt_str: str, value: str, parser: OptionParser
|
|
652
|
+
) -> None:
|
|
653
|
+
existing = _get_release_control(parser.values, option)
|
|
654
|
+
existing.handle_mutual_excludes(
|
|
655
|
+
value,
|
|
656
|
+
existing.all_releases,
|
|
657
|
+
existing.only_final,
|
|
658
|
+
"all_releases",
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
def _handle_only_final(
|
|
663
|
+
option: Option, opt_str: str, value: str, parser: OptionParser
|
|
664
|
+
) -> None:
|
|
665
|
+
existing = _get_release_control(parser.values, option)
|
|
666
|
+
existing.handle_mutual_excludes(
|
|
667
|
+
value,
|
|
668
|
+
existing.only_final,
|
|
669
|
+
existing.all_releases,
|
|
670
|
+
"only_final",
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def all_releases() -> Option:
|
|
675
|
+
release_control = ReleaseControl(set(), set())
|
|
676
|
+
return Option(
|
|
677
|
+
"--all-releases",
|
|
678
|
+
dest="release_control",
|
|
679
|
+
action="callback",
|
|
680
|
+
callback=_handle_all_releases,
|
|
681
|
+
type="str",
|
|
682
|
+
default=release_control,
|
|
683
|
+
help="Allow all release types (including pre-releases) for a package. "
|
|
684
|
+
"Can be supplied multiple times, and each time adds to the existing "
|
|
685
|
+
'value. Accepts either ":all:" to allow pre-releases for all '
|
|
686
|
+
'packages, ":none:" to empty the set (notice the colons), or one or '
|
|
687
|
+
"more package names with commas between them (no colons). Cannot be "
|
|
688
|
+
"used with --pre.",
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def only_final() -> Option:
|
|
693
|
+
release_control = ReleaseControl(set(), set())
|
|
694
|
+
return Option(
|
|
695
|
+
"--only-final",
|
|
696
|
+
dest="release_control",
|
|
697
|
+
action="callback",
|
|
698
|
+
callback=_handle_only_final,
|
|
699
|
+
type="str",
|
|
700
|
+
default=release_control,
|
|
701
|
+
help="Only allow final releases (no pre-releases) for a package. Can be "
|
|
702
|
+
"supplied multiple times, and each time adds to the existing value. "
|
|
703
|
+
'Accepts either ":all:" to disable pre-releases for all packages, '
|
|
704
|
+
'":none:" to empty the set, or one or more package names with commas '
|
|
705
|
+
"between them. Cannot be used with --pre.",
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def check_release_control_exclusive(options: Values) -> None:
|
|
710
|
+
"""
|
|
711
|
+
Raise an error if --pre is used with --all-releases or --only-final,
|
|
712
|
+
and transform --pre into --all-releases :all: if used alone.
|
|
713
|
+
"""
|
|
714
|
+
if not hasattr(options, "pre") or not options.pre:
|
|
715
|
+
return
|
|
716
|
+
|
|
717
|
+
release_control = options.release_control
|
|
718
|
+
if release_control.all_releases or release_control.only_final:
|
|
719
|
+
raise CommandError("--pre cannot be used with --all-releases or --only-final.")
|
|
720
|
+
|
|
721
|
+
# Transform --pre into --all-releases :all:
|
|
722
|
+
release_control.all_releases.add(":all:")
|
|
723
|
+
|
|
724
|
+
|
|
547
725
|
platforms: Callable[..., Option] = partial(
|
|
548
726
|
Option,
|
|
549
727
|
"--platform",
|
|
@@ -796,6 +974,7 @@ ignore_requires_python: Callable[..., Option] = partial(
|
|
|
796
974
|
help="Ignore the Requires-Python information.",
|
|
797
975
|
)
|
|
798
976
|
|
|
977
|
+
|
|
799
978
|
no_build_isolation: Callable[..., Option] = partial(
|
|
800
979
|
Option,
|
|
801
980
|
"--no-build-isolation",
|
|
@@ -813,62 +992,16 @@ check_build_deps: Callable[..., Option] = partial(
|
|
|
813
992
|
dest="check_build_deps",
|
|
814
993
|
action="store_true",
|
|
815
994
|
default=False,
|
|
816
|
-
help="Check the build dependencies
|
|
995
|
+
help="Check the build dependencies.",
|
|
817
996
|
)
|
|
818
997
|
|
|
819
998
|
|
|
820
|
-
def _handle_no_use_pep517(
|
|
821
|
-
option: Option, opt: str, value: str, parser: OptionParser
|
|
822
|
-
) -> None:
|
|
823
|
-
"""
|
|
824
|
-
Process a value provided for the --no-use-pep517 option.
|
|
825
|
-
|
|
826
|
-
This is an optparse.Option callback for the no_use_pep517 option.
|
|
827
|
-
"""
|
|
828
|
-
# Since --no-use-pep517 doesn't accept arguments, the value argument
|
|
829
|
-
# will be None if --no-use-pep517 is passed via the command-line.
|
|
830
|
-
# However, the value can be non-None if the option is triggered e.g.
|
|
831
|
-
# by an environment variable, for example "PIP_NO_USE_PEP517=true".
|
|
832
|
-
if value is not None:
|
|
833
|
-
msg = """A value was passed for --no-use-pep517,
|
|
834
|
-
probably using either the PIP_NO_USE_PEP517 environment variable
|
|
835
|
-
or the "no-use-pep517" config file option. Use an appropriate value
|
|
836
|
-
of the PIP_USE_PEP517 environment variable or the "use-pep517"
|
|
837
|
-
config file option instead.
|
|
838
|
-
"""
|
|
839
|
-
raise_option_error(parser, option=option, msg=msg)
|
|
840
|
-
|
|
841
|
-
# If user doesn't wish to use pep517, we check if setuptools is installed
|
|
842
|
-
# and raise error if it is not.
|
|
843
|
-
packages = ("setuptools",)
|
|
844
|
-
if not all(importlib.util.find_spec(package) for package in packages):
|
|
845
|
-
msg = (
|
|
846
|
-
f"It is not possible to use --no-use-pep517 "
|
|
847
|
-
f"without {' and '.join(packages)} installed."
|
|
848
|
-
)
|
|
849
|
-
raise_option_error(parser, option=option, msg=msg)
|
|
850
|
-
|
|
851
|
-
# Otherwise, --no-use-pep517 was passed via the command-line.
|
|
852
|
-
parser.values.use_pep517 = False
|
|
853
|
-
|
|
854
|
-
|
|
855
999
|
use_pep517: Any = partial(
|
|
856
1000
|
Option,
|
|
857
1001
|
"--use-pep517",
|
|
858
1002
|
dest="use_pep517",
|
|
859
1003
|
action="store_true",
|
|
860
|
-
default=
|
|
861
|
-
help="Use PEP 517 for building source distributions "
|
|
862
|
-
"(use --no-use-pep517 to force legacy behaviour).",
|
|
863
|
-
)
|
|
864
|
-
|
|
865
|
-
no_use_pep517: Any = partial(
|
|
866
|
-
Option,
|
|
867
|
-
"--no-use-pep517",
|
|
868
|
-
dest="use_pep517",
|
|
869
|
-
action="callback",
|
|
870
|
-
callback=_handle_no_use_pep517,
|
|
871
|
-
default=None,
|
|
1004
|
+
default=True,
|
|
872
1005
|
help=SUPPRESS_HELP,
|
|
873
1006
|
)
|
|
874
1007
|
|
|
@@ -901,30 +1034,11 @@ config_settings: Callable[..., Option] = partial(
|
|
|
901
1034
|
action="callback",
|
|
902
1035
|
callback=_handle_config_settings,
|
|
903
1036
|
metavar="settings",
|
|
904
|
-
help="Configuration settings to be passed to the
|
|
1037
|
+
help="Configuration settings to be passed to the build backend. "
|
|
905
1038
|
"Settings take the form KEY=VALUE. Use multiple --config-settings options "
|
|
906
1039
|
"to pass multiple keys to the backend.",
|
|
907
1040
|
)
|
|
908
1041
|
|
|
909
|
-
build_options: Callable[..., Option] = partial(
|
|
910
|
-
Option,
|
|
911
|
-
"--build-option",
|
|
912
|
-
dest="build_options",
|
|
913
|
-
metavar="options",
|
|
914
|
-
action="append",
|
|
915
|
-
help="Extra arguments to be supplied to 'setup.py bdist_wheel'.",
|
|
916
|
-
)
|
|
917
|
-
|
|
918
|
-
global_options: Callable[..., Option] = partial(
|
|
919
|
-
Option,
|
|
920
|
-
"--global-option",
|
|
921
|
-
dest="global_options",
|
|
922
|
-
action="append",
|
|
923
|
-
metavar="options",
|
|
924
|
-
help="Extra global options to be supplied to the setup.py "
|
|
925
|
-
"call before the install or bdist_wheel command.",
|
|
926
|
-
)
|
|
927
|
-
|
|
928
1042
|
no_clean: Callable[..., Option] = partial(
|
|
929
1043
|
Option,
|
|
930
1044
|
"--no-clean",
|
|
@@ -1072,6 +1186,8 @@ use_new_feature: Callable[..., Option] = partial(
|
|
|
1072
1186
|
default=[],
|
|
1073
1187
|
choices=[
|
|
1074
1188
|
"fast-deps",
|
|
1189
|
+
"build-constraint",
|
|
1190
|
+
"inprocess-build-deps",
|
|
1075
1191
|
]
|
|
1076
1192
|
+ ALWAYS_ENABLED_FEATURES,
|
|
1077
1193
|
help="Enable new functionality, that may be backward incompatible.",
|
|
@@ -1134,5 +1250,18 @@ index_group: dict[str, Any] = {
|
|
|
1134
1250
|
extra_index_url,
|
|
1135
1251
|
no_index,
|
|
1136
1252
|
find_links,
|
|
1253
|
+
uploaded_prior_to,
|
|
1254
|
+
],
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
package_selection_group: dict[str, Any] = {
|
|
1258
|
+
"name": "Package Selection Options",
|
|
1259
|
+
"options": [
|
|
1260
|
+
pre,
|
|
1261
|
+
all_releases,
|
|
1262
|
+
only_final,
|
|
1263
|
+
no_binary,
|
|
1264
|
+
only_binary,
|
|
1265
|
+
prefer_binary,
|
|
1137
1266
|
],
|
|
1138
1267
|
}
|
|
@@ -23,6 +23,8 @@ from pip._internal.cli.command_context import CommandContextMixIn
|
|
|
23
23
|
if TYPE_CHECKING:
|
|
24
24
|
from ssl import SSLContext
|
|
25
25
|
|
|
26
|
+
from pip._vendor.packaging.utils import NormalizedName
|
|
27
|
+
|
|
26
28
|
from pip._internal.network.session import PipSession
|
|
27
29
|
|
|
28
30
|
logger = logging.getLogger(__name__)
|
|
@@ -103,6 +105,7 @@ class SessionCommandMixin(CommandContextMixIn):
|
|
|
103
105
|
session = PipSession(
|
|
104
106
|
cache=os.path.join(cache_dir, "http-v2") if cache_dir else None,
|
|
105
107
|
retries=retries if retries is not None else options.retries,
|
|
108
|
+
resume_retries=options.resume_retries,
|
|
106
109
|
trusted_hosts=options.trusted_hosts,
|
|
107
110
|
index_urls=self._get_index_urls(options),
|
|
108
111
|
ssl_context=ssl_context,
|
|
@@ -149,6 +152,23 @@ class IndexGroupCommand(Command, SessionCommandMixin):
|
|
|
149
152
|
This also corresponds to the commands that permit the pip version check.
|
|
150
153
|
"""
|
|
151
154
|
|
|
155
|
+
def should_exclude_prerelease(
|
|
156
|
+
self, options: Values, package_name: NormalizedName
|
|
157
|
+
) -> bool:
|
|
158
|
+
"""
|
|
159
|
+
Determine if pre-releases should be excluded for a package.
|
|
160
|
+
"""
|
|
161
|
+
# Check per-package release control settings
|
|
162
|
+
if options.release_control:
|
|
163
|
+
allow_prereleases = options.release_control.allows_prereleases(package_name)
|
|
164
|
+
if allow_prereleases is True:
|
|
165
|
+
return False # Include pre-releases
|
|
166
|
+
elif allow_prereleases is False:
|
|
167
|
+
return True # Exclude pre-releases
|
|
168
|
+
|
|
169
|
+
# No specific setting: exclude prereleases by default
|
|
170
|
+
return True
|
|
171
|
+
|
|
152
172
|
def handle_pip_version_check(self, options: Values) -> None:
|
|
153
173
|
"""
|
|
154
174
|
Do the pip version check if not disabled.
|
pip/_internal/cli/main.py
CHANGED
|
@@ -8,12 +8,6 @@ import os
|
|
|
8
8
|
import sys
|
|
9
9
|
import warnings
|
|
10
10
|
|
|
11
|
-
from pip._internal.cli.autocompletion import autocomplete
|
|
12
|
-
from pip._internal.cli.main_parser import parse_command
|
|
13
|
-
from pip._internal.commands import create_command
|
|
14
|
-
from pip._internal.exceptions import PipError
|
|
15
|
-
from pip._internal.utils import deprecation
|
|
16
|
-
|
|
17
11
|
logger = logging.getLogger(__name__)
|
|
18
12
|
|
|
19
13
|
|
|
@@ -45,6 +39,17 @@ logger = logging.getLogger(__name__)
|
|
|
45
39
|
|
|
46
40
|
|
|
47
41
|
def main(args: list[str] | None = None) -> int:
|
|
42
|
+
# NOTE: Lazy imports to speed up import of this module,
|
|
43
|
+
# which is imported from the pip console script. This doesn't
|
|
44
|
+
# speed up normal pip execution, but might be important in the future
|
|
45
|
+
# if we use ``multiprocessing`` module,
|
|
46
|
+
# which imports __main__ for each spawned subprocess.
|
|
47
|
+
from pip._internal.cli.autocompletion import autocomplete
|
|
48
|
+
from pip._internal.cli.main_parser import parse_command
|
|
49
|
+
from pip._internal.commands import create_command
|
|
50
|
+
from pip._internal.exceptions import PipError
|
|
51
|
+
from pip._internal.utils import deprecation
|
|
52
|
+
|
|
48
53
|
if args is None:
|
|
49
54
|
args = sys.argv[1:]
|
|
50
55
|
|
pip/_internal/cli/main_parser.py
CHANGED
|
@@ -6,6 +6,8 @@ import os
|
|
|
6
6
|
import subprocess
|
|
7
7
|
import sys
|
|
8
8
|
|
|
9
|
+
from pip._vendor.rich.markup import escape
|
|
10
|
+
|
|
9
11
|
from pip._internal.build_env import get_runnable_pip
|
|
10
12
|
from pip._internal.cli import cmdoptions
|
|
11
13
|
from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
|
|
@@ -39,7 +41,7 @@ def create_main_parser() -> ConfigOptionParser:
|
|
|
39
41
|
|
|
40
42
|
# create command listing for description
|
|
41
43
|
description = [""] + [
|
|
42
|
-
f"{name:27} {command_info.summary}"
|
|
44
|
+
f"[optparse.longargs]{name:27}[/] {escape(command_info.summary)}"
|
|
43
45
|
for name, command_info in commands_dict.items()
|
|
44
46
|
]
|
|
45
47
|
parser.description = "\n".join(description)
|
pip/_internal/cli/parser.py
CHANGED
|
@@ -4,6 +4,8 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import optparse
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
7
9
|
import shutil
|
|
8
10
|
import sys
|
|
9
11
|
import textwrap
|
|
@@ -11,8 +13,12 @@ from collections.abc import Generator
|
|
|
11
13
|
from contextlib import suppress
|
|
12
14
|
from typing import Any, NoReturn
|
|
13
15
|
|
|
16
|
+
from pip._vendor.rich.markup import escape
|
|
17
|
+
from pip._vendor.rich.theme import Theme
|
|
18
|
+
|
|
14
19
|
from pip._internal.cli.status_codes import UNKNOWN_ERROR
|
|
15
20
|
from pip._internal.configuration import Configuration, ConfigurationError
|
|
21
|
+
from pip._internal.utils.logging import PipConsole
|
|
16
22
|
from pip._internal.utils.misc import redact_auth_from_url, strtobool
|
|
17
23
|
|
|
18
24
|
logger = logging.getLogger(__name__)
|
|
@@ -21,6 +27,17 @@ logger = logging.getLogger(__name__)
|
|
|
21
27
|
class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
|
|
22
28
|
"""A prettier/less verbose help formatter for optparse."""
|
|
23
29
|
|
|
30
|
+
styles = {
|
|
31
|
+
"optparse.shortargs": "green",
|
|
32
|
+
"optparse.longargs": "cyan",
|
|
33
|
+
"optparse.groups": "bold blue",
|
|
34
|
+
"optparse.metavar": "yellow",
|
|
35
|
+
}
|
|
36
|
+
highlights = {
|
|
37
|
+
r"\s(-{1}[\w]+[\w-]*)": "shortargs", # highlight -letter as short args
|
|
38
|
+
r"\s(-{2}[\w]+[\w-]*)": "longargs", # highlight --words as long args
|
|
39
|
+
}
|
|
40
|
+
|
|
24
41
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
25
42
|
# help position must be aligned with __init__.parseopts.description
|
|
26
43
|
kwargs["max_help_position"] = 30
|
|
@@ -29,61 +46,82 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
|
|
|
29
46
|
super().__init__(*args, **kwargs)
|
|
30
47
|
|
|
31
48
|
def format_option_strings(self, option: optparse.Option) -> str:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def _format_option_strings(
|
|
35
|
-
self, option: optparse.Option, mvarfmt: str = " <{}>", optsep: str = ", "
|
|
36
|
-
) -> str:
|
|
37
|
-
"""
|
|
38
|
-
Return a comma-separated list of option strings and metavars.
|
|
39
|
-
|
|
40
|
-
:param option: tuple of (short opt, long opt), e.g: ('-f', '--format')
|
|
41
|
-
:param mvarfmt: metavar format string
|
|
42
|
-
:param optsep: separator
|
|
43
|
-
"""
|
|
49
|
+
"""Return a comma-separated list of option strings and metavars."""
|
|
44
50
|
opts = []
|
|
45
51
|
|
|
46
52
|
if option._short_opts:
|
|
47
|
-
opts.append(option._short_opts[0])
|
|
53
|
+
opts.append(f"[optparse.shortargs]{option._short_opts[0]}[/]")
|
|
48
54
|
if option._long_opts:
|
|
49
|
-
opts.append(option._long_opts[0])
|
|
55
|
+
opts.append(f"[optparse.longargs]{option._long_opts[0]}[/]")
|
|
50
56
|
if len(opts) > 1:
|
|
51
|
-
opts.insert(1,
|
|
57
|
+
opts.insert(1, ", ")
|
|
52
58
|
|
|
53
59
|
if option.takes_value():
|
|
54
60
|
assert option.dest is not None
|
|
55
61
|
metavar = option.metavar or option.dest.lower()
|
|
56
|
-
opts.append(
|
|
62
|
+
opts.append(f" [optparse.metavar]<{escape(metavar.lower())}>[/]")
|
|
57
63
|
|
|
58
64
|
return "".join(opts)
|
|
59
65
|
|
|
66
|
+
def format_option(self, option: optparse.Option) -> str:
|
|
67
|
+
"""Overridden method with Rich support."""
|
|
68
|
+
# fmt: off
|
|
69
|
+
result = []
|
|
70
|
+
opts = self.option_strings[option]
|
|
71
|
+
opt_width = self.help_position - self.current_indent - 2
|
|
72
|
+
# Remove the rich style tags before calculating width during
|
|
73
|
+
# text wrap calculations. Also store the length removed to adjust
|
|
74
|
+
# the padding in the else branch.
|
|
75
|
+
stripped = re.sub(r"(\[[a-z.]+\])|(\[\/\])", "", opts)
|
|
76
|
+
style_tag_length = len(opts) - len(stripped)
|
|
77
|
+
if len(stripped) > opt_width:
|
|
78
|
+
opts = "%*s%s\n" % (self.current_indent, "", opts) # noqa: UP031
|
|
79
|
+
indent_first = self.help_position
|
|
80
|
+
else: # start help on same line as opts
|
|
81
|
+
opts = "%*s%-*s " % (self.current_indent, "", # noqa: UP031
|
|
82
|
+
opt_width + style_tag_length, opts)
|
|
83
|
+
indent_first = 0
|
|
84
|
+
result.append(opts)
|
|
85
|
+
if option.help:
|
|
86
|
+
help_text = self.expand_default(option)
|
|
87
|
+
help_lines = textwrap.wrap(help_text, self.help_width)
|
|
88
|
+
result.append("%*s%s\n" % (indent_first, "", help_lines[0])) # noqa: UP031
|
|
89
|
+
result.extend(["%*s%s\n" % (self.help_position, "", line) # noqa: UP031
|
|
90
|
+
for line in help_lines[1:]])
|
|
91
|
+
elif opts[-1] != "\n":
|
|
92
|
+
result.append("\n")
|
|
93
|
+
return "".join(result)
|
|
94
|
+
# fmt: on
|
|
95
|
+
|
|
60
96
|
def format_heading(self, heading: str) -> str:
|
|
61
97
|
if heading == "Options":
|
|
62
98
|
return ""
|
|
63
|
-
return heading + "
|
|
99
|
+
return "[optparse.groups]" + escape(heading) + ":[/]\n"
|
|
64
100
|
|
|
65
101
|
def format_usage(self, usage: str) -> str:
|
|
66
102
|
"""
|
|
67
103
|
Ensure there is only one newline between usage and the first heading
|
|
68
104
|
if there is no description.
|
|
69
105
|
"""
|
|
70
|
-
|
|
106
|
+
contents = self.indent_lines(textwrap.dedent(usage), " ")
|
|
107
|
+
msg = f"\n[optparse.groups]Usage:[/] {escape(contents)}\n"
|
|
71
108
|
return msg
|
|
72
109
|
|
|
73
110
|
def format_description(self, description: str | None) -> str:
|
|
74
111
|
# leave full control over description to us
|
|
75
112
|
if description:
|
|
76
113
|
if hasattr(self.parser, "main"):
|
|
77
|
-
label = "Commands"
|
|
114
|
+
label = "[optparse.groups]Commands:[/]"
|
|
78
115
|
else:
|
|
79
|
-
label = "Description"
|
|
116
|
+
label = "[optparse.groups]Description:[/]"
|
|
117
|
+
|
|
80
118
|
# some doc strings have initial newlines, some don't
|
|
81
119
|
description = description.lstrip("\n")
|
|
82
120
|
# some doc strings have final newlines and spaces, some don't
|
|
83
121
|
description = description.rstrip()
|
|
84
122
|
# dedent, then reindent
|
|
85
123
|
description = self.indent_lines(textwrap.dedent(description), " ")
|
|
86
|
-
description = f"{label}
|
|
124
|
+
description = f"{label}\n{description}\n"
|
|
87
125
|
return description
|
|
88
126
|
else:
|
|
89
127
|
return ""
|
|
@@ -91,10 +129,17 @@ class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
|
|
|
91
129
|
def format_epilog(self, epilog: str | None) -> str:
|
|
92
130
|
# leave full control over epilog to us
|
|
93
131
|
if epilog:
|
|
94
|
-
return epilog
|
|
132
|
+
return escape(epilog)
|
|
95
133
|
else:
|
|
96
134
|
return ""
|
|
97
135
|
|
|
136
|
+
def expand_default(self, option: optparse.Option) -> str:
|
|
137
|
+
"""Overridden HelpFormatter.expand_default() which colorizes flags."""
|
|
138
|
+
help = escape(super().expand_default(option))
|
|
139
|
+
for regex, style in self.highlights.items():
|
|
140
|
+
help = re.sub(regex, rf"[optparse.{style}] \1[/]", help)
|
|
141
|
+
return help
|
|
142
|
+
|
|
98
143
|
def indent_lines(self, text: str, indent: str) -> str:
|
|
99
144
|
new_lines = [indent + line for line in text.split("\n")]
|
|
100
145
|
return "\n".join(new_lines)
|
|
@@ -185,27 +230,29 @@ class ConfigOptionParser(CustomOptionParser):
|
|
|
185
230
|
override_order = ["global", self.name, ":env:"]
|
|
186
231
|
|
|
187
232
|
# Pool the options into different groups
|
|
188
|
-
|
|
189
|
-
|
|
233
|
+
# Use a dict because we need to implement the fallthrough logic after PR 12201
|
|
234
|
+
# was merged which removed the fallthrough logic for options
|
|
235
|
+
section_items_dict: dict[str, dict[str, Any]] = {
|
|
236
|
+
name: {} for name in override_order
|
|
190
237
|
}
|
|
191
238
|
|
|
192
|
-
for _, value in self.config.items():
|
|
239
|
+
for _, value in self.config.items():
|
|
193
240
|
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
241
|
|
|
202
242
|
section, key = section_key.split(".", 1)
|
|
203
243
|
if section in override_order:
|
|
204
|
-
|
|
244
|
+
section_items_dict[section][key] = val
|
|
205
245
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
246
|
+
# Now that we a dict of items per section, convert to list of tuples
|
|
247
|
+
# Make sure we completely remove empty values again
|
|
248
|
+
section_items = {
|
|
249
|
+
name: [(k, v) for k, v in section_items_dict[name].items() if v]
|
|
250
|
+
for name in override_order
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# Yield each group in their override order
|
|
254
|
+
for section in override_order:
|
|
255
|
+
yield from section_items[section]
|
|
209
256
|
|
|
210
257
|
def _update_defaults(self, defaults: dict[str, Any]) -> dict[str, Any]:
|
|
211
258
|
"""Updates the given defaults with values from the config files and
|
|
@@ -296,3 +343,16 @@ class ConfigOptionParser(CustomOptionParser):
|
|
|
296
343
|
def error(self, msg: str) -> NoReturn:
|
|
297
344
|
self.print_usage(sys.stderr)
|
|
298
345
|
self.exit(UNKNOWN_ERROR, f"{msg}\n")
|
|
346
|
+
|
|
347
|
+
def print_help(self, file: Any = None) -> None:
|
|
348
|
+
# This is unfortunate but necessary since arguments may have not been
|
|
349
|
+
# parsed yet at this point, so detect --no-color manually.
|
|
350
|
+
no_color = (
|
|
351
|
+
"--no-color" in sys.argv
|
|
352
|
+
or bool(strtobool(os.environ.get("PIP_NO_COLOR", "no") or "no"))
|
|
353
|
+
or "NO_COLOR" in os.environ
|
|
354
|
+
)
|
|
355
|
+
console = PipConsole(
|
|
356
|
+
theme=Theme(PrettyHelpFormatter.styles), no_color=no_color, file=file
|
|
357
|
+
)
|
|
358
|
+
console.print(self.format_help().rstrip(), highlight=False)
|