|
1 | 1 | """Base Command class, and related routines"""
|
2 | 2 |
|
3 |
| -import functools |
4 | 3 | import logging
|
5 | 4 | import logging.config
|
6 | 5 | import optparse
|
7 | 6 | import os
|
8 | 7 | import sys
|
9 | 8 | import traceback
|
10 | 9 | from optparse import Values
|
11 |
| -from typing import Any, Callable, List, Optional, Tuple |
| 10 | +from typing import List, Optional, Tuple |
12 | 11 |
|
13 | 12 | from pip._vendor.rich import reconfigure
|
14 | 13 | from pip._vendor.rich import traceback as rich_traceback
|
@@ -91,6 +90,63 @@ def handle_pip_version_check(self, options: Values) -> None:
|
91 | 90 | def run(self, options: Values, args: List[str]) -> int:
|
92 | 91 | raise NotImplementedError
|
93 | 92 |
|
| 93 | + def _run_wrapper(self, level_number: int, options: Values, args: List[str]) -> int: |
| 94 | + def _inner_run() -> int: |
| 95 | + try: |
| 96 | + return self.run(options, args) |
| 97 | + finally: |
| 98 | + self.handle_pip_version_check(options) |
| 99 | + |
| 100 | + if options.debug_mode: |
| 101 | + rich_traceback.install(show_locals=True) |
| 102 | + return _inner_run() |
| 103 | + |
| 104 | + try: |
| 105 | + status = _inner_run() |
| 106 | + assert isinstance(status, int) |
| 107 | + return status |
| 108 | + except DiagnosticPipError as exc: |
| 109 | + logger.error("%s", exc, extra={"rich": True}) |
| 110 | + logger.debug("Exception information:", exc_info=True) |
| 111 | + |
| 112 | + return ERROR |
| 113 | + except PreviousBuildDirError as exc: |
| 114 | + logger.critical(str(exc)) |
| 115 | + logger.debug("Exception information:", exc_info=True) |
| 116 | + |
| 117 | + return PREVIOUS_BUILD_DIR_ERROR |
| 118 | + except ( |
| 119 | + InstallationError, |
| 120 | + BadCommand, |
| 121 | + NetworkConnectionError, |
| 122 | + ) as exc: |
| 123 | + logger.critical(str(exc)) |
| 124 | + logger.debug("Exception information:", exc_info=True) |
| 125 | + |
| 126 | + return ERROR |
| 127 | + except CommandError as exc: |
| 128 | + logger.critical("%s", exc) |
| 129 | + logger.debug("Exception information:", exc_info=True) |
| 130 | + |
| 131 | + return ERROR |
| 132 | + except BrokenStdoutLoggingError: |
| 133 | + # Bypass our logger and write any remaining messages to |
| 134 | + # stderr because stdout no longer works. |
| 135 | + print("ERROR: Pipe to stdout was broken", file=sys.stderr) |
| 136 | + if level_number <= logging.DEBUG: |
| 137 | + traceback.print_exc(file=sys.stderr) |
| 138 | + |
| 139 | + return ERROR |
| 140 | + except KeyboardInterrupt: |
| 141 | + logger.critical("Operation cancelled by user") |
| 142 | + logger.debug("Exception information:", exc_info=True) |
| 143 | + |
| 144 | + return ERROR |
| 145 | + except BaseException: |
| 146 | + logger.critical("Exception:", exc_info=True) |
| 147 | + |
| 148 | + return UNKNOWN_ERROR |
| 149 | + |
94 | 150 | def parse_args(self, args: List[str]) -> Tuple[Values, List[str]]:
|
95 | 151 | # factored out for testability
|
96 | 152 | return self.parser.parse_args(args)
|
@@ -172,65 +228,4 @@ def _main(self, args: List[str]) -> int:
|
172 | 228 | )
|
173 | 229 | options.cache_dir = None
|
174 | 230 |
|
175 |
| - def intercepts_unhandled_exc( |
176 |
| - run_func: Callable[..., int] |
177 |
| - ) -> Callable[..., int]: |
178 |
| - @functools.wraps(run_func) |
179 |
| - def exc_logging_wrapper(*args: Any) -> int: |
180 |
| - try: |
181 |
| - status = run_func(*args) |
182 |
| - assert isinstance(status, int) |
183 |
| - return status |
184 |
| - except DiagnosticPipError as exc: |
185 |
| - logger.error("%s", exc, extra={"rich": True}) |
186 |
| - logger.debug("Exception information:", exc_info=True) |
187 |
| - |
188 |
| - return ERROR |
189 |
| - except PreviousBuildDirError as exc: |
190 |
| - logger.critical(str(exc)) |
191 |
| - logger.debug("Exception information:", exc_info=True) |
192 |
| - |
193 |
| - return PREVIOUS_BUILD_DIR_ERROR |
194 |
| - except ( |
195 |
| - InstallationError, |
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: |
213 |
| - traceback.print_exc(file=sys.stderr) |
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) |
| 231 | + return self._run_wrapper(level_number, options, args) |
0 commit comments