ZhengyangZhang commited on
Commit
297cb90
·
verified ·
1 Parent(s): b7beb76

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/pip/_internal/cli/__init__.py +4 -0
  2. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc +0 -0
  6. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/index_command.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc +0 -0
  9. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc +0 -0
  10. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc +0 -0
  11. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc +0 -0
  12. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-312.pyc +0 -0
  13. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc +0 -0
  14. lib/python3.12/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc +0 -0
  15. lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py +176 -0
  16. lib/python3.12/site-packages/pip/_internal/cli/base_command.py +240 -0
  17. lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py +1075 -0
  18. lib/python3.12/site-packages/pip/_internal/cli/command_context.py +27 -0
  19. lib/python3.12/site-packages/pip/_internal/cli/index_command.py +171 -0
  20. lib/python3.12/site-packages/pip/_internal/cli/main.py +80 -0
  21. lib/python3.12/site-packages/pip/_internal/cli/main_parser.py +134 -0
  22. lib/python3.12/site-packages/pip/_internal/cli/parser.py +294 -0
  23. lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py +94 -0
  24. lib/python3.12/site-packages/pip/_internal/cli/req_command.py +329 -0
  25. lib/python3.12/site-packages/pip/_internal/cli/spinners.py +159 -0
  26. lib/python3.12/site-packages/pip/_internal/cli/status_codes.py +6 -0
  27. lib/python3.12/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc +0 -0
  28. lib/python3.12/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc +0 -0
  29. lib/python3.12/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc +0 -0
  30. lib/python3.12/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/pip/_internal/operations/build/__init__.py +0 -0
  32. lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc +0 -0
  33. lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc +0 -0
  34. lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc +0 -0
  35. lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc +0 -0
  36. lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-312.pyc +0 -0
  37. lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc +0 -0
  38. lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc +0 -0
  39. lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-312.pyc +0 -0
  40. lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py +138 -0
  41. lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py +39 -0
  42. lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py +42 -0
  43. lib/python3.12/site-packages/pip/_internal/operations/build/metadata_legacy.py +74 -0
  44. lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py +37 -0
  45. lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py +46 -0
  46. lib/python3.12/site-packages/pip/_internal/operations/build/wheel_legacy.py +102 -0
  47. lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py +2 -0
  48. lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc +0 -0
  49. lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-312.pyc +0 -0
  50. lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc +0 -0
