Let us walk on the 3-isogeny graph
Loading...
Searching...
No Matches
base_command.py
Go to the documentation of this file.
1"""Base Command class, and related routines"""
2
3import functools
4import logging
6import optparse
7import os
8import sys
9import traceback
10from optparse import Values
11from typing import Any, Callable, List, Optional, Tuple
12
13from pip._vendor.rich import traceback as rich_traceback
14
15from pip._internal.cli import cmdoptions
16from pip._internal.cli.command_context import CommandContextMixIn
17from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
19 ERROR,
20 PREVIOUS_BUILD_DIR_ERROR,
21 UNKNOWN_ERROR,
22 VIRTUALENV_NOT_FOUND,
23)
24from pip._internal.exceptions import (
25 BadCommand,
26 CommandError,
27 DiagnosticPipError,
28 InstallationError,
29 NetworkConnectionError,
30 PreviousBuildDirError,
31 UninstallationError,
32)
33from pip._internal.utils.filesystem import check_path_owner
34from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging
35from pip._internal.utils.misc import get_prog, normalize_path
36from pip._internal.utils.temp_dir import TempDirectoryTypeRegistry as TempDirRegistry
37from pip._internal.utils.temp_dir import global_tempdir_manager, tempdir_registry
38from pip._internal.utils.virtualenv import running_under_virtualenv
39
40__all__ = ["Command"]
41
42logger = logging.getLogger(__name__)
43
44
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
55 usage=self.usage,
56 prog=f"{get_prog()} {name}",
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
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 parse_args(self, args: List[str]) -> Tuple[Values, List[str]]:
95 # factored out for testability
96 return self.parser.parse_args(args)
97
98 def main(self, args: List[str]) -> int:
99 try:
100 with self.main_context():
101 return self._main(args)
102 finally:
104
105 def _main(self, args: List[str]) -> int:
106 # We must initialize this before the tempdir manager, otherwise the
107 # configuration would not be accessible by the time we clean up the
108 # tempdir manager.
110 # Intentionally set as early as possible so globally-managed temporary
111 # directories are available to the rest of the code.
112 self.enter_context(global_tempdir_manager())
113
114 options, args = self.parse_args(args)
115
116 # Set verbosity so that it can be used elsewhere.
118
119 level_number = setup_logging(
120 verbosity=self.verbosity,
121 no_color=options.no_color,
122 user_log_file=options.log,
123 )
124
125 always_enabled_features = set(options.features_enabled) & set(
127 )
128 if always_enabled_features:
130 "The following features are always enabled: %s. ",
131 ", ".join(sorted(always_enabled_features)),
132 )
133
134 # Make sure that the --python argument isn't specified after the
135 # subcommand. We can tell, because if --python was specified,
136 # we should only reach this point if we're running in the created
137 # subprocess, which has the _PIP_RUNNING_IN_SUBPROCESS environment
138 # variable set.
139 if options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ:
141 "The --python option must be placed before the pip subcommand name"
142 )
143 sys.exit(ERROR)
144
145 # TODO: Try to get these passing down from the command?
146 # without resorting to os.environ to hold these.
147 # This also affects isolated builds and it should.
148
150 os.environ["PIP_NO_INPUT"] = "1"
151
153 os.environ["PIP_EXISTS_ACTION"] = " ".join(options.exists_action)
154
156 # If a venv is required check if it can really be found
157 if not running_under_virtualenv():
158 logger.critical("Could not find an activated virtualenv (required).")
159 sys.exit(VIRTUALENV_NOT_FOUND)
160
162 options.cache_dir = normalize_path(options.cache_dir)
163 if not check_path_owner(options.cache_dir):
165 "The directory '%s' or its parent directory is not owned "
166 "or is not writable by the current user. The cache "
167 "has been disabled. Check the permissions and owner of "
168 "that directory. If executing pip with sudo, you should "
169 "use sudo's -H flag.",
171 )
172 options.cache_dir = None
173
175 run_func: Callable[..., int]
176 ) -> Callable[..., int]:
177 @functools.wraps(run_func)
178 def exc_logging_wrapper(*args: Any) -> int:
179 try:
180 status = run_func(*args)
181 assert isinstance(status, int)
182 return status
183 except DiagnosticPipError as exc:
184 logger.error("[present-rich] %s", exc)
185 logger.debug("Exception information:", exc_info=True)
186
187 return ERROR
188 except PreviousBuildDirError as exc:
189 logger.critical(str(exc))
190 logger.debug("Exception information:", exc_info=True)
191
192 return PREVIOUS_BUILD_DIR_ERROR
193 except (
194 InstallationError,
195 UninstallationError,
196 BadCommand,
197 NetworkConnectionError,
198 ) as exc:
199 logger.critical(str(exc))
200 logger.debug("Exception information:", exc_info=True)
201
202 return ERROR
203 except CommandError as exc:
204 logger.critical("%s", exc)
205 logger.debug("Exception information:", exc_info=True)
206
207 return ERROR
208 except BrokenStdoutLoggingError:
209 # Bypass our logger and write any remaining messages to
210 # stderr because stdout no longer works.
211 print("ERROR: Pipe to stdout was broken", file=sys.stderr)
212 if level_number <= logging.DEBUG:
214
215 return ERROR
216 except KeyboardInterrupt:
217 logger.critical("Operation cancelled by user")
218 logger.debug("Exception information:", exc_info=True)
219
220 return ERROR
221 except BaseException:
222 logger.critical("Exception:", exc_info=True)
223
224 return UNKNOWN_ERROR
225
226 return exc_logging_wrapper
227
228 try:
229 if not options.debug_mode:
230 run = intercepts_unhandled_exc(self.run)
231 else:
232 run = self.run
233 rich_traceback.install(show_locals=True)
234 return run(options, args)
235 finally:
236 self.handle_pip_version_check(options)
int _main(self, List[str] args)
None __init__(self, str name, str summary, bool isolated=False)
Tuple[Values, List[str]] parse_args(self, List[str] args)
None handle_pip_version_check(self, Values options)
int run(self, Values options, List[str] args)
_T enter_context(self, ContextManager[_T] context_provider)
for i