lib/python3.12/site-packages/pip/_internal/cli/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ """Subpackage containing all of pip's command line interface related code
2
+ """
3
+
4
+ # This file intentionally does not import submodules
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (284 Bytes). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc ADDED
Binary file (8.61 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc ADDED
Binary file (10.5 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc ADDED
Binary file (30.4 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-312.pyc ADDED
Binary file (1.78 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/index_command.cpython-312.pyc ADDED
Binary file (7.19 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc ADDED
Binary file (2.31 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc ADDED
Binary file (4.91 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc ADDED
Binary file (15 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc ADDED
Binary file (3.86 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-312.pyc ADDED
Binary file (12.2 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc ADDED
Binary file (7.84 kB). View file
 
lib/python3.12/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc ADDED
Binary file (381 Bytes). View file
 
lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Logic that powers autocompletion installed by ``pip completion``.
2
+ """
3
+
4
+ import optparse
5
+ import os
6
+ import sys
7
+ from itertools import chain
8
+ from typing import Any, Iterable, List, Optional
9
+
10
+ from pip._internal.cli.main_parser import create_main_parser
11
+ from pip._internal.commands import commands_dict, create_command
12
+ from pip._internal.metadata import get_default_environment
13
+
14
+
15
+ def autocomplete() -> None:
16
+ """Entry Point for completion of main and subcommand options."""
17
+ # Don't complete if user hasn't sourced bash_completion file.
18
+ if "PIP_AUTO_COMPLETE" not in os.environ:
19
+ return
20
+ # Don't complete if autocompletion environment variables
21
+ # are not present
22
+ if not os.environ.get("COMP_WORDS") or not os.environ.get("COMP_CWORD"):
23
+ return
24
+ cwords = os.environ["COMP_WORDS"].split()[1:]
25
+ cword = int(os.environ["COMP_CWORD"])
26
+ try:
27
+ current = cwords[cword - 1]
28
+ except IndexError:
29
+ current = ""
30
+
31
+ parser = create_main_parser()
32
+ subcommands = list(commands_dict)
33
+ options = []
34
+
35
+ # subcommand
36
+ subcommand_name: Optional[str] = None
37
+ for word in cwords:
38
+ if word in subcommands:
39
+ subcommand_name = word
40
+ break
41
+ # subcommand options
42
+ if subcommand_name is not None:
43
+ # special case: 'help' subcommand has no options
44
+ if subcommand_name == "help":
45
+ sys.exit(1)
46
+ # special case: list locally installed dists for show and uninstall
47
+ should_list_installed = not current.startswith("-") and subcommand_name in [
48
+ "show",
49
+ "uninstall",
50
+ ]
51
+ if should_list_installed:
52
+ env = get_default_environment()
53
+ lc = current.lower()
54
+ installed = [
55
+ dist.canonical_name
56
+ for dist in env.iter_installed_distributions(local_only=True)
57
+ if dist.canonical_name.startswith(lc)
58
+ and dist.canonical_name not in cwords[1:]
59
+ ]
60
+ # if there are no dists installed, fall back to option completion
61
+ if installed:
62
+ for dist in installed:
63
+ print(dist)
64
+ sys.exit(1)
65
+
66
+ should_list_installables = (
67
+ not current.startswith("-") and subcommand_name == "install"
68
+ )
69
+ if should_list_installables:
70
+ for path in auto_complete_paths(current, "path"):
71
+ print(path)
72
+ sys.exit(1)
73
+
74
+ subcommand = create_command(subcommand_name)
75
+
76
+ for opt in subcommand.parser.option_list_all:
77
+ if opt.help != optparse.SUPPRESS_HELP:
78
+ options += [
79
+ (opt_str, opt.nargs) for opt_str in opt._long_opts + opt._short_opts
80
+ ]
81
+
82
+ # filter out previously specified options from available options
83
+ prev_opts = [x.split("=")[0] for x in cwords[1 : cword - 1]]
84
+ options = [(x, v) for (x, v) in options if x not in prev_opts]
85
+ # filter options by current input
86
+ options = [(k, v) for k, v in options if k.startswith(current)]
87
+ # get completion type given cwords and available subcommand options
88
+ completion_type = get_path_completion_type(
89
+ cwords,
90
+ cword,
91
+ subcommand.parser.option_list_all,
92
+ )
93
+ # get completion files and directories if ``completion_type`` is
94
+ # ``<file>``, ``<dir>`` or ``<path>``
95
+ if completion_type:
96
+ paths = auto_complete_paths(current, completion_type)
97
+ options = [(path, 0) for path in paths]
98
+ for option in options:
99
+ opt_label = option[0]
100
+ # append '=' to options which require args
101
+ if option[1] and option[0][:2] == "--":
102
+ opt_label += "="
103
+ print(opt_label)
104
+ else:
105
+ # show main parser options only when necessary
106
+
107
+ opts = [i.option_list for i in parser.option_groups]
108
+ opts.append(parser.option_list)
109
+ flattened_opts = chain.from_iterable(opts)
110
+ if current.startswith("-"):
111
+ for opt in flattened_opts:
112
+ if opt.help != optparse.SUPPRESS_HELP:
113
+ subcommands += opt._long_opts + opt._short_opts
114
+ else:
115
+ # get completion type given cwords and all available options
116
+ completion_type = get_path_completion_type(cwords, cword, flattened_opts)
117
+ if completion_type:
118
+ subcommands = list(auto_complete_paths(current, completion_type))
119
+
120
+ print(" ".join([x for x in subcommands if x.startswith(current)]))
121
+ sys.exit(1)
122
+
123
+
124
+ def get_path_completion_type(
125
+ cwords: List[str], cword: int, opts: Iterable[Any]
126
+ ) -> Optional[str]:
127
+ """Get the type of path completion (``file``, ``dir``, ``path`` or None)
128
+
129
+ :param cwords: same as the environmental variable ``COMP_WORDS``
130
+ :param cword: same as the environmental variable ``COMP_CWORD``
131
+ :param opts: The available options to check
132
+ :return: path completion type (``file``, ``dir``, ``path`` or None)
133
+ """
134
+ if cword < 2 or not cwords[cword - 2].startswith("-"):
135
+ return None
136
+ for opt in opts:
137
+ if opt.help == optparse.SUPPRESS_HELP:
138
+ continue
139
+ for o in str(opt).split("/"):
140
+ if cwords[cword - 2].split("=")[0] == o:
141
+ if not opt.metavar or any(
142
+ x in ("path", "file", "dir") for x in opt.metavar.split("/")
143
+ ):
144
+ return opt.metavar
145
+ return None
146
+
147
+
148
+ def auto_complete_paths(current: str, completion_type: str) -> Iterable[str]:
149
+ """If ``completion_type`` is ``file`` or ``path``, list all regular files
150
+ and directories starting with ``current``; otherwise only list directories
151
+ starting with ``current``.
152
+
153
+ :param current: The word to be completed
154
+ :param completion_type: path completion type(``file``, ``path`` or ``dir``)
155
+ :return: A generator of regular files and/or directories
156
+ """
157
+ directory, filename = os.path.split(current)
158
+ current_path = os.path.abspath(directory)
159
+ # Don't complete paths if they can't be accessed
160
+ if not os.access(current_path, os.R_OK):
161
+ return
162
+ filename = os.path.normcase(filename)
163
+ # list all files that start with ``filename``
164
+ file_list = (
165
+ x for x in os.listdir(current_path) if os.path.normcase(x).startswith(filename)
166
+ )
167
+ for f in file_list:
168
+ opt = os.path.join(current_path, f)
169
+ comp_file = os.path.normcase(os.path.join(directory, f))
170
+ # complete regular files when there is not ``<dir>`` after option
171
+ # complete directories when there is ``<file>``, ``<path>`` or
172
+ # ``<dir>``after option
173
+ if completion_type != "dir" and os.path.isfile(opt):
174
+ yield comp_file
175
+ elif os.path.isdir(opt):
176
+ yield os.path.join(comp_file, "")
lib/python3.12/site-packages/pip/_internal/cli/base_command.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Base Command class, and related routines"""
2
+
3
+ import logging
4
+ import logging.config
5
+ import optparse
6
+ import os
7
+ import sys
8
+ import traceback
9
+ from optparse import Values
10
+ from typing import List, Optional, Tuple
11
+
12
+ from pip._vendor.rich import reconfigure
13
+ from pip._vendor.rich import traceback as rich_traceback
14
+
15
+ from pip._internal.cli import cmdoptions
16
+ from pip._internal.cli.command_context import CommandContextMixIn
17
+ from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
18
+ from pip._internal.cli.status_codes import (
19
+ ERROR,
20
+ PREVIOUS_BUILD_DIR_ERROR,
21
+ UNKNOWN_ERROR,
22
+ VIRTUALENV_NOT_FOUND,
23
+ )
24
+ from pip._internal.exceptions import (
25
+ BadCommand,
26
+ CommandError,
27
+ DiagnosticPipError,
28
+ InstallationError,
29
+ NetworkConnectionError,
30
+ PreviousBuildDirError,
31
+ )
32
+ from pip._internal.utils.deprecation import deprecated
33
+ from pip._internal.utils.filesystem import check_path_owner
34
+ from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging
35
+ from pip._internal.utils.misc import get_prog, normalize_path
36
+ from pip._internal.utils.temp_dir import TempDirectoryTypeRegistry as TempDirRegistry
37
+ from pip._internal.utils.temp_dir import global_tempdir_manager, tempdir_registry
38
+ from pip._internal.utils.virtualenv import running_under_virtualenv
39
+
40
+ __all__ = ["Command"]
41
+
42
+ logger = logging.getLogger(__name__)
43
+
44
+
45
+ class Command(CommandContextMixIn):
46
+ usage: str = ""
47
+ ignore_require_venv: bool = False
48
+
49
+ def __init__(self, name: str, summary: str, isolated: bool = False) -> None:
50
+ super().__init__()
51
+
52
+ self.name = name
53
+ self.summary = summary
54
+ self.parser = ConfigOptionParser(
55
+ usage=self.usage,
56
+ prog=f"{get_prog()} {name}",
57
+ formatter=UpdatingDefaultsHelpFormatter(),
58
+ add_help_option=False,
59
+ name=name,
60
+ description=self.__doc__,
61
+ isolated=isolated,
62
+ )
63
+
64
+ self.tempdir_registry: Optional[TempDirRegistry] = None
65
+
66
+ # Commands should add options to this option group
67
+ optgroup_name = f"{self.name.capitalize()} Options"
68
+ self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name)
69
+
70
+ # Add the general options
71
+ gen_opts = cmdoptions.make_option_group(
72
+ cmdoptions.general_group,
73
+ self.parser,
74
+ )
75
+ self.parser.add_option_group(gen_opts)
76
+
77
+ self.add_options()
78
+
79
+ def add_options(self) -> None:
80
+ pass
81
+
82
+ def handle_pip_version_check(self, options: Values) -> None:
83
+ """
84
+ This is a no-op so that commands by default do not do the pip version
85
+ check.
86
+ """
87
+ # Make sure we do the pip version check if the index_group options
88
+ # are present.
89
+ assert not hasattr(options, "no_index")
90
+
91
+ def run(self, options: Values, args: List[str]) -> int:
92
+ raise NotImplementedError
93
+
94
+ def _run_wrapper(self, level_number: int, options: Values, args: List[str]) -> int:
95
+ def _inner_run() -> int:
96
+ try:
97
+ return self.run(options, args)
98
+ finally:
99
+ self.handle_pip_version_check(options)
100
+
101
+ if options.debug_mode:
102
+ rich_traceback.install(show_locals=True)
103
+ return _inner_run()
104
+
105
+ try:
106
+ status = _inner_run()
107
+ assert isinstance(status, int)
108
+ return status
109
+ except DiagnosticPipError as exc:
110
+ logger.error("%s", exc, extra={"rich": True})
111
+ logger.debug("Exception information:", exc_info=True)
112
+
113
+ return ERROR
114
+ except PreviousBuildDirError as exc:
115
+ logger.critical(str(exc))
116
+ logger.debug("Exception information:", exc_info=True)
117
+
118
+ return PREVIOUS_BUILD_DIR_ERROR
119
+ except (
120
+ InstallationError,
121
+ BadCommand,
122
+ NetworkConnectionError,
123
+ ) as exc:
124
+ logger.critical(str(exc))
125
+ logger.debug("Exception information:", exc_info=True)
126
+
127
+ return ERROR
128
+ except CommandError as exc:
129
+ logger.critical("%s", exc)
130
+ logger.debug("Exception information:", exc_info=True)
131
+
132
+ return ERROR
133
+ except BrokenStdoutLoggingError:
134
+ # Bypass our logger and write any remaining messages to
135
+ # stderr because stdout no longer works.
136
+ print("ERROR: Pipe to stdout was broken", file=sys.stderr)
137
+ if level_number <= logging.DEBUG:
138
+ traceback.print_exc(file=sys.stderr)
139
+
140
+ return ERROR
141
+ except KeyboardInterrupt:
142
+ logger.critical("Operation cancelled by user")
143
+ logger.debug("Exception information:", exc_info=True)
144
+
145
+ return ERROR
146
+ except BaseException:
147
+ logger.critical("Exception:", exc_info=True)
148
+
149
+ return UNKNOWN_ERROR
150
+
151
+ def parse_args(self, args: List[str]) -> Tuple[Values, List[str]]:
152
+ # factored out for testability
153
+ return self.parser.parse_args(args)
154
+
155
+ def main(self, args: List[str]) -> int:
156
+ try:
157
+ with self.main_context():
158
+ return self._main(args)
159
+ finally:
160
+ logging.shutdown()
161
+
162
+ def _main(self, args: List[str]) -> int:
163
+ # We must initialize this before the tempdir manager, otherwise the
164
+ # configuration would not be accessible by the time we clean up the
165
+ # tempdir manager.
166
+ self.tempdir_registry = self.enter_context(tempdir_registry())
167
+ # Intentionally set as early as possible so globally-managed temporary
168
+ # directories are available to the rest of the code.
169
+ self.enter_context(global_tempdir_manager())
170
+
171
+ options, args = self.parse_args(args)
172
+
173
+ # Set verbosity so that it can be used elsewhere.
174
+ self.verbosity = options.verbose - options.quiet
175
+
176
+ reconfigure(no_color=options.no_color)
177
+ level_number = setup_logging(
178
+ verbosity=self.verbosity,
179
+ no_color=options.no_color,
180
+ user_log_file=options.log,
181
+ )
182
+
183
+ always_enabled_features = set(options.features_enabled) & set(
184
+ cmdoptions.ALWAYS_ENABLED_FEATURES
185
+ )
186
+ if always_enabled_features:
187
+ logger.warning(
188
+ "The following features are always enabled: %s. ",
189
+ ", ".join(sorted(always_enabled_features)),
190
+ )
191
+
192
+ # Make sure that the --python argument isn't specified after the
193
+ # subcommand. We can tell, because if --python was specified,
194
+ # we should only reach this point if we're running in the created
195
+ # subprocess, which has the _PIP_RUNNING_IN_SUBPROCESS environment
196
+ # variable set.
197
+ if options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ:
198
+ logger.critical(
199
+ "The --python option must be placed before the pip subcommand name"
200
+ )
201
+ sys.exit(ERROR)
202
+
203
+ # TODO: Try to get these passing down from the command?
204
+ # without resorting to os.environ to hold these.
205
+ # This also affects isolated builds and it should.
206
+
207
+ if options.no_input:
208
+ os.environ["PIP_NO_INPUT"] = "1"
209
+
210
+ if options.exists_action:
211
+ os.environ["PIP_EXISTS_ACTION"] = " ".join(options.exists_action)
212
+
213
+ if options.require_venv and not self.ignore_require_venv:
214
+ # If a venv is required check if it can really be found
215
+ if not running_under_virtualenv():
216
+ logger.critical("Could not find an activated virtualenv (required).")
217
+ sys.exit(VIRTUALENV_NOT_FOUND)
218
+
219
+ if options.cache_dir:
220
+ options.cache_dir = normalize_path(options.cache_dir)
221
+ if not check_path_owner(options.cache_dir):
222
+ logger.warning(
223
+ "The directory '%s' or its parent directory is not owned "
224
+ "or is not writable by the current user. The cache "
225
+ "has been disabled. Check the permissions and owner of "
226
+ "that directory. If executing pip with sudo, you should "
227
+ "use sudo's -H flag.",
228
+ options.cache_dir,
229
+ )
230
+ options.cache_dir = None
231
+
232
+ if options.no_python_version_warning:
233
+ deprecated(
234
+ reason="--no-python-version-warning is deprecated.",
235
+ replacement="to remove the flag as it's a no-op",
236
+ gone_in="25.1",
237
+ issue=13154,
238
+ )
239
+
240
+ return self._run_wrapper(level_number, options, args)
lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py ADDED
@@ -0,0 +1,1075 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ shared options and groups
3
+
4
+ The principle here is to define options once, but *not* instantiate them
5
+ globally. One reason being that options with action='append' can carry state
6
+ between parses. pip parses general options twice internally, and shouldn't
7
+ pass on state. To be consistent, all options will follow this design.
8
+ """
9
+
10
+ # The following comment should be removed at some point in the future.
11
+ # mypy: strict-optional=False
12
+
13
+ import importlib.util
14
+ import logging
15
+ import os
16
+ import textwrap
17
+ from functools import partial
18
+ from optparse import SUPPRESS_HELP, Option, OptionGroup, OptionParser, Values
19
+ from textwrap import dedent
20
+ from typing import Any, Callable, Dict, Optional, Tuple
21
+
22
+ from pip._vendor.packaging.utils import canonicalize_name
23
+
24
+ from pip._internal.cli.parser import ConfigOptionParser
25
+ from pip._internal.exceptions import CommandError
26
+ from pip._internal.locations import USER_CACHE_DIR, get_src_prefix
27
+ from pip._internal.models.format_control import FormatControl
28
+ from pip._internal.models.index import PyPI
29
+ from pip._internal.models.target_python import TargetPython
30
+ from pip._internal.utils.hashes import STRONG_HASHES
31
+ from pip._internal.utils.misc import strtobool
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ def raise_option_error(parser: OptionParser, option: Option, msg: str) -> None:
37
+ """
38
+ Raise an option parsing error using parser.error().
39
+
40
+ Args:
41
+ parser: an OptionParser instance.
42
+ option: an Option instance.
43
+ msg: the error text.
44
+ """
45
+ msg = f"{option} error: {msg}"
46
+ msg = textwrap.fill(" ".join(msg.split()))
47
+ parser.error(msg)
48
+
49
+
50
+ def make_option_group(group: Dict[str, Any], parser: ConfigOptionParser) -> OptionGroup:
51
+ """
52
+ Return an OptionGroup object
53
+ group -- assumed to be dict with 'name' and 'options' keys
54
+ parser -- an optparse Parser
55
+ """
56
+ option_group = OptionGroup(parser, group["name"])
57
+ for option in group["options"]:
58
+ option_group.add_option(option())
59
+ return option_group
60
+
61
+
62
+ def check_dist_restriction(options: Values, check_target: bool = False) -> None:
63
+ """Function for determining if custom platform options are allowed.
64
+
65
+ :param options: The OptionParser options.
66
+ :param check_target: Whether or not to check if --target is being used.
67
+ """
68
+ dist_restriction_set = any(
69
+ [
70
+ options.python_version,
71
+ options.platforms,
72
+ options.abis,
73
+ options.implementation,
74
+ ]
75
+ )
76
+
77
+ binary_only = FormatControl(set(), {":all:"})
78
+ sdist_dependencies_allowed = (
79
+ options.format_control != binary_only and not options.ignore_dependencies
80
+ )
81
+
82
+ # Installations or downloads using dist restrictions must not combine
83
+ # source distributions and dist-specific wheels, as they are not
84
+ # guaranteed to be locally compatible.
85
+ if dist_restriction_set and sdist_dependencies_allowed:
86
+ raise CommandError(
87
+ "When restricting platform and interpreter constraints using "
88
+ "--python-version, --platform, --abi, or --implementation, "
89
+ "either --no-deps must be set, or --only-binary=:all: must be "
90
+ "set and --no-binary must not be set (or must be set to "
91
+ ":none:)."
92
+ )
93
+
94
+ if check_target:
95
+ if not options.dry_run and dist_restriction_set and not options.target_dir:
96
+ raise CommandError(
97
+ "Can not use any platform or abi specific options unless "
98
+ "installing via '--target' or using '--dry-run'"
99
+ )
100
+
101
+
102
+ def _path_option_check(option: Option, opt: str, value: str) -> str:
103
+ return os.path.expanduser(value)
104
+
105
+
106
+ def _package_name_option_check(option: Option, opt: str, value: str) -> str:
107
+ return canonicalize_name(value)
108
+
109
+
110
+ class PipOption(Option):
111
+ TYPES = Option.TYPES + ("path", "package_name")
112
+ TYPE_CHECKER = Option.TYPE_CHECKER.copy()
113
+ TYPE_CHECKER["package_name"] = _package_name_option_check
114
+ TYPE_CHECKER["path"] = _path_option_check
115
+
116
+
117
+ ###########
118
+ # options #
119
+ ###########
120
+
121
+ help_: Callable[..., Option] = partial(
122
+ Option,
123
+ "-h",
124
+ "--help",
125
+ dest="help",
126
+ action="help",
127
+ help="Show help.",
128
+ )
129
+
130
+ debug_mode: Callable[..., Option] = partial(
131
+ Option,
132
+ "--debug",
133
+ dest="debug_mode",
134
+ action="store_true",
135
+ default=False,
136
+ help=(
137
+ "Let unhandled exceptions propagate outside the main subroutine, "
138
+ "instead of logging them to stderr."
139
+ ),
140
+ )
141
+
142
+ isolated_mode: Callable[..., Option] = partial(
143
+ Option,
144
+ "--isolated",
145
+ dest="isolated_mode",
146
+ action="store_true",
147
+ default=False,
148
+ help=(
149
+ "Run pip in an isolated mode, ignoring environment variables and user "
150
+ "configuration."
151
+ ),
152
+ )
153
+
154
+ require_virtualenv: Callable[..., Option] = partial(
155
+ Option,
156
+ "--require-virtualenv",
157
+ "--require-venv",
158
+ dest="require_venv",
159
+ action="store_true",
160
+ default=False,
161
+ help=(
162
+ "Allow pip to only run in a virtual environment; "
163
+ "exit with an error otherwise."
164
+ ),
165
+ )
166
+
167
+ override_externally_managed: Callable[..., Option] = partial(
168
+ Option,
169
+ "--break-system-packages",
170
+ dest="override_externally_managed",
171
+ action="store_true",
172
+ help="Allow pip to modify an EXTERNALLY-MANAGED Python installation",
173
+ )
174
+
175
+ python: Callable[..., Option] = partial(
176
+ Option,
177
+ "--python",
178
+ dest="python",
179
+ help="Run pip with the specified Python interpreter.",
180
+ )
181
+
182
+ verbose: Callable[..., Option] = partial(
183
+ Option,
184
+ "-v",
185
+ "--verbose",
186
+ dest="verbose",
187
+ action="count",
188
+ default=0,
189
+ help="Give more output. Option is additive, and can be used up to 3 times.",
190
+ )
191
+
192
+ no_color: Callable[..., Option] = partial(
193
+ Option,
194
+ "--no-color",
195
+ dest="no_color",
196
+ action="store_true",
197
+ default=False,
198
+ help="Suppress colored output.",
199
+ )
200
+
201
+ version: Callable[..., Option] = partial(
202
+ Option,
203
+ "-V",
204
+ "--version",
205
+ dest="version",
206
+ action="store_true",
207
+ help="Show version and exit.",
208
+ )
209
+
210
+ quiet: Callable[..., Option] = partial(
211
+ Option,
212
+ "-q",
213
+ "--quiet",
214
+ dest="quiet",
215
+ action="count",
216
+ default=0,
217
+ help=(
218
+ "Give less output. Option is additive, and can be used up to 3"
219
+ " times (corresponding to WARNING, ERROR, and CRITICAL logging"
220
+ " levels)."
221
+ ),
222
+ )
223
+
224
+ progress_bar: Callable[..., Option] = partial(
225
+ Option,
226
+ "--progress-bar",
227
+ dest="progress_bar",
228
+ type="choice",
229
+ choices=["on", "off", "raw"],
230
+ default="on",
231
+ help="Specify whether the progress bar should be used [on, off, raw] (default: on)",
232
+ )
233
+
234
+ log: Callable[..., Option] = partial(
235
+ PipOption,
236
+ "--log",
237
+ "--log-file",
238
+ "--local-log",
239
+ dest="log",
240
+ metavar="path",
241
+ type="path",
242
+ help="Path to a verbose appending log.",
243
+ )
244
+
245
+ no_input: Callable[..., Option] = partial(
246
+ Option,
247
+ # Don't ask for input
248
+ "--no-input",
249
+ dest="no_input",
250
+ action="store_true",
251
+ default=False,
252
+ help="Disable prompting for input.",
253
+ )
254
+
255
+ keyring_provider: Callable[..., Option] = partial(
256
+ Option,
257
+ "--keyring-provider",
258
+ dest="keyring_provider",
259
+ choices=["auto", "disabled", "import", "subprocess"],
260
+ default="auto",
261
+ help=(
262
+ "Enable the credential lookup via the keyring library if user input is allowed."
263
+ " Specify which mechanism to use [auto, disabled, import, subprocess]."
264
+ " (default: %default)"
265
+ ),
266
+ )
267
+
268
+ proxy: Callable[..., Option] = partial(
269
+ Option,
270
+ "--proxy",
271
+ dest="proxy",
272
+ type="str",
273
+ default="",
274
+ help="Specify a proxy in the form scheme://[user:passwd@]proxy.server:port.",
275
+ )
276
+
277
+ retries: Callable[..., Option] = partial(
278
+ Option,
279
+ "--retries",
280
+ dest="retries",
281
+ type="int",
282
+ default=5,
283
+ help="Maximum number of retries each connection should attempt "
284
+ "(default %default times).",
285
+ )
286
+
287
+ timeout: Callable[..., Option] = partial(
288
+ Option,
289
+ "--timeout",
290
+ "--default-timeout",
291
+ metavar="sec",
292
+ dest="timeout",
293
+ type="float",
294
+ default=15,
295
+ help="Set the socket timeout (default %default seconds).",
296
+ )
297
+
298
+
299
+ def exists_action() -> Option:
300
+ return Option(
301
+ # Option when path already exist
302
+ "--exists-action",
303
+ dest="exists_action",
304
+ type="choice",
305
+ choices=["s", "i", "w", "b", "a"],
306
+ default=[],
307
+ action="append",
308
+ metavar="action",
309
+ help="Default action when a path already exists: "
310
+ "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.",
311
+ )
312
+
313
+
314
+ cert: Callable[..., Option] = partial(
315
+ PipOption,
316
+ "--cert",
317
+ dest="cert",
318
+ type="path",
319
+ metavar="path",
320
+ help=(
321
+ "Path to PEM-encoded CA certificate bundle. "
322
+ "If provided, overrides the default. "
323
+ "See 'SSL Certificate Verification' in pip documentation "
324
+ "for more information."
325
+ ),
326
+ )
327
+
328
+ client_cert: Callable[..., Option] = partial(
329
+ PipOption,
330
+ "--client-cert",
331
+ dest="client_cert",
332
+ type="path",
333
+ default=None,
334
+ metavar="path",
335
+ help="Path to SSL client certificate, a single file containing the "
336
+ "private key and the certificate in PEM format.",
337
+ )
338
+
339
+ index_url: Callable[..., Option] = partial(
340
+ Option,
341
+ "-i",
342
+ "--index-url",
343
+ "--pypi-url",
344
+ dest="index_url",
345
+ metavar="URL",
346
+ default=PyPI.simple_url,
347
+ help="Base URL of the Python Package Index (default %default). "
348
+ "This should point to a repository compliant with PEP 503 "
349
+ "(the simple repository API) or a local directory laid out "
350
+ "in the same format.",
351
+ )
352
+
353
+
354
+ def extra_index_url() -> Option:
355
+ return Option(
356
+ "--extra-index-url",
357
+ dest="extra_index_urls",
358
+ metavar="URL",
359
+ action="append",
360
+ default=[],
361
+ help="Extra URLs of package indexes to use in addition to "
362
+ "--index-url. Should follow the same rules as "
363
+ "--index-url.",
364
+ )
365
+
366
+
367
+ no_index: Callable[..., Option] = partial(
368
+ Option,
369
+ "--no-index",
370
+ dest="no_index",
371
+ action="store_true",
372
+ default=False,
373
+ help="Ignore package index (only looking at --find-links URLs instead).",
374
+ )
375
+
376
+
377
+ def find_links() -> Option:
378
+ return Option(
379
+ "-f",
380
+ "--find-links",
381
+ dest="find_links",
382
+ action="append",
383
+ default=[],
384
+ metavar="url",
385
+ help="If a URL or path to an html file, then parse for links to "
386
+ "archives such as sdist (.tar.gz) or wheel (.whl) files. "
387
+ "If a local path or file:// URL that's a directory, "
388
+ "then look for archives in the directory listing. "
389
+ "Links to VCS project URLs are not supported.",
390
+ )
391
+
392
+
393
+ def trusted_host() -> Option:
394
+ return Option(
395
+ "--trusted-host",
396
+ dest="trusted_hosts",
397
+ action="append",
398
+ metavar="HOSTNAME",
399
+ default=[],
400
+ help="Mark this host or host:port pair as trusted, even though it "
401
+ "does not have valid or any HTTPS.",
402
+ )
403
+
404
+
405
+ def constraints() -> Option:
406
+ return Option(
407
+ "-c",
408
+ "--constraint",
409
+ dest="constraints",
410
+ action="append",
411
+ default=[],
412
+ metavar="file",
413
+ help="Constrain versions using the given constraints file. "
414
+ "This option can be used multiple times.",
415
+ )
416
+
417
+
418
+ def requirements() -> Option:
419
+ return Option(
420
+ "-r",
421
+ "--requirement",
422
+ dest="requirements",
423
+ action="append",
424
+ default=[],
425
+ metavar="file",
426
+ help="Install from the given requirements file. "
427
+ "This option can be used multiple times.",
428
+ )
429
+
430
+
431
+ def editable() -> Option:
432
+ return Option(
433
+ "-e",
434
+ "--editable",
435
+ dest="editables",
436
+ action="append",
437
+ default=[],
438
+ metavar="path/url",
439
+ help=(
440
+ "Install a project in editable mode (i.e. setuptools "
441
+ '"develop mode") from a local project path or a VCS url.'
442
+ ),
443
+ )
444
+
445
+
446
+ def _handle_src(option: Option, opt_str: str, value: str, parser: OptionParser) -> None:
447
+ value = os.path.abspath(value)
448
+ setattr(parser.values, option.dest, value)
449
+
450
+
451
+ src: Callable[..., Option] = partial(
452
+ PipOption,
453
+ "--src",
454
+ "--source",
455
+ "--source-dir",
456
+ "--source-directory",
457
+ dest="src_dir",
458
+ type="path",
459
+ metavar="dir",
460
+ default=get_src_prefix(),
461
+ action="callback",
462
+ callback=_handle_src,
463
+ help="Directory to check out editable projects into. "
464
+ 'The default in a virtualenv is "<venv path>/src". '
465
+ 'The default for global installs is "<current dir>/src".',
466
+ )
467
+
468
+
469
+ def _get_format_control(values: Values, option: Option) -> Any:
470
+ """Get a format_control object."""
471
+ return getattr(values, option.dest)
472
+
473
+
474
+ def _handle_no_binary(
475
+ option: Option, opt_str: str, value: str, parser: OptionParser
476
+ ) -> None:
477
+ existing = _get_format_control(parser.values, option)
478
+ FormatControl.handle_mutual_excludes(
479
+ value,
480
+ existing.no_binary,
481
+ existing.only_binary,
482
+ )
483
+
484
+
485
+ def _handle_only_binary(
486
+ option: Option, opt_str: str, value: str, parser: OptionParser
487
+ ) -> None:
488
+ existing = _get_format_control(parser.values, option)
489
+ FormatControl.handle_mutual_excludes(
490
+ value,
491
+ existing.only_binary,
492
+ existing.no_binary,
493
+ )
494
+
495
+
496
+ def no_binary() -> Option:
497
+ format_control = FormatControl(set(), set())
498
+ return Option(
499
+ "--no-binary",
500
+ dest="format_control",
501
+ action="callback",
502
+ callback=_handle_no_binary,
503
+ type="str",
504
+ default=format_control,
505
+ help="Do not use binary packages. Can be supplied multiple times, and "
506
+ 'each time adds to the existing value. Accepts either ":all:" to '
507
+ 'disable all binary packages, ":none:" to empty the set (notice '
508
+ "the colons), or one or more package names with commas between "
509
+ "them (no colons). Note that some packages are tricky to compile "
510
+ "and may fail to install when this option is used on them.",
511
+ )
512
+
513
+
514
+ def only_binary() -> Option:
515
+ format_control = FormatControl(set(), set())
516
+ return Option(
517
+ "--only-binary",
518
+ dest="format_control",
519
+ action="callback",
520
+ callback=_handle_only_binary,
521
+ type="str",
522
+ default=format_control,
523
+ help="Do not use source packages. Can be supplied multiple times, and "
524
+ 'each time adds to the existing value. Accepts either ":all:" to '
525
+ 'disable all source packages, ":none:" to empty the set, or one '
526
+ "or more package names with commas between them. Packages "
527
+ "without binary distributions will fail to install when this "
528
+ "option is used on them.",
529
+ )
530
+
531
+
532
+ platforms: Callable[..., Option] = partial(
533
+ Option,
534
+ "--platform",
535
+ dest="platforms",
536
+ metavar="platform",
537
+ action="append",
538
+ default=None,
539
+ help=(
540
+ "Only use wheels compatible with <platform>. Defaults to the "
541
+ "platform of the running system. Use this option multiple times to "
542
+ "specify multiple platforms supported by the target interpreter."
543
+ ),
544
+ )
545
+
546
+
547
+ # This was made a separate function for unit-testing purposes.
548
+ def _convert_python_version(value: str) -> Tuple[Tuple[int, ...], Optional[str]]:
549
+ """
550
+ Convert a version string like "3", "37", or "3.7.3" into a tuple of ints.
551
+
552
+ :return: A 2-tuple (version_info, error_msg), where `error_msg` is
553
+ non-None if and only if there was a parsing error.
554
+ """
555
+ if not value:
556
+ # The empty string is the same as not providing a value.
557
+ return (None, None)
558
+
559
+ parts = value.split(".")
560
+ if len(parts) > 3:
561
+ return ((), "at most three version parts are allowed")
562
+
563
+ if len(parts) == 1:
564
+ # Then we are in the case of "3" or "37".
565
+ value = parts[0]
566
+ if len(value) > 1:
567
+ parts = [value[0], value[1:]]
568
+
569
+ try:
570
+ version_info = tuple(int(part) for part in parts)
571
+ except ValueError:
572
+ return ((), "each version part must be an integer")
573
+
574
+ return (version_info, None)
575
+
576
+
577
+ def _handle_python_version(
578
+ option: Option, opt_str: str, value: str, parser: OptionParser
579
+ ) -> None:
580
+ """
581
+ Handle a provided --python-version value.
582
+ """
583
+ version_info, error_msg = _convert_python_version(value)
584
+ if error_msg is not None:
585
+ msg = f"invalid --python-version value: {value!r}: {error_msg}"
586
+ raise_option_error(parser, option=option, msg=msg)
587
+
588
+ parser.values.python_version = version_info
589
+
590
+
591
+ python_version: Callable[..., Option] = partial(
592
+ Option,
593
+ "--python-version",
594
+ dest="python_version",
595
+ metavar="python_version",
596
+ action="callback",
597
+ callback=_handle_python_version,
598
+ type="str",
599
+ default=None,
600
+ help=dedent(
601
+ """\
602
+ The Python interpreter version to use for wheel and "Requires-Python"
603
+ compatibility checks. Defaults to a version derived from the running
604
+ interpreter. The version can be specified using up to three dot-separated
605
+ integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor
606
+ version can also be given as a string without dots (e.g. "37" for 3.7.0).
607
+ """
608
+ ),
609
+ )
610
+
611
+
612
+ implementation: Callable[..., Option] = partial(
613
+ Option,
614
+ "--implementation",
615
+ dest="implementation",
616
+ metavar="implementation",
617
+ default=None,
618
+ help=(
619
+ "Only use wheels compatible with Python "
620
+ "implementation <implementation>, e.g. 'pp', 'jy', 'cp', "
621
+ " or 'ip'. If not specified, then the current "
622
+ "interpreter implementation is used. Use 'py' to force "
623
+ "implementation-agnostic wheels."
624
+ ),
625
+ )
626
+
627
+
628
+ abis: Callable[..., Option] = partial(
629
+ Option,
630
+ "--abi",
631
+ dest="abis",
632
+ metavar="abi",
633
+ action="append",
634
+ default=None,
635
+ help=(
636
+ "Only use wheels compatible with Python abi <abi>, e.g. 'pypy_41'. "
637
+ "If not specified, then the current interpreter abi tag is used. "
638
+ "Use this option multiple times to specify multiple abis supported "
639
+ "by the target interpreter. Generally you will need to specify "
640
+ "--implementation, --platform, and --python-version when using this "
641
+ "option."
642
+ ),
643
+ )
644
+
645
+
646
+ def add_target_python_options(cmd_opts: OptionGroup) -> None:
647
+ cmd_opts.add_option(platforms())
648
+ cmd_opts.add_option(python_version())
649
+ cmd_opts.add_option(implementation())
650
+ cmd_opts.add_option(abis())
651
+
652
+
653
+ def make_target_python(options: Values) -> TargetPython:
654
+ target_python = TargetPython(
655
+ platforms=options.platforms,
656
+ py_version_info=options.python_version,
657
+ abis=options.abis,
658
+ implementation=options.implementation,
659
+ )
660
+
661
+ return target_python
662
+
663
+
664
+ def prefer_binary() -> Option:
665
+ return Option(
666
+ "--prefer-binary",
667
+ dest="prefer_binary",
668
+ action="store_true",
669
+ default=False,
670
+ help=(
671
+ "Prefer binary packages over source packages, even if the "
672
+ "source packages are newer."
673
+ ),
674
+ )
675
+
676
+
677
+ cache_dir: Callable[..., Option] = partial(
678
+ PipOption,
679
+ "--cache-dir",
680
+ dest="cache_dir",
681
+ default=USER_CACHE_DIR,
682
+ metavar="dir",
683
+ type="path",
684
+ help="Store the cache data in <dir>.",
685
+ )
686
+
687
+
688
+ def _handle_no_cache_dir(
689
+ option: Option, opt: str, value: str, parser: OptionParser
690
+ ) -> None:
691
+ """
692
+ Process a value provided for the --no-cache-dir option.
693
+
694
+ This is an optparse.Option callback for the --no-cache-dir option.
695
+ """
696
+ # The value argument will be None if --no-cache-dir is passed via the
697
+ # command-line, since the option doesn't accept arguments. However,
698
+ # the value can be non-None if the option is triggered e.g. by an
699
+ # environment variable, like PIP_NO_CACHE_DIR=true.
700
+ if value is not None:
701
+ # Then parse the string value to get argument error-checking.
702
+ try:
703
+ strtobool(value)
704
+ except ValueError as exc:
705
+ raise_option_error(parser, option=option, msg=str(exc))
706
+
707
+ # Originally, setting PIP_NO_CACHE_DIR to a value that strtobool()
708
+ # converted to 0 (like "false" or "no") caused cache_dir to be disabled
709
+ # rather than enabled (logic would say the latter). Thus, we disable
710
+ # the cache directory not just on values that parse to True, but (for
711
+ # backwards compatibility reasons) also on values that parse to False.
712
+ # In other words, always set it to False if the option is provided in
713
+ # some (valid) form.
714
+ parser.values.cache_dir = False
715
+
716
+
717
+ no_cache: Callable[..., Option] = partial(
718
+ Option,
719
+ "--no-cache-dir",
720
+ dest="cache_dir",
721
+ action="callback",
722
+ callback=_handle_no_cache_dir,
723
+ help="Disable the cache.",
724
+ )
725
+
726
+ no_deps: Callable[..., Option] = partial(
727
+ Option,
728
+ "--no-deps",
729
+ "--no-dependencies",
730
+ dest="ignore_dependencies",
731
+ action="store_true",
732
+ default=False,
733
+ help="Don't install package dependencies.",
734
+ )
735
+
736
+ ignore_requires_python: Callable[..., Option] = partial(
737
+ Option,
738
+ "--ignore-requires-python",
739
+ dest="ignore_requires_python",
740
+ action="store_true",
741
+ help="Ignore the Requires-Python information.",
742
+ )
743
+
744
+ no_build_isolation: Callable[..., Option] = partial(
745
+ Option,
746
+ "--no-build-isolation",
747
+ dest="build_isolation",
748
+ action="store_false",
749
+ default=True,
750
+ help="Disable isolation when building a modern source distribution. "
751
+ "Build dependencies specified by PEP 518 must be already installed "
752
+ "if this option is used.",
753
+ )
754
+
755
+ check_build_deps: Callable[..., Option] = partial(
756
+ Option,
757
+ "--check-build-dependencies",
758
+ dest="check_build_deps",
759
+ action="store_true",
760
+ default=False,
761
+ help="Check the build dependencies when PEP517 is used.",
762
+ )
763
+
764
+
765
+ def _handle_no_use_pep517(
766
+ option: Option, opt: str, value: str, parser: OptionParser
767
+ ) -> None:
768
+ """
769
+ Process a value provided for the --no-use-pep517 option.
770
+
771
+ This is an optparse.Option callback for the no_use_pep517 option.
772
+ """
773
+ # Since --no-use-pep517 doesn't accept arguments, the value argument
774
+ # will be None if --no-use-pep517 is passed via the command-line.
775
+ # However, the value can be non-None if the option is triggered e.g.
776
+ # by an environment variable, for example "PIP_NO_USE_PEP517=true".
777
+ if value is not None:
778
+ msg = """A value was passed for --no-use-pep517,
779
+ probably using either the PIP_NO_USE_PEP517 environment variable
780
+ or the "no-use-pep517" config file option. Use an appropriate value
781
+ of the PIP_USE_PEP517 environment variable or the "use-pep517"
782
+ config file option instead.
783
+ """
784
+ raise_option_error(parser, option=option, msg=msg)
785
+
786
+ # If user doesn't wish to use pep517, we check if setuptools and wheel are installed
787
+ # and raise error if it is not.
788
+ packages = ("setuptools", "wheel")
789
+ if not all(importlib.util.find_spec(package) for package in packages):
790
+ msg = (
791
+ f"It is not possible to use --no-use-pep517 "
792
+ f"without {' and '.join(packages)} installed."
793
+ )
794
+ raise_option_error(parser, option=option, msg=msg)
795
+
796
+ # Otherwise, --no-use-pep517 was passed via the command-line.
797
+ parser.values.use_pep517 = False
798
+
799
+
800
+ use_pep517: Any = partial(
801
+ Option,
802
+ "--use-pep517",
803
+ dest="use_pep517",
804
+ action="store_true",
805
+ default=None,
806
+ help="Use PEP 517 for building source distributions "
807
+ "(use --no-use-pep517 to force legacy behaviour).",
808
+ )
809
+
810
+ no_use_pep517: Any = partial(
811
+ Option,
812
+ "--no-use-pep517",
813
+ dest="use_pep517",
814
+ action="callback",
815
+ callback=_handle_no_use_pep517,
816
+ default=None,
817
+ help=SUPPRESS_HELP,
818
+ )
819
+
820
+
821
+ def _handle_config_settings(
822
+ option: Option, opt_str: str, value: str, parser: OptionParser
823
+ ) -> None:
824
+ key, sep, val = value.partition("=")
825
+ if sep != "=":
826
+ parser.error(f"Arguments to {opt_str} must be of the form KEY=VAL")
827
+ dest = getattr(parser.values, option.dest)
828
+ if dest is None:
829
+ dest = {}
830
+ setattr(parser.values, option.dest, dest)
831
+ if key in dest:
832
+ if isinstance(dest[key], list):
833
+ dest[key].append(val)
834
+ else:
835
+ dest[key] = [dest[key], val]
836
+ else:
837
+ dest[key] = val
838
+
839
+
840
+ config_settings: Callable[..., Option] = partial(
841
+ Option,
842
+ "-C",
843
+ "--config-settings",
844
+ dest="config_settings",
845
+ type=str,
846
+ action="callback",
847
+ callback=_handle_config_settings,
848
+ metavar="settings",
849
+ help="Configuration settings to be passed to the PEP 517 build backend. "
850
+ "Settings take the form KEY=VALUE. Use multiple --config-settings options "
851
+ "to pass multiple keys to the backend.",
852
+ )
853
+
854
+ build_options: Callable[..., Option] = partial(
855
+ Option,
856
+ "--build-option",
857
+ dest="build_options",
858
+ metavar="options",
859
+ action="append",
860
+ help="Extra arguments to be supplied to 'setup.py bdist_wheel'.",
861
+ )
862
+
863
+ global_options: Callable[..., Option] = partial(
864
+ Option,
865
+ "--global-option",
866
+ dest="global_options",
867
+ action="append",
868
+ metavar="options",
869
+ help="Extra global options to be supplied to the setup.py "
870
+ "call before the install or bdist_wheel command.",
871
+ )
872
+
873
+ no_clean: Callable[..., Option] = partial(
874
+ Option,
875
+ "--no-clean",
876
+ action="store_true",
877
+ default=False,
878
+ help="Don't clean up build directories.",
879
+ )
880
+
881
+ pre: Callable[..., Option] = partial(
882
+ Option,
883
+ "--pre",
884
+ action="store_true",
885
+ default=False,
886
+ help="Include pre-release and development versions. By default, "
887
+ "pip only finds stable versions.",
888
+ )
889
+
890
+ disable_pip_version_check: Callable[..., Option] = partial(
891
+ Option,
892
+ "--disable-pip-version-check",
893
+ dest="disable_pip_version_check",
894
+ action="store_true",
895
+ default=False,
896
+ help="Don't periodically check PyPI to determine whether a new version "
897
+ "of pip is available for download. Implied with --no-index.",
898
+ )
899
+
900
+ root_user_action: Callable[..., Option] = partial(
901
+ Option,
902
+ "--root-user-action",
903
+ dest="root_user_action",
904
+ default="warn",
905
+ choices=["warn", "ignore"],
906
+ help="Action if pip is run as a root user [warn, ignore] (default: warn)",
907
+ )
908
+
909
+
910
+ def _handle_merge_hash(
911
+ option: Option, opt_str: str, value: str, parser: OptionParser
912
+ ) -> None:
913
+ """Given a value spelled "algo:digest", append the digest to a list
914
+ pointed to in a dict by the algo name."""
915
+ if not parser.values.hashes:
916
+ parser.values.hashes = {}
917
+ try:
918
+ algo, digest = value.split(":", 1)
919
+ except ValueError:
920
+ parser.error(
921
+ f"Arguments to {opt_str} must be a hash name "
922
+ "followed by a value, like --hash=sha256:"
923
+ "abcde..."
924
+ )
925
+ if algo not in STRONG_HASHES:
926
+ parser.error(
927
+ "Allowed hash algorithms for {} are {}.".format(
928
+ opt_str, ", ".join(STRONG_HASHES)
929
+ )
930
+ )
931
+ parser.values.hashes.setdefault(algo, []).append(digest)
932
+
933
+
934
+ hash: Callable[..., Option] = partial(
935
+ Option,
936
+ "--hash",
937
+ # Hash values eventually end up in InstallRequirement.hashes due to
938
+ # __dict__ copying in process_line().
939
+ dest="hashes",
940
+ action="callback",
941
+ callback=_handle_merge_hash,
942
+ type="string",
943
+ help="Verify that the package's archive matches this "
944
+ "hash before installing. Example: --hash=sha256:abcdef...",
945
+ )
946
+
947
+
948
+ require_hashes: Callable[..., Option] = partial(
949
+ Option,
950
+ "--require-hashes",
951
+ dest="require_hashes",
952
+ action="store_true",
953
+ default=False,
954
+ help="Require a hash to check each requirement against, for "
955
+ "repeatable installs. This option is implied when any package in a "
956
+ "requirements file has a --hash option.",
957
+ )
958
+
959
+
960
+ list_path: Callable[..., Option] = partial(
961
+ PipOption,
962
+ "--path",
963
+ dest="path",
964
+ type="path",
965
+ action="append",
966
+ help="Restrict to the specified installation path for listing "
967
+ "packages (can be used multiple times).",
968
+ )
969
+
970
+
971
+ def check_list_path_option(options: Values) -> None:
972
+ if options.path and (options.user or options.local):
973
+ raise CommandError("Cannot combine '--path' with '--user' or '--local'")
974
+
975
+
976
+ list_exclude: Callable[..., Option] = partial(
977
+ PipOption,
978
+ "--exclude",
979
+ dest="excludes",
980
+ action="append",
981
+ metavar="package",
982
+ type="package_name",
983
+ help="Exclude specified package from the output",
984
+ )
985
+
986
+
987
+ no_python_version_warning: Callable[..., Option] = partial(
988
+ Option,
989
+ "--no-python-version-warning",
990
+ dest="no_python_version_warning",
991
+ action="store_true",
992
+ default=False,
993
+ help="Silence deprecation warnings for upcoming unsupported Pythons.",
994
+ )
995
+
996
+
997
+ # Features that are now always on. A warning is printed if they are used.
998
+ ALWAYS_ENABLED_FEATURES = [
999
+ "truststore", # always on since 24.2
1000
+ "no-binary-enable-wheel-cache", # always on since 23.1
1001
+ ]
1002
+
1003
+ use_new_feature: Callable[..., Option] = partial(
1004
+ Option,
1005
+ "--use-feature",
1006
+ dest="features_enabled",
1007
+ metavar="feature",
1008
+ action="append",
1009
+ default=[],
1010
+ choices=[
1011
+ "fast-deps",
1012
+ ]
1013
+ + ALWAYS_ENABLED_FEATURES,
1014
+ help="Enable new functionality, that may be backward incompatible.",
1015
+ )
1016
+
1017
+ use_deprecated_feature: Callable[..., Option] = partial(
1018
+ Option,
1019
+ "--use-deprecated",
1020
+ dest="deprecated_features_enabled",
1021
+ metavar="feature",
1022
+ action="append",
1023
+ default=[],
1024
+ choices=[
1025
+ "legacy-resolver",
1026
+ "legacy-certs",
1027
+ ],
1028
+ help=("Enable deprecated functionality, that will be removed in the future."),
1029
+ )
1030
+
1031
+
1032
+ ##########
1033
+ # groups #
1034
+ ##########
1035
+
1036
+ general_group: Dict[str, Any] = {
1037
+ "name": "General Options",
1038
+ "options": [
1039
+ help_,
1040
+ debug_mode,
1041
+ isolated_mode,
1042
+ require_virtualenv,
1043
+ python,
1044
+ verbose,
1045
+ version,
1046
+ quiet,
1047
+ log,
1048
+ no_input,
1049
+ keyring_provider,
1050
+ proxy,
1051
+ retries,
1052
+ timeout,
1053
+ exists_action,
1054
+ trusted_host,
1055
+ cert,
1056
+ client_cert,
1057
+ cache_dir,
1058
+ no_cache,
1059
+ disable_pip_version_check,
1060
+ no_color,
1061
+ no_python_version_warning,
1062
+ use_new_feature,
1063
+ use_deprecated_feature,
1064
+ ],
1065
+ }
1066
+
1067
+ index_group: Dict[str, Any] = {
1068
+ "name": "Package Index Options",
1069
+ "options": [
1070
+ index_url,
1071
+ extra_index_url,
1072
+ no_index,
1073
+ find_links,
1074
+ ],
1075
+ }
lib/python3.12/site-packages/pip/_internal/cli/command_context.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from contextlib import ExitStack, contextmanager
2
+ from typing import ContextManager, Generator, TypeVar
3
+
4
+ _T = TypeVar("_T", covariant=True)
5
+
6
+
7
+ class CommandContextMixIn:
8
+ def __init__(self) -> None:
9
+ super().__init__()
10
+ self._in_main_context = False
11
+ self._main_context = ExitStack()
12
+
13
+ @contextmanager
14
+ def main_context(self) -> Generator[None, None, None]:
15
+ assert not self._in_main_context
16
+
17
+ self._in_main_context = True
18
+ try:
19
+ with self._main_context:
20
+ yield
21
+ finally:
22
+ self._in_main_context = False
23
+
24
+ def enter_context(self, context_provider: ContextManager[_T]) -> _T:
25
+ assert self._in_main_context
26
+
27
+ return self._main_context.enter_context(context_provider)
lib/python3.12/site-packages/pip/_internal/cli/index_command.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Contains command classes which may interact with an index / the network.
3
+
4
+ Unlike its sister module, req_command, this module still uses lazy imports
5
+ so commands which don't always hit the network (e.g. list w/o --outdated or
6
+ --uptodate) don't need waste time importing PipSession and friends.
7
+ """
8
+
9
+ import logging
10
+ import os
11
+ import sys
12
+ from optparse import Values
13
+ from typing import TYPE_CHECKING, List, Optional
14
+
15
+ from pip._vendor import certifi
16
+
17
+ from pip._internal.cli.base_command import Command
18
+ from pip._internal.cli.command_context import CommandContextMixIn
19
+
20
+ if TYPE_CHECKING:
21
+ from ssl import SSLContext
22
+
23
+ from pip._internal.network.session import PipSession
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ def _create_truststore_ssl_context() -> Optional["SSLContext"]:
29
+ if sys.version_info < (3, 10):
30
+ logger.debug("Disabling truststore because Python version isn't 3.10+")
31
+ return None
32
+
33
+ try:
34
+ import ssl
35
+ except ImportError:
36
+ logger.warning("Disabling truststore since ssl support is missing")
37
+ return None
38
+
39
+ try:
40
+ from pip._vendor import truststore
41
+ except ImportError:
42
+ logger.warning("Disabling truststore because platform isn't supported")
43
+ return None
44
+
45
+ ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
46
+ ctx.load_verify_locations(certifi.where())
47
+ return ctx
48
+
49
+
50
+ class SessionCommandMixin(CommandContextMixIn):
51
+ """
52
+ A class mixin for command classes needing _build_session().
53
+ """
54
+
55
+ def __init__(self) -> None:
56
+ super().__init__()
57
+ self._session: Optional[PipSession] = None
58
+
59
+ @classmethod
60
+ def _get_index_urls(cls, options: Values) -> Optional[List[str]]:
61
+ """Return a list of index urls from user-provided options."""
62
+ index_urls = []
63
+ if not getattr(options, "no_index", False):
64
+ url = getattr(options, "index_url", None)
65
+ if url:
66
+ index_urls.append(url)
67
+ urls = getattr(options, "extra_index_urls", None)
68
+ if urls:
69
+ index_urls.extend(urls)
70
+ # Return None rather than an empty list
71
+ return index_urls or None
72
+
73
+ def get_default_session(self, options: Values) -> "PipSession":
74
+ """Get a default-managed session."""
75
+ if self._session is None:
76
+ self._session = self.enter_context(self._build_session(options))
77
+ # there's no type annotation on requests.Session, so it's
78
+ # automatically ContextManager[Any] and self._session becomes Any,
79
+ # then https://github.com/python/mypy/issues/7696 kicks in
80
+ assert self._session is not None
81
+ return self._session
82
+
83
+ def _build_session(
84
+ self,
85
+ options: Values,
86
+ retries: Optional[int] = None,
87
+ timeout: Optional[int] = None,
88
+ ) -> "PipSession":
89
+ from pip._internal.network.session import PipSession
90
+
91
+ cache_dir = options.cache_dir
92
+ assert not cache_dir or os.path.isabs(cache_dir)
93
+
94
+ if "legacy-certs" not in options.deprecated_features_enabled:
95
+ ssl_context = _create_truststore_ssl_context()
96
+ else:
97
+ ssl_context = None
98
+
99
+ session = PipSession(
100
+ cache=os.path.join(cache_dir, "http-v2") if cache_dir else None,
101
+ retries=retries if retries is not None else options.retries,
102
+ trusted_hosts=options.trusted_hosts,
103
+ index_urls=self._get_index_urls(options),
104
+ ssl_context=ssl_context,
105
+ )
106
+
107
+ # Handle custom ca-bundles from the user
108
+ if options.cert:
109
+ session.verify = options.cert
110
+
111
+ # Handle SSL client certificate
112
+ if options.client_cert:
113
+ session.cert = options.client_cert
114
+
115
+ # Handle timeouts
116
+ if options.timeout or timeout:
117
+ session.timeout = timeout if timeout is not None else options.timeout
118
+
119
+ # Handle configured proxies
120
+ if options.proxy:
121
+ session.proxies = {
122
+ "http": options.proxy,
123
+ "https": options.proxy,
124
+ }
125
+ session.trust_env = False
126
+ session.pip_proxy = options.proxy
127
+
128
+ # Determine if we can prompt the user for authentication or not
129
+ session.auth.prompting = not options.no_input
130
+ session.auth.keyring_provider = options.keyring_provider
131
+
132
+ return session
133
+
134
+
135
+ def _pip_self_version_check(session: "PipSession", options: Values) -> None:
136
+ from pip._internal.self_outdated_check import pip_self_version_check as check
137
+
138
+ check(session, options)
139
+
140
+
141
+ class IndexGroupCommand(Command, SessionCommandMixin):
142
+ """
143
+ Abstract base class for commands with the index_group options.
144
+
145
+ This also corresponds to the commands that permit the pip version check.
146
+ """
147
+
148
+ def handle_pip_version_check(self, options: Values) -> None:
149
+ """
150
+ Do the pip version check if not disabled.
151
+
152
+ This overrides the default behavior of not doing the check.
153
+ """
154
+ # Make sure the index_group options are present.
155
+ assert hasattr(options, "no_index")
156
+
157
+ if options.disable_pip_version_check or options.no_index:
158
+ return
159
+
160
+ try:
161
+ # Otherwise, check if we're using the latest version of pip available.
162
+ session = self._build_session(
163
+ options,
164
+ retries=0,
165
+ timeout=min(5, options.timeout),
166
+ )
167
+ with session:
168
+ _pip_self_version_check(session, options)
169
+ except Exception:
170
+ logger.warning("There was an error checking the latest version of pip.")
171
+ logger.debug("See below for error", exc_info=True)
lib/python3.12/site-packages/pip/_internal/cli/main.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Primary application entrypoint.
2
+ """
3
+
4
+ import locale
5
+ import logging
6
+ import os
7
+ import sys
8
+ import warnings
9
+ from typing import List, Optional
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
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ # Do not import and use main() directly! Using it directly is actively
21
+ # discouraged by pip's maintainers. The name, location and behavior of
22
+ # this function is subject to change, so calling it directly is not
23
+ # portable across different pip versions.
24
+
25
+ # In addition, running pip in-process is unsupported and unsafe. This is
26
+ # elaborated in detail at
27
+ # https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program.
28
+ # That document also provides suggestions that should work for nearly
29
+ # all users that are considering importing and using main() directly.
30
+
31
+ # However, we know that certain users will still want to invoke pip
32
+ # in-process. If you understand and accept the implications of using pip
33
+ # in an unsupported manner, the best approach is to use runpy to avoid
34
+ # depending on the exact location of this entry point.
35
+
36
+ # The following example shows how to use runpy to invoke pip in that
37
+ # case:
38
+ #
39
+ # sys.argv = ["pip", your, args, here]
40
+ # runpy.run_module("pip", run_name="__main__")
41
+ #
42
+ # Note that this will exit the process after running, unlike a direct
43
+ # call to main. As it is not safe to do any processing after calling
44
+ # main, this should not be an issue in practice.
45
+
46
+
47
+ def main(args: Optional[List[str]] = None) -> int:
48
+ if args is None:
49
+ args = sys.argv[1:]
50
+
51
+ # Suppress the pkg_resources deprecation warning
52
+ # Note - we use a module of .*pkg_resources to cover
53
+ # the normal case (pip._vendor.pkg_resources) and the
54
+ # devendored case (a bare pkg_resources)
55
+ warnings.filterwarnings(
56
+ action="ignore", category=DeprecationWarning, module=".*pkg_resources"
57
+ )
58
+
59
+ # Configure our deprecation warnings to be sent through loggers
60
+ deprecation.install_warning_logger()
61
+
62
+ autocomplete()
63
+
64
+ try:
65
+ cmd_name, cmd_args = parse_command(args)
66
+ except PipError as exc:
67
+ sys.stderr.write(f"ERROR: {exc}")
68
+ sys.stderr.write(os.linesep)
69
+ sys.exit(1)
70
+
71
+ # Needed for locale.getpreferredencoding(False) to work
72
+ # in pip._internal.utils.encoding.auto_decode
73
+ try:
74
+ locale.setlocale(locale.LC_ALL, "")
75
+ except locale.Error as e:
76
+ # setlocale can apparently crash if locale are uninitialized
77
+ logger.debug("Ignoring error %s when setting locale", e)
78
+ command = create_command(cmd_name, isolated=("--isolated" in cmd_args))
79
+
80
+ return command.main(cmd_args)
lib/python3.12/site-packages/pip/_internal/cli/main_parser.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """A single place for constructing and exposing the main parser
2
+ """
3
+
4
+ import os
5
+ import subprocess
6
+ import sys
7
+ from typing import List, Optional, Tuple
8
+
9
+ from pip._internal.build_env import get_runnable_pip
10
+ from pip._internal.cli import cmdoptions
11
+ from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
12
+ from pip._internal.commands import commands_dict, get_similar_commands
13
+ from pip._internal.exceptions import CommandError
14
+ from pip._internal.utils.misc import get_pip_version, get_prog
15
+
16
+ __all__ = ["create_main_parser", "parse_command"]
17
+
18
+
19
+ def create_main_parser() -> ConfigOptionParser:
20
+ """Creates and returns the main parser for pip's CLI"""
21
+
22
+ parser = ConfigOptionParser(
23
+ usage="\n%prog <command> [options]",
24
+ add_help_option=False,
25
+ formatter=UpdatingDefaultsHelpFormatter(),
26
+ name="global",
27
+ prog=get_prog(),
28
+ )
29
+ parser.disable_interspersed_args()
30
+
31
+ parser.version = get_pip_version()
32
+
33
+ # add the general options
34
+ gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser)
35
+ parser.add_option_group(gen_opts)
36
+
37
+ # so the help formatter knows
38
+ parser.main = True # type: ignore
39
+
40
+ # create command listing for description
41
+ description = [""] + [
42
+ f"{name:27} {command_info.summary}"
43
+ for name, command_info in commands_dict.items()
44
+ ]
45
+ parser.description = "\n".join(description)
46
+
47
+ return parser
48
+
49
+
50
+ def identify_python_interpreter(python: str) -> Optional[str]:
51
+ # If the named file exists, use it.
52
+ # If it's a directory, assume it's a virtual environment and
53
+ # look for the environment's Python executable.
54
+ if os.path.exists(python):
55
+ if os.path.isdir(python):
56
+ # bin/python for Unix, Scripts/python.exe for Windows
57
+ # Try both in case of odd cases like cygwin.
58
+ for exe in ("bin/python", "Scripts/python.exe"):
59
+ py = os.path.join(python, exe)
60
+ if os.path.exists(py):
61
+ return py
62
+ else:
63
+ return python
64
+
65
+ # Could not find the interpreter specified
66
+ return None
67
+
68
+
69
+ def parse_command(args: List[str]) -> Tuple[str, List[str]]:
70
+ parser = create_main_parser()
71
+
72
+ # Note: parser calls disable_interspersed_args(), so the result of this
73
+ # call is to split the initial args into the general options before the
74
+ # subcommand and everything else.
75
+ # For example:
76
+ # args: ['--timeout=5', 'install', '--user', 'INITools']
77
+ # general_options: ['--timeout==5']
78
+ # args_else: ['install', '--user', 'INITools']
79
+ general_options, args_else = parser.parse_args(args)
80
+
81
+ # --python
82
+ if general_options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ:
83
+ # Re-invoke pip using the specified Python interpreter
84
+ interpreter = identify_python_interpreter(general_options.python)
85
+ if interpreter is None:
86
+ raise CommandError(
87
+ f"Could not locate Python interpreter {general_options.python}"
88
+ )
89
+
90
+ pip_cmd = [
91
+ interpreter,
92
+ get_runnable_pip(),
93
+ ]
94
+ pip_cmd.extend(args)
95
+
96
+ # Set a flag so the child doesn't re-invoke itself, causing
97
+ # an infinite loop.
98
+ os.environ["_PIP_RUNNING_IN_SUBPROCESS"] = "1"
99
+ returncode = 0
100
+ try:
101
+ proc = subprocess.run(pip_cmd)
102
+ returncode = proc.returncode
103
+ except (subprocess.SubprocessError, OSError) as exc:
104
+ raise CommandError(f"Failed to run pip under {interpreter}: {exc}")
105
+ sys.exit(returncode)
106
+
107
+ # --version
108
+ if general_options.version:
109
+ sys.stdout.write(parser.version)
110
+ sys.stdout.write(os.linesep)
111
+ sys.exit()
112
+
113
+ # pip || pip help -> print_help()
114
+ if not args_else or (args_else[0] == "help" and len(args_else) == 1):
115
+ parser.print_help()
116
+ sys.exit()
117
+
118
+ # the subcommand name
119
+ cmd_name = args_else[0]
120
+
121
+ if cmd_name not in commands_dict:
122
+ guess = get_similar_commands(cmd_name)
123
+
124
+ msg = [f'unknown command "{cmd_name}"']
125
+ if guess:
126
+ msg.append(f'maybe you meant "{guess}"')
127
+
128
+ raise CommandError(" - ".join(msg))
129
+
130
+ # all the args without the subcommand
131
+ cmd_args = args[:]
132
+ cmd_args.remove(cmd_name)
133
+
134
+ return cmd_name, cmd_args
lib/python3.12/site-packages/pip/_internal/cli/parser.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Base option parser setup"""
2
+
3
+ import logging
4
+ import optparse
5
+ import shutil
6
+ import sys
7
+ import textwrap
8
+ from contextlib import suppress
9
+ from typing import Any, Dict, Generator, List, NoReturn, Optional, Tuple
10
+
11
+ from pip._internal.cli.status_codes import UNKNOWN_ERROR
12
+ from pip._internal.configuration import Configuration, ConfigurationError
13
+ from pip._internal.utils.misc import redact_auth_from_url, strtobool
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
19
+ """A prettier/less verbose help formatter for optparse."""
20
+
21
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
22
+ # help position must be aligned with __init__.parseopts.description
23
+ kwargs["max_help_position"] = 30
24
+ kwargs["indent_increment"] = 1
25
+ kwargs["width"] = shutil.get_terminal_size()[0] - 2
26
+ super().__init__(*args, **kwargs)
27
+
28
+ def format_option_strings(self, option: optparse.Option) -> str:
29
+ return self._format_option_strings(option)
30
+
31
+ def _format_option_strings(
32
+ self, option: optparse.Option, mvarfmt: str = " <{}>", optsep: str = ", "
33
+ ) -> str:
34
+ """
35
+ Return a comma-separated list of option strings and metavars.
36
+
37
+ :param option: tuple of (short opt, long opt), e.g: ('-f', '--format')
38
+ :param mvarfmt: metavar format string
39
+ :param optsep: separator
40
+ """
41
+ opts = []
42
+
43
+ if option._short_opts:
44
+ opts.append(option._short_opts[0])
45
+ if option._long_opts:
46
+ opts.append(option._long_opts[0])
47
+ if len(opts) > 1:
48
+ opts.insert(1, optsep)
49
+
50
+ if option.takes_value():
51
+ assert option.dest is not None
52
+ metavar = option.metavar or option.dest.lower()
53
+ opts.append(mvarfmt.format(metavar.lower()))
54
+
55
+ return "".join(opts)
56
+
57
+ def format_heading(self, heading: str) -> str:
58
+ if heading == "Options":
59
+ return ""
60
+ return heading + ":\n"
61
+
62
+ def format_usage(self, usage: str) -> str:
63
+ """
64
+ Ensure there is only one newline between usage and the first heading
65
+ if there is no description.
66
+ """
67
+ msg = "\nUsage: {}\n".format(self.indent_lines(textwrap.dedent(usage), " "))
68
+ return msg
69
+
70
+ def format_description(self, description: Optional[str]) -> str:
71
+ # leave full control over description to us
72
+ if description:
73
+ if hasattr(self.parser, "main"):
74
+ label = "Commands"
75
+ else:
76
+ label = "Description"
77
+ # some doc strings have initial newlines, some don't
78
+ description = description.lstrip("\n")
79
+ # some doc strings have final newlines and spaces, some don't
80
+ description = description.rstrip()
81
+ # dedent, then reindent
82
+ description = self.indent_lines(textwrap.dedent(description), " ")
83
+ description = f"{label}:\n{description}\n"
84
+ return description
85
+ else:
86
+ return ""
87
+
88
+ def format_epilog(self, epilog: Optional[str]) -> str:
89
+ # leave full control over epilog to us
90
+ if epilog:
91
+ return epilog
92
+ else:
93
+ return ""
94
+
95
+ def indent_lines(self, text: str, indent: str) -> str:
96
+ new_lines = [indent + line for line in text.split("\n")]
97
+ return "\n".join(new_lines)
98
+
99
+
100
+ class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
101
+ """Custom help formatter for use in ConfigOptionParser.
102
+
103
+ This is updates the defaults before expanding them, allowing
104
+ them to show up correctly in the help listing.
105
+
106
+ Also redact auth from url type options
107
+ """
108
+
109
+ def expand_default(self, option: optparse.Option) -> str:
110
+ default_values = None
111
+ if self.parser is not None:
112
+ assert isinstance(self.parser, ConfigOptionParser)
113
+ self.parser._update_defaults(self.parser.defaults)
114
+ assert option.dest is not None
115
+ default_values = self.parser.defaults.get(option.dest)
116
+ help_text = super().expand_default(option)
117
+
118
+ if default_values and option.metavar == "URL":
119
+ if isinstance(default_values, str):
120
+ default_values = [default_values]
121
+
122
+ # If its not a list, we should abort and just return the help text
123
+ if not isinstance(default_values, list):
124
+ default_values = []
125
+
126
+ for val in default_values:
127
+ help_text = help_text.replace(val, redact_auth_from_url(val))
128
+
129
+ return help_text
130
+
131
+
132
+ class CustomOptionParser(optparse.OptionParser):
133
+ def insert_option_group(
134
+ self, idx: int, *args: Any, **kwargs: Any
135
+ ) -> optparse.OptionGroup:
136
+ """Insert an OptionGroup at a given position."""
137
+ group = self.add_option_group(*args, **kwargs)
138
+
139
+ self.option_groups.pop()
140
+ self.option_groups.insert(idx, group)
141
+
142
+ return group
143
+
144
+ @property
145
+ def option_list_all(self) -> List[optparse.Option]:
146
+ """Get a list of all options, including those in option groups."""
147
+ res = self.option_list[:]
148
+ for i in self.option_groups:
149
+ res.extend(i.option_list)
150
+
151
+ return res
152
+
153
+
154
+ class ConfigOptionParser(CustomOptionParser):
155
+ """Custom option parser which updates its defaults by checking the
156
+ configuration files and environmental variables"""
157
+
158
+ def __init__(
159
+ self,
160
+ *args: Any,
161
+ name: str,
162
+ isolated: bool = False,
163
+ **kwargs: Any,
164
+ ) -> None:
165
+ self.name = name
166
+ self.config = Configuration(isolated)
167
+
168
+ assert self.name
169
+ super().__init__(*args, **kwargs)
170
+
171
+ def check_default(self, option: optparse.Option, key: str, val: Any) -> Any:
172
+ try:
173
+ return option.check_value(key, val)
174
+ except optparse.OptionValueError as exc:
175
+ print(f"An error occurred during configuration: {exc}")
176
+ sys.exit(3)
177
+
178
+ def _get_ordered_configuration_items(
179
+ self,
180
+ ) -> Generator[Tuple[str, Any], None, None]:
181
+ # Configuration gives keys in an unordered manner. Order them.
182
+ override_order = ["global", self.name, ":env:"]
183
+
184
+ # Pool the options into different groups
185
+ section_items: Dict[str, List[Tuple[str, Any]]] = {
186
+ name: [] for name in override_order
187
+ }
188
+ for section_key, val in self.config.items():
189
+ # ignore empty values
190
+ if not val:
191
+ logger.debug(
192
+ "Ignoring configuration key '%s' as it's value is empty.",
193
+ section_key,
194
+ )
195
+ continue
196
+
197
+ section, key = section_key.split(".", 1)
198
+ if section in override_order:
199
+ section_items[section].append((key, val))
200
+
201
+ # Yield each group in their override order
202
+ for section in override_order:
203
+ for key, val in section_items[section]:
204
+ yield key, val
205
+
206
+ def _update_defaults(self, defaults: Dict[str, Any]) -> Dict[str, Any]:
207
+ """Updates the given defaults with values from the config files and
208
+ the environ. Does a little special handling for certain types of
209
+ options (lists)."""
210
+
211
+ # Accumulate complex default state.
212
+ self.values = optparse.Values(self.defaults)
213
+ late_eval = set()
214
+ # Then set the options with those values
215
+ for key, val in self._get_ordered_configuration_items():
216
+ # '--' because configuration supports only long names
217
+ option = self.get_option("--" + key)
218
+
219
+ # Ignore options not present in this parser. E.g. non-globals put
220
+ # in [global] by users that want them to apply to all applicable
221
+ # commands.
222
+ if option is None:
223
+ continue
224
+
225
+ assert option.dest is not None
226
+
227
+ if option.action in ("store_true", "store_false"):
228
+ try:
229
+ val = strtobool(val)
230
+ except ValueError:
231
+ self.error(
232
+ f"{val} is not a valid value for {key} option, "
233
+ "please specify a boolean value like yes/no, "
234
+ "true/false or 1/0 instead."
235
+ )
236
+ elif option.action == "count":
237
+ with suppress(ValueError):
238
+ val = strtobool(val)
239
+ with suppress(ValueError):
240
+ val = int(val)
241
+ if not isinstance(val, int) or val < 0:
242
+ self.error(
243
+ f"{val} is not a valid value for {key} option, "
244
+ "please instead specify either a non-negative integer "
245
+ "or a boolean value like yes/no or false/true "
246
+ "which is equivalent to 1/0."
247
+ )
248
+ elif option.action == "append":
249
+ val = val.split()
250
+ val = [self.check_default(option, key, v) for v in val]
251
+ elif option.action == "callback":
252
+ assert option.callback is not None
253
+ late_eval.add(option.dest)
254
+ opt_str = option.get_opt_string()
255
+ val = option.convert_value(opt_str, val)
256
+ # From take_action
257
+ args = option.callback_args or ()
258
+ kwargs = option.callback_kwargs or {}
259
+ option.callback(option, opt_str, val, self, *args, **kwargs)
260
+ else:
261
+ val = self.check_default(option, key, val)
262
+
263
+ defaults[option.dest] = val
264
+
265
+ for key in late_eval:
266
+ defaults[key] = getattr(self.values, key)
267
+ self.values = None
268
+ return defaults
269
+
270
+ def get_default_values(self) -> optparse.Values:
271
+ """Overriding to make updating the defaults after instantiation of
272
+ the option parser possible, _update_defaults() does the dirty work."""
273
+ if not self.process_default_values:
274
+ # Old, pre-Optik 1.5 behaviour.
275
+ return optparse.Values(self.defaults)
276
+
277
+ # Load the configuration, or error out in case of an error
278
+ try:
279
+ self.config.load()
280
+ except ConfigurationError as err:
281
+ self.exit(UNKNOWN_ERROR, str(err))
282
+
283
+ defaults = self._update_defaults(self.defaults.copy()) # ours
284
+ for option in self._get_all_options():
285
+ assert option.dest is not None
286
+ default = defaults.get(option.dest)
287
+ if isinstance(default, str):
288
+ opt_str = option.get_opt_string()
289
+ defaults[option.dest] = option.check_value(opt_str, default)
290
+ return optparse.Values(defaults)
291
+
292
+ def error(self, msg: str) -> NoReturn:
293
+ self.print_usage(sys.stderr)
294
+ self.exit(UNKNOWN_ERROR, f"{msg}\n")
lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import functools
2
+ import sys
3
+ from typing import Callable, Generator, Iterable, Iterator, Optional, Tuple
4
+
5
+ from pip._vendor.rich.progress import (
6
+ BarColumn,
7
+ DownloadColumn,
8
+ FileSizeColumn,
9
+ Progress,
10
+ ProgressColumn,
11
+ SpinnerColumn,
12
+ TextColumn,
13
+ TimeElapsedColumn,
14
+ TimeRemainingColumn,
15
+ TransferSpeedColumn,
16
+ )
17
+
18
+ from pip._internal.cli.spinners import RateLimiter
19
+ from pip._internal.utils.logging import get_indentation
20
+
21
+ DownloadProgressRenderer = Callable[[Iterable[bytes]], Iterator[bytes]]
22
+
23
+
24
+ def _rich_progress_bar(
25
+ iterable: Iterable[bytes],
26
+ *,
27
+ bar_type: str,
28
+ size: Optional[int],
29
+ ) -> Generator[bytes, None, None]:
30
+ assert bar_type == "on", "This should only be used in the default mode."
31
+
32
+ if not size:
33
+ total = float("inf")
34
+ columns: Tuple[ProgressColumn, ...] = (
35
+ TextColumn("[progress.description]{task.description}"),
36
+ SpinnerColumn("line", speed=1.5),
37
+ FileSizeColumn(),
38
+ TransferSpeedColumn(),
39
+ TimeElapsedColumn(),
40
+ )
41
+ else:
42
+ total = size
43
+ columns = (
44
+ TextColumn("[progress.description]{task.description}"),
45
+ BarColumn(),
46
+ DownloadColumn(),
47
+ TransferSpeedColumn(),
48
+ TextColumn("eta"),
49
+ TimeRemainingColumn(),
50
+ )
51
+
52
+ progress = Progress(*columns, refresh_per_second=5)
53
+ task_id = progress.add_task(" " * (get_indentation() + 2), total=total)
54
+ with progress:
55
+ for chunk in iterable:
56
+ yield chunk
57
+ progress.update(task_id, advance=len(chunk))
58
+
59
+
60
+ def _raw_progress_bar(
61
+ iterable: Iterable[bytes],
62
+ *,
63
+ size: Optional[int],
64
+ ) -> Generator[bytes, None, None]:
65
+ def write_progress(current: int, total: int) -> None:
66
+ sys.stdout.write(f"Progress {current} of {total}\n")
67
+ sys.stdout.flush()
68
+
69
+ current = 0
70
+ total = size or 0
71
+ rate_limiter = RateLimiter(0.25)
72
+
73
+ write_progress(current, total)
74
+ for chunk in iterable:
75
+ current += len(chunk)
76
+ if rate_limiter.ready() or current == total:
77
+ write_progress(current, total)
78
+ rate_limiter.reset()
79
+ yield chunk
80
+
81
+
82
+ def get_download_progress_renderer(
83
+ *, bar_type: str, size: Optional[int] = None
84
+ ) -> DownloadProgressRenderer:
85
+ """Get an object that can be used to render the download progress.
86
+
87
+ Returns a callable, that takes an iterable to "wrap".
88
+ """
89
+ if bar_type == "on":
90
+ return functools.partial(_rich_progress_bar, bar_type=bar_type, size=size)
91
+ elif bar_type == "raw":
92
+ return functools.partial(_raw_progress_bar, size=size)
93
+ else:
94
+ return iter # no-op, when passed an iterator
lib/python3.12/site-packages/pip/_internal/cli/req_command.py ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Contains the RequirementCommand base class.
2
+
3
+ This class is in a separate module so the commands that do not always
4
+ need PackageFinder capability don't unnecessarily import the
5
+ PackageFinder machinery and all its vendored dependencies, etc.
6
+ """
7
+
8
+ import logging
9
+ from functools import partial
10
+ from optparse import Values
11
+ from typing import Any, List, Optional, Tuple
12
+
13
+ from pip._internal.cache import WheelCache
14
+ from pip._internal.cli import cmdoptions
15
+ from pip._internal.cli.index_command import IndexGroupCommand
16
+ from pip._internal.cli.index_command import SessionCommandMixin as SessionCommandMixin
17
+ from pip._internal.exceptions import CommandError, PreviousBuildDirError
18
+ from pip._internal.index.collector import LinkCollector
19
+ from pip._internal.index.package_finder import PackageFinder
20
+ from pip._internal.models.selection_prefs import SelectionPreferences
21
+ from pip._internal.models.target_python import TargetPython
22
+ from pip._internal.network.session import PipSession
23
+ from pip._internal.operations.build.build_tracker import BuildTracker
24
+ from pip._internal.operations.prepare import RequirementPreparer
25
+ from pip._internal.req.constructors import (
26
+ install_req_from_editable,
27
+ install_req_from_line,
28
+ install_req_from_parsed_requirement,
29
+ install_req_from_req_string,
30
+ )
31
+ from pip._internal.req.req_file import parse_requirements
32
+ from pip._internal.req.req_install import InstallRequirement
33
+ from pip._internal.resolution.base import BaseResolver
34
+ from pip._internal.utils.temp_dir import (
35
+ TempDirectory,
36
+ TempDirectoryTypeRegistry,
37
+ tempdir_kinds,
38
+ )
39
+
40
+ logger = logging.getLogger(__name__)
41
+
42
+
43
+ KEEPABLE_TEMPDIR_TYPES = [
44
+ tempdir_kinds.BUILD_ENV,
45
+ tempdir_kinds.EPHEM_WHEEL_CACHE,
46
+ tempdir_kinds.REQ_BUILD,
47
+ ]
48
+
49
+
50
+ def with_cleanup(func: Any) -> Any:
51
+ """Decorator for common logic related to managing temporary
52
+ directories.
53
+ """
54
+
55
+ def configure_tempdir_registry(registry: TempDirectoryTypeRegistry) -> None:
56
+ for t in KEEPABLE_TEMPDIR_TYPES:
57
+ registry.set_delete(t, False)
58
+
59
+ def wrapper(
60
+ self: RequirementCommand, options: Values, args: List[Any]
61
+ ) -> Optional[int]:
62
+ assert self.tempdir_registry is not None
63
+ if options.no_clean:
64
+ configure_tempdir_registry(self.tempdir_registry)
65
+
66
+ try:
67
+ return func(self, options, args)
68
+ except PreviousBuildDirError:
69
+ # This kind of conflict can occur when the user passes an explicit
70
+ # build directory with a pre-existing folder. In that case we do
71
+ # not want to accidentally remove it.
72
+ configure_tempdir_registry(self.tempdir_registry)
73
+ raise
74
+
75
+ return wrapper
76
+
77
+
78
+ class RequirementCommand(IndexGroupCommand):
79
+ def __init__(self, *args: Any, **kw: Any) -> None:
80
+ super().__init__(*args, **kw)
81
+
82
+ self.cmd_opts.add_option(cmdoptions.no_clean())
83
+
84
+ @staticmethod
85
+ def determine_resolver_variant(options: Values) -> str:
86
+ """Determines which resolver should be used, based on the given options."""
87
+ if "legacy-resolver" in options.deprecated_features_enabled:
88
+ return "legacy"
89
+
90
+ return "resolvelib"
91
+
92
+ @classmethod
93
+ def make_requirement_preparer(
94
+ cls,
95
+ temp_build_dir: TempDirectory,
96
+ options: Values,
97
+ build_tracker: BuildTracker,
98
+ session: PipSession,
99
+ finder: PackageFinder,
100
+ use_user_site: bool,
101
+ download_dir: Optional[str] = None,
102
+ verbosity: int = 0,
103
+ ) -> RequirementPreparer:
104
+ """
105
+ Create a RequirementPreparer instance for the given parameters.
106
+ """
107
+ temp_build_dir_path = temp_build_dir.path
108
+ assert temp_build_dir_path is not None
109
+ legacy_resolver = False
110
+
111
+ resolver_variant = cls.determine_resolver_variant(options)
112
+ if resolver_variant == "resolvelib":
113
+ lazy_wheel = "fast-deps" in options.features_enabled
114
+ if lazy_wheel:
115
+ logger.warning(
116
+ "pip is using lazily downloaded wheels using HTTP "
117
+ "range requests to obtain dependency information. "
118
+ "This experimental feature is enabled through "
119
+ "--use-feature=fast-deps and it is not ready for "
120
+ "production."
121
+ )
122
+ else:
123
+ legacy_resolver = True
124
+ lazy_wheel = False
125
+ if "fast-deps" in options.features_enabled:
126
+ logger.warning(
127
+ "fast-deps has no effect when used with the legacy resolver."
128
+ )
129
+
130
+ return RequirementPreparer(
131
+ build_dir=temp_build_dir_path,
132
+ src_dir=options.src_dir,
133
+ download_dir=download_dir,
134
+ build_isolation=options.build_isolation,
135
+ check_build_deps=options.check_build_deps,
136
+ build_tracker=build_tracker,
137
+ session=session,
138
+ progress_bar=options.progress_bar,
139
+ finder=finder,
140
+ require_hashes=options.require_hashes,
141
+ use_user_site=use_user_site,
142
+ lazy_wheel=lazy_wheel,
143
+ verbosity=verbosity,
144
+ legacy_resolver=legacy_resolver,
145
+ )
146
+
147
+ @classmethod
148
+ def make_resolver(
149
+ cls,
150
+ preparer: RequirementPreparer,
151
+ finder: PackageFinder,
152
+ options: Values,
153
+ wheel_cache: Optional[WheelCache] = None,
154
+ use_user_site: bool = False,
155
+ ignore_installed: bool = True,
156
+ ignore_requires_python: bool = False,
157
+ force_reinstall: bool = False,
158
+ upgrade_strategy: str = "to-satisfy-only",
159
+ use_pep517: Optional[bool] = None,
160
+ py_version_info: Optional[Tuple[int, ...]] = None,
161
+ ) -> BaseResolver:
162
+ """
163
+ Create a Resolver instance for the given parameters.
164
+ """
165
+ make_install_req = partial(
166
+ install_req_from_req_string,
167
+ isolated=options.isolated_mode,
168
+ use_pep517=use_pep517,
169
+ )
170
+ resolver_variant = cls.determine_resolver_variant(options)
171
+ # The long import name and duplicated invocation is needed to convince
172
+ # Mypy into correctly typechecking. Otherwise it would complain the
173
+ # "Resolver" class being redefined.
174
+ if resolver_variant == "resolvelib":
175
+ import pip._internal.resolution.resolvelib.resolver
176
+
177
+ return pip._internal.resolution.resolvelib.resolver.Resolver(
178
+ preparer=preparer,
179
+ finder=finder,
180
+ wheel_cache=wheel_cache,
181
+ make_install_req=make_install_req,
182
+ use_user_site=use_user_site,
183
+ ignore_dependencies=options.ignore_dependencies,
184
+ ignore_installed=ignore_installed,
185
+ ignore_requires_python=ignore_requires_python,
186
+ force_reinstall=force_reinstall,
187
+ upgrade_strategy=upgrade_strategy,
188
+ py_version_info=py_version_info,
189
+ )
190
+ import pip._internal.resolution.legacy.resolver
191
+
192
+ return pip._internal.resolution.legacy.resolver.Resolver(
193
+ preparer=preparer,
194
+ finder=finder,
195
+ wheel_cache=wheel_cache,
196
+ make_install_req=make_install_req,
197
+ use_user_site=use_user_site,
198
+ ignore_dependencies=options.ignore_dependencies,
199
+ ignore_installed=ignore_installed,
200
+ ignore_requires_python=ignore_requires_python,
201
+ force_reinstall=force_reinstall,
202
+ upgrade_strategy=upgrade_strategy,
203
+ py_version_info=py_version_info,
204
+ )
205
+
206
+ def get_requirements(
207
+ self,
208
+ args: List[str],
209
+ options: Values,
210
+ finder: PackageFinder,
211
+ session: PipSession,
212
+ ) -> List[InstallRequirement]:
213
+ """
214
+ Parse command-line arguments into the corresponding requirements.
215
+ """
216
+ requirements: List[InstallRequirement] = []
217
+ for filename in options.constraints:
218
+ for parsed_req in parse_requirements(
219
+ filename,
220
+ constraint=True,
221
+ finder=finder,
222
+ options=options,
223
+ session=session,
224
+ ):
225
+ req_to_add = install_req_from_parsed_requirement(
226
+ parsed_req,
227
+ isolated=options.isolated_mode,
228
+ user_supplied=False,
229
+ )
230
+ requirements.append(req_to_add)
231
+
232
+ for req in args:
233
+ req_to_add = install_req_from_line(
234
+ req,
235
+ comes_from=None,
236
+ isolated=options.isolated_mode,
237
+ use_pep517=options.use_pep517,
238
+ user_supplied=True,
239
+ config_settings=getattr(options, "config_settings", None),
240
+ )
241
+ requirements.append(req_to_add)
242
+
243
+ for req in options.editables:
244
+ req_to_add = install_req_from_editable(
245
+ req,
246
+ user_supplied=True,
247
+ isolated=options.isolated_mode,
248
+ use_pep517=options.use_pep517,
249
+ config_settings=getattr(options, "config_settings", None),
250
+ )
251
+ requirements.append(req_to_add)
252
+
253
+ # NOTE: options.require_hashes may be set if --require-hashes is True
254
+ for filename in options.requirements:
255
+ for parsed_req in parse_requirements(
256
+ filename, finder=finder, options=options, session=session
257
+ ):
258
+ req_to_add = install_req_from_parsed_requirement(
259
+ parsed_req,
260
+ isolated=options.isolated_mode,
261
+ use_pep517=options.use_pep517,
262
+ user_supplied=True,
263
+ config_settings=(
264
+ parsed_req.options.get("config_settings")
265
+ if parsed_req.options
266
+ else None
267
+ ),
268
+ )
269
+ requirements.append(req_to_add)
270
+
271
+ # If any requirement has hash options, enable hash checking.
272
+ if any(req.has_hash_options for req in requirements):
273
+ options.require_hashes = True
274
+
275
+ if not (args or options.editables or options.requirements):
276
+ opts = {"name": self.name}
277
+ if options.find_links:
278
+ raise CommandError(
279
+ "You must give at least one requirement to {name} "
280
+ '(maybe you meant "pip {name} {links}"?)'.format(
281
+ **dict(opts, links=" ".join(options.find_links))
282
+ )
283
+ )
284
+ else:
285
+ raise CommandError(
286
+ "You must give at least one requirement to {name} "
287
+ '(see "pip help {name}")'.format(**opts)
288
+ )
289
+
290
+ return requirements
291
+
292
+ @staticmethod
293
+ def trace_basic_info(finder: PackageFinder) -> None:
294
+ """
295
+ Trace basic information about the provided objects.
296
+ """
297
+ # Display where finder is looking for packages
298
+ search_scope = finder.search_scope
299
+ locations = search_scope.get_formatted_locations()
300
+ if locations:
301
+ logger.info(locations)
302
+
303
+ def _build_package_finder(
304
+ self,
305
+ options: Values,
306
+ session: PipSession,
307
+ target_python: Optional[TargetPython] = None,
308
+ ignore_requires_python: Optional[bool] = None,
309
+ ) -> PackageFinder:
310
+ """
311
+ Create a package finder appropriate to this requirement command.
312
+
313
+ :param ignore_requires_python: Whether to ignore incompatible
314
+ "Requires-Python" values in links. Defaults to False.
315
+ """
316
+ link_collector = LinkCollector.create(session, options=options)
317
+ selection_prefs = SelectionPreferences(
318
+ allow_yanked=True,
319
+ format_control=options.format_control,
320
+ allow_all_prereleases=options.pre,
321
+ prefer_binary=options.prefer_binary,
322
+ ignore_requires_python=ignore_requires_python,
323
+ )
324
+
325
+ return PackageFinder.create(
326
+ link_collector=link_collector,
327
+ selection_prefs=selection_prefs,
328
+ target_python=target_python,
329
+ )
lib/python3.12/site-packages/pip/_internal/cli/spinners.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import contextlib
2
+ import itertools
3
+ import logging
4
+ import sys
5
+ import time
6
+ from typing import IO, Generator, Optional
7
+
8
+ from pip._internal.utils.compat import WINDOWS
9
+ from pip._internal.utils.logging import get_indentation
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class SpinnerInterface:
15
+ def spin(self) -> None:
16
+ raise NotImplementedError()
17
+
18
+ def finish(self, final_status: str) -> None:
19
+ raise NotImplementedError()
20
+
21
+
22
+ class InteractiveSpinner(SpinnerInterface):
23
+ def __init__(
24
+ self,
25
+ message: str,
26
+ file: Optional[IO[str]] = None,
27
+ spin_chars: str = "-\\|/",
28
+ # Empirically, 8 updates/second looks nice
29
+ min_update_interval_seconds: float = 0.125,
30
+ ):
31
+ self._message = message
32
+ if file is None:
33
+ file = sys.stdout
34
+ self._file = file
35
+ self._rate_limiter = RateLimiter(min_update_interval_seconds)
36
+ self._finished = False
37
+
38
+ self._spin_cycle = itertools.cycle(spin_chars)
39
+
40
+ self._file.write(" " * get_indentation() + self._message + " ... ")
41
+ self._width = 0
42
+
43
+ def _write(self, status: str) -> None:
44
+ assert not self._finished
45
+ # Erase what we wrote before by backspacing to the beginning, writing
46
+ # spaces to overwrite the old text, and then backspacing again
47
+ backup = "\b" * self._width
48
+ self._file.write(backup + " " * self._width + backup)
49
+ # Now we have a blank slate to add our status
50
+ self._file.write(status)
51
+ self._width = len(status)
52
+ self._file.flush()
53
+ self._rate_limiter.reset()
54
+
55
+ def spin(self) -> None:
56
+ if self._finished:
57
+ return
58
+ if not self._rate_limiter.ready():
59
+ return
60
+ self._write(next(self._spin_cycle))
61
+
62
+ def finish(self, final_status: str) -> None:
63
+ if self._finished:
64
+ return
65
+ self._write(final_status)
66
+ self._file.write("\n")
67
+ self._file.flush()
68
+ self._finished = True
69
+
70
+
71
+ # Used for dumb terminals, non-interactive installs (no tty), etc.
72
+ # We still print updates occasionally (once every 60 seconds by default) to
73
+ # act as a keep-alive for systems like Travis-CI that take lack-of-output as
74
+ # an indication that a task has frozen.
75
+ class NonInteractiveSpinner(SpinnerInterface):
76
+ def __init__(self, message: str, min_update_interval_seconds: float = 60.0) -> None:
77
+ self._message = message
78
+ self._finished = False
79
+ self._rate_limiter = RateLimiter(min_update_interval_seconds)
80
+ self._update("started")
81
+
82
+ def _update(self, status: str) -> None:
83
+ assert not self._finished
84
+ self._rate_limiter.reset()
85
+ logger.info("%s: %s", self._message, status)
86
+
87
+ def spin(self) -> None:
88
+ if self._finished:
89
+ return
90
+ if not self._rate_limiter.ready():
91
+ return
92
+ self._update("still running...")
93
+
94
+ def finish(self, final_status: str) -> None:
95
+ if self._finished:
96
+ return
97
+ self._update(f"finished with status '{final_status}'")
98
+ self._finished = True
99
+
100
+
101
+ class RateLimiter:
102
+ def __init__(self, min_update_interval_seconds: float) -> None:
103
+ self._min_update_interval_seconds = min_update_interval_seconds
104
+ self._last_update: float = 0
105
+
106
+ def ready(self) -> bool:
107
+ now = time.time()
108
+ delta = now - self._last_update
109
+ return delta >= self._min_update_interval_seconds
110
+
111
+ def reset(self) -> None:
112
+ self._last_update = time.time()
113
+
114
+
115
+ @contextlib.contextmanager
116
+ def open_spinner(message: str) -> Generator[SpinnerInterface, None, None]:
117
+ # Interactive spinner goes directly to sys.stdout rather than being routed
118
+ # through the logging system, but it acts like it has level INFO,
119
+ # i.e. it's only displayed if we're at level INFO or better.
120
+ # Non-interactive spinner goes through the logging system, so it is always
121
+ # in sync with logging configuration.
122
+ if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO:
123
+ spinner: SpinnerInterface = InteractiveSpinner(message)
124
+ else:
125
+ spinner = NonInteractiveSpinner(message)
126
+ try:
127
+ with hidden_cursor(sys.stdout):
128
+ yield spinner
129
+ except KeyboardInterrupt:
130
+ spinner.finish("canceled")
131
+ raise
132
+ except Exception:
133
+ spinner.finish("error")
134
+ raise
135
+ else:
136
+ spinner.finish("done")
137
+
138
+
139
+ HIDE_CURSOR = "\x1b[?25l"
140
+ SHOW_CURSOR = "\x1b[?25h"
141
+
142
+
143
+ @contextlib.contextmanager
144
+ def hidden_cursor(file: IO[str]) -> Generator[None, None, None]:
145
+ # The Windows terminal does not support the hide/show cursor ANSI codes,
146
+ # even via colorama. So don't even try.
147
+ if WINDOWS:
148
+ yield
149
+ # We don't want to clutter the output with control characters if we're
150
+ # writing to a file, or if the user is running with --quiet.
151
+ # See https://github.com/pypa/pip/issues/3418
152
+ elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO:
153
+ yield
154
+ else:
155
+ file.write(HIDE_CURSOR)
156
+ try:
157
+ yield
158
+ finally:
159
+ file.write(SHOW_CURSOR)
lib/python3.12/site-packages/pip/_internal/cli/status_codes.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ SUCCESS = 0
2
+ ERROR = 1
3
+ UNKNOWN_ERROR = 2
4
+ VIRTUALENV_NOT_FOUND = 3
5
+ PREVIOUS_BUILD_DIR_ERROR = 4
6
+ NO_MATCHES_FOUND = 23
lib/python3.12/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (203 Bytes). View file
 
lib/python3.12/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc ADDED
Binary file (7.11 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc ADDED
Binary file (10.3 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc ADDED
Binary file (25.8 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/build/__init__.py ADDED
File without changes
lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (209 Bytes). View file
 
lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc ADDED
Binary file (7.67 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc ADDED
Binary file (1.86 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc ADDED
Binary file (1.92 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-312.pyc ADDED
Binary file (3.02 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc ADDED
Binary file (1.68 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc ADDED
Binary file (2.02 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-312.pyc ADDED
Binary file (3.85 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import contextlib
2
+ import hashlib
3
+ import logging
4
+ import os
5
+ from types import TracebackType
6
+ from typing import Dict, Generator, Optional, Type, Union
7
+
8
+ from pip._internal.req.req_install import InstallRequirement
9
+ from pip._internal.utils.temp_dir import TempDirectory
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ @contextlib.contextmanager
15
+ def update_env_context_manager(**changes: str) -> Generator[None, None, None]:
16
+ target = os.environ
17
+
18
+ # Save values from the target and change them.
19
+ non_existent_marker = object()
20
+ saved_values: Dict[str, Union[object, str]] = {}
21
+ for name, new_value in changes.items():
22
+ try:
23
+ saved_values[name] = target[name]
24
+ except KeyError:
25
+ saved_values[name] = non_existent_marker
26
+ target[name] = new_value
27
+
28
+ try:
29
+ yield
30
+ finally:
31
+ # Restore original values in the target.
32
+ for name, original_value in saved_values.items():
33
+ if original_value is non_existent_marker:
34
+ del target[name]
35
+ else:
36
+ assert isinstance(original_value, str) # for mypy
37
+ target[name] = original_value
38
+
39
+
40
+ @contextlib.contextmanager
41
+ def get_build_tracker() -> Generator["BuildTracker", None, None]:
42
+ root = os.environ.get("PIP_BUILD_TRACKER")
43
+ with contextlib.ExitStack() as ctx:
44
+ if root is None:
45
+ root = ctx.enter_context(TempDirectory(kind="build-tracker")).path
46
+ ctx.enter_context(update_env_context_manager(PIP_BUILD_TRACKER=root))
47
+ logger.debug("Initialized build tracking at %s", root)
48
+
49
+ with BuildTracker(root) as tracker:
50
+ yield tracker
51
+
52
+
53
+ class TrackerId(str):
54
+ """Uniquely identifying string provided to the build tracker."""
55
+
56
+
57
+ class BuildTracker:
58
+ """Ensure that an sdist cannot request itself as a setup requirement.
59
+
60
+ When an sdist is prepared, it identifies its setup requirements in the
61
+ context of ``BuildTracker.track()``. If a requirement shows up recursively, this
62
+ raises an exception.
63
+
64
+ This stops fork bombs embedded in malicious packages."""
65
+
66
+ def __init__(self, root: str) -> None:
67
+ self._root = root
68
+ self._entries: Dict[TrackerId, InstallRequirement] = {}
69
+ logger.debug("Created build tracker: %s", self._root)
70
+
71
+ def __enter__(self) -> "BuildTracker":
72
+ logger.debug("Entered build tracker: %s", self._root)
73
+ return self
74
+
75
+ def __exit__(
76
+ self,
77
+ exc_type: Optional[Type[BaseException]],
78
+ exc_val: Optional[BaseException],
79
+ exc_tb: Optional[TracebackType],
80
+ ) -> None:
81
+ self.cleanup()
82
+
83
+ def _entry_path(self, key: TrackerId) -> str:
84
+ hashed = hashlib.sha224(key.encode()).hexdigest()
85
+ return os.path.join(self._root, hashed)
86
+
87
+ def add(self, req: InstallRequirement, key: TrackerId) -> None:
88
+ """Add an InstallRequirement to build tracking."""
89
+
90
+ # Get the file to write information about this requirement.
91
+ entry_path = self._entry_path(key)
92
+
93
+ # Try reading from the file. If it exists and can be read from, a build
94
+ # is already in progress, so a LookupError is raised.
95
+ try:
96
+ with open(entry_path) as fp:
97
+ contents = fp.read()
98
+ except FileNotFoundError:
99
+ pass
100
+ else:
101
+ message = f"{req.link} is already being built: {contents}"
102
+ raise LookupError(message)
103
+
104
+ # If we're here, req should really not be building already.
105
+ assert key not in self._entries
106
+
107
+ # Start tracking this requirement.
108
+ with open(entry_path, "w", encoding="utf-8") as fp:
109
+ fp.write(str(req))
110
+ self._entries[key] = req
111
+
112
+ logger.debug("Added %s to build tracker %r", req, self._root)
113
+
114
+ def remove(self, req: InstallRequirement, key: TrackerId) -> None:
115
+ """Remove an InstallRequirement from build tracking."""
116
+
117
+ # Delete the created file and the corresponding entry.
118
+ os.unlink(self._entry_path(key))
119
+ del self._entries[key]
120
+
121
+ logger.debug("Removed %s from build tracker %r", req, self._root)
122
+
123
+ def cleanup(self) -> None:
124
+ for key, req in list(self._entries.items()):
125
+ self.remove(req, key)
126
+
127
+ logger.debug("Removed build tracker: %r", self._root)
128
+
129
+ @contextlib.contextmanager
130
+ def track(self, req: InstallRequirement, key: str) -> Generator[None, None, None]:
131
+ """Ensure that `key` cannot install itself as a setup requirement.
132
+
133
+ :raises LookupError: If `key` was already provided in a parent invocation of
134
+ the context introduced by this method."""
135
+ tracker_id = TrackerId(key)
136
+ self.add(req, tracker_id)
137
+ yield
138
+ self.remove(req, tracker_id)
lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Metadata generation logic for source distributions.
2
+ """
3
+
4
+ import os
5
+
6
+ from pip._vendor.pyproject_hooks import BuildBackendHookCaller
7
+
8
+ from pip._internal.build_env import BuildEnvironment
9
+ from pip._internal.exceptions import (
10
+ InstallationSubprocessError,
11
+ MetadataGenerationFailed,
12
+ )
13
+ from pip._internal.utils.subprocess import runner_with_spinner_message
14
+ from pip._internal.utils.temp_dir import TempDirectory
15
+
16
+
17
+ def generate_metadata(
18
+ build_env: BuildEnvironment, backend: BuildBackendHookCaller, details: str
19
+ ) -> str:
20
+ """Generate metadata using mechanisms described in PEP 517.
21
+
22
+ Returns the generated metadata directory.
23
+ """
24
+ metadata_tmpdir = TempDirectory(kind="modern-metadata", globally_managed=True)
25
+
26
+ metadata_dir = metadata_tmpdir.path
27
+
28
+ with build_env:
29
+ # Note that BuildBackendHookCaller implements a fallback for
30
+ # prepare_metadata_for_build_wheel, so we don't have to
31
+ # consider the possibility that this hook doesn't exist.
32
+ runner = runner_with_spinner_message("Preparing metadata (pyproject.toml)")
33
+ with backend.subprocess_runner(runner):
34
+ try:
35
+ distinfo_dir = backend.prepare_metadata_for_build_wheel(metadata_dir)
36
+ except InstallationSubprocessError as error:
37
+ raise MetadataGenerationFailed(package_details=details) from error
38
+
39
+ return os.path.join(metadata_dir, distinfo_dir)
lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Metadata generation logic for source distributions.
2
+ """
3
+
4
+ import os
5
+
6
+ from pip._vendor.pyproject_hooks import BuildBackendHookCaller
7
+
8
+ from pip._internal.build_env import BuildEnvironment
9
+ from pip._internal.exceptions import (
10
+ InstallationSubprocessError,
11
+ MetadataGenerationFailed,
12
+ )
13
+ from pip._internal.utils.subprocess import runner_with_spinner_message
14
+ from pip._internal.utils.temp_dir import TempDirectory
15
+
16
+
17
+ def generate_editable_metadata(
18
+ build_env: BuildEnvironment, backend: BuildBackendHookCaller, details: str
19
+ ) -> str:
20
+ """Generate metadata using mechanisms described in PEP 660.
21
+
22
+ Returns the generated metadata directory.
23
+ """
24
+ metadata_tmpdir = TempDirectory(kind="modern-metadata", globally_managed=True)
25
+
26
+ metadata_dir = metadata_tmpdir.path
27
+
28
+ with build_env:
29
+ # Note that BuildBackendHookCaller implements a fallback for
30
+ # prepare_metadata_for_build_wheel/editable, so we don't have to
31
+ # consider the possibility that this hook doesn't exist.
32
+ runner = runner_with_spinner_message(
33
+ "Preparing editable metadata (pyproject.toml)"
34
+ )
35
+ with backend.subprocess_runner(runner):
36
+ try:
37
+ distinfo_dir = backend.prepare_metadata_for_build_editable(metadata_dir)
38
+ except InstallationSubprocessError as error:
39
+ raise MetadataGenerationFailed(package_details=details) from error
40
+
41
+ assert distinfo_dir is not None
42
+ return os.path.join(metadata_dir, distinfo_dir)
lib/python3.12/site-packages/pip/_internal/operations/build/metadata_legacy.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Metadata generation logic for legacy source distributions.
2
+ """
3
+
4
+ import logging
5
+ import os
6
+
7
+ from pip._internal.build_env import BuildEnvironment
8
+ from pip._internal.cli.spinners import open_spinner
9
+ from pip._internal.exceptions import (
10
+ InstallationError,
11
+ InstallationSubprocessError,
12
+ MetadataGenerationFailed,
13
+ )
14
+ from pip._internal.utils.setuptools_build import make_setuptools_egg_info_args
15
+ from pip._internal.utils.subprocess import call_subprocess
16
+ from pip._internal.utils.temp_dir import TempDirectory
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ def _find_egg_info(directory: str) -> str:
22
+ """Find an .egg-info subdirectory in `directory`."""
23
+ filenames = [f for f in os.listdir(directory) if f.endswith(".egg-info")]
24
+
25
+ if not filenames:
26
+ raise InstallationError(f"No .egg-info directory found in {directory}")
27
+
28
+ if len(filenames) > 1:
29
+ raise InstallationError(
30
+ f"More than one .egg-info directory found in {directory}"
31
+ )
32
+
33
+ return os.path.join(directory, filenames[0])
34
+
35
+
36
+ def generate_metadata(
37
+ build_env: BuildEnvironment,
38
+ setup_py_path: str,
39
+ source_dir: str,
40
+ isolated: bool,
41
+ details: str,
42
+ ) -> str:
43
+ """Generate metadata using setup.py-based defacto mechanisms.
44
+
45
+ Returns the generated metadata directory.
46
+ """
47
+ logger.debug(
48
+ "Running setup.py (path:%s) egg_info for package %s",
49
+ setup_py_path,
50
+ details,
51
+ )
52
+
53
+ egg_info_dir = TempDirectory(kind="pip-egg-info", globally_managed=True).path
54
+
55
+ args = make_setuptools_egg_info_args(
56
+ setup_py_path,
57
+ egg_info_dir=egg_info_dir,
58
+ no_user_config=isolated,
59
+ )
60
+
61
+ with build_env:
62
+ with open_spinner("Preparing metadata (setup.py)") as spinner:
63
+ try:
64
+ call_subprocess(
65
+ args,
66
+ cwd=source_dir,
67
+ command_desc="python setup.py egg_info",
68
+ spinner=spinner,
69
+ )
70
+ except InstallationSubprocessError as error:
71
+ raise MetadataGenerationFailed(package_details=details) from error
72
+
73
+ # Return the .egg-info directory.
74
+ return _find_egg_info(egg_info_dir)
lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+ from typing import Optional
4
+
5
+ from pip._vendor.pyproject_hooks import BuildBackendHookCaller
6
+
7
+ from pip._internal.utils.subprocess import runner_with_spinner_message
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ def build_wheel_pep517(
13
+ name: str,
14
+ backend: BuildBackendHookCaller,
15
+ metadata_directory: str,
16
+ tempd: str,
17
+ ) -> Optional[str]:
18
+ """Build one InstallRequirement using the PEP 517 build process.
19
+
20
+ Returns path to wheel if successfully built. Otherwise, returns None.
21
+ """
22
+ assert metadata_directory is not None
23
+ try:
24
+ logger.debug("Destination directory: %s", tempd)
25
+
26
+ runner = runner_with_spinner_message(
27
+ f"Building wheel for {name} (pyproject.toml)"
28
+ )
29
+ with backend.subprocess_runner(runner):
30
+ wheel_name = backend.build_wheel(
31
+ tempd,
32
+ metadata_directory=metadata_directory,
33
+ )
34
+ except Exception:
35
+ logger.error("Failed building wheel for %s", name)
36
+ return None
37
+ return os.path.join(tempd, wheel_name)
lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+ from typing import Optional
4
+
5
+ from pip._vendor.pyproject_hooks import BuildBackendHookCaller, HookMissing
6
+
7
+ from pip._internal.utils.subprocess import runner_with_spinner_message
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ def build_wheel_editable(
13
+ name: str,
14
+ backend: BuildBackendHookCaller,
15
+ metadata_directory: str,
16
+ tempd: str,
17
+ ) -> Optional[str]:
18
+ """Build one InstallRequirement using the PEP 660 build process.
19
+
20
+ Returns path to wheel if successfully built. Otherwise, returns None.
21
+ """
22
+ assert metadata_directory is not None
23
+ try:
24
+ logger.debug("Destination directory: %s", tempd)
25
+
26
+ runner = runner_with_spinner_message(
27
+ f"Building editable for {name} (pyproject.toml)"
28
+ )
29
+ with backend.subprocess_runner(runner):
30
+ try:
31
+ wheel_name = backend.build_editable(
32
+ tempd,
33
+ metadata_directory=metadata_directory,
34
+ )
35
+ except HookMissing as e:
36
+ logger.error(
37
+ "Cannot build editable %s because the build "
38
+ "backend does not have the %s hook",
39
+ name,
40
+ e,
41
+ )
42
+ return None
43
+ except Exception:
44
+ logger.error("Failed building editable for %s", name)
45
+ return None
46
+ return os.path.join(tempd, wheel_name)
lib/python3.12/site-packages/pip/_internal/operations/build/wheel_legacy.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os.path
3
+ from typing import List, Optional
4
+
5
+ from pip._internal.cli.spinners import open_spinner
6
+ from pip._internal.utils.setuptools_build import make_setuptools_bdist_wheel_args
7
+ from pip._internal.utils.subprocess import call_subprocess, format_command_args
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ def format_command_result(
13
+ command_args: List[str],
14
+ command_output: str,
15
+ ) -> str:
16
+ """Format command information for logging."""
17
+ command_desc = format_command_args(command_args)
18
+ text = f"Command arguments: {command_desc}\n"
19
+
20
+ if not command_output:
21
+ text += "Command output: None"
22
+ elif logger.getEffectiveLevel() > logging.DEBUG:
23
+ text += "Command output: [use --verbose to show]"
24
+ else:
25
+ if not command_output.endswith("\n"):
26
+ command_output += "\n"
27
+ text += f"Command output:\n{command_output}"
28
+
29
+ return text
30
+
31
+
32
+ def get_legacy_build_wheel_path(
33
+ names: List[str],
34
+ temp_dir: str,
35
+ name: str,
36
+ command_args: List[str],
37
+ command_output: str,
38
+ ) -> Optional[str]:
39
+ """Return the path to the wheel in the temporary build directory."""
40
+ # Sort for determinism.
41
+ names = sorted(names)
42
+ if not names:
43
+ msg = f"Legacy build of wheel for {name!r} created no files.\n"
44
+ msg += format_command_result(command_args, command_output)
45
+ logger.warning(msg)
46
+ return None
47
+
48
+ if len(names) > 1:
49
+ msg = (
50
+ f"Legacy build of wheel for {name!r} created more than one file.\n"
51
+ f"Filenames (choosing first): {names}\n"
52
+ )
53
+ msg += format_command_result(command_args, command_output)
54
+ logger.warning(msg)
55
+
56
+ return os.path.join(temp_dir, names[0])
57
+
58
+
59
+ def build_wheel_legacy(
60
+ name: str,
61
+ setup_py_path: str,
62
+ source_dir: str,
63
+ global_options: List[str],
64
+ build_options: List[str],
65
+ tempd: str,
66
+ ) -> Optional[str]:
67
+ """Build one unpacked package using the "legacy" build process.
68
+
69
+ Returns path to wheel if successfully built. Otherwise, returns None.
70
+ """
71
+ wheel_args = make_setuptools_bdist_wheel_args(
72
+ setup_py_path,
73
+ global_options=global_options,
74
+ build_options=build_options,
75
+ destination_dir=tempd,
76
+ )
77
+
78
+ spin_message = f"Building wheel for {name} (setup.py)"
79
+ with open_spinner(spin_message) as spinner:
80
+ logger.debug("Destination directory: %s", tempd)
81
+
82
+ try:
83
+ output = call_subprocess(
84
+ wheel_args,
85
+ command_desc="python setup.py bdist_wheel",
86
+ cwd=source_dir,
87
+ spinner=spinner,
88
+ )
89
+ except Exception:
90
+ spinner.finish("error")
91
+ logger.error("Failed building wheel for %s", name)
92
+ return None
93
+
94
+ names = os.listdir(tempd)
95
+ wheel_path = get_legacy_build_wheel_path(
96
+ names=names,
97
+ temp_dir=tempd,
98
+ name=name,
99
+ command_args=wheel_args,
100
+ command_output=output,
101
+ )
102
+ return wheel_path
lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ """For modules related to installing packages.
2
+ """
lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (272 Bytes). View file
 
lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-312.pyc ADDED
Binary file (1.8 kB). View file
 
lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc ADDED
Binary file (34.1 kB). View file