Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/pip/_internal/utils/logging.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:32:28 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:d30785e31577 | 1:56ad4e20f292 |
|---|---|
| 1 from __future__ import absolute_import | |
| 2 | |
| 3 import contextlib | |
| 4 import errno | |
| 5 import logging | |
| 6 import logging.handlers | |
| 7 import os | |
| 8 import sys | |
| 9 from logging import Filter | |
| 10 | |
| 11 from pip._vendor.six import PY2 | |
| 12 | |
| 13 from pip._internal.utils.compat import WINDOWS | |
| 14 from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX | |
| 15 from pip._internal.utils.misc import ensure_dir, subprocess_logger | |
| 16 | |
| 17 try: | |
| 18 import threading | |
| 19 except ImportError: | |
| 20 import dummy_threading as threading # type: ignore | |
| 21 | |
| 22 | |
| 23 try: | |
| 24 # Use "import as" and set colorama in the else clause to avoid mypy | |
| 25 # errors and get the following correct revealed type for colorama: | |
| 26 # `Union[_importlib_modulespec.ModuleType, None]` | |
| 27 # Otherwise, we get an error like the following in the except block: | |
| 28 # > Incompatible types in assignment (expression has type "None", | |
| 29 # variable has type Module) | |
| 30 # TODO: eliminate the need to use "import as" once mypy addresses some | |
| 31 # of its issues with conditional imports. Here is an umbrella issue: | |
| 32 # https://github.com/python/mypy/issues/1297 | |
| 33 from pip._vendor import colorama as _colorama | |
| 34 # Lots of different errors can come from this, including SystemError and | |
| 35 # ImportError. | |
| 36 except Exception: | |
| 37 colorama = None | |
| 38 else: | |
| 39 # Import Fore explicitly rather than accessing below as colorama.Fore | |
| 40 # to avoid the following error running mypy: | |
| 41 # > Module has no attribute "Fore" | |
| 42 # TODO: eliminate the need to import Fore once mypy addresses some of its | |
| 43 # issues with conditional imports. This particular case could be an | |
| 44 # instance of the following issue (but also see the umbrella issue above): | |
| 45 # https://github.com/python/mypy/issues/3500 | |
| 46 from pip._vendor.colorama import Fore | |
| 47 | |
| 48 colorama = _colorama | |
| 49 | |
| 50 | |
| 51 _log_state = threading.local() | |
| 52 _log_state.indentation = 0 | |
| 53 | |
| 54 | |
| 55 class BrokenStdoutLoggingError(Exception): | |
| 56 """ | |
| 57 Raised if BrokenPipeError occurs for the stdout stream while logging. | |
| 58 """ | |
| 59 pass | |
| 60 | |
| 61 | |
| 62 # BrokenPipeError does not exist in Python 2 and, in addition, manifests | |
| 63 # differently in Windows and non-Windows. | |
| 64 if WINDOWS: | |
| 65 # In Windows, a broken pipe can show up as EINVAL rather than EPIPE: | |
| 66 # https://bugs.python.org/issue19612 | |
| 67 # https://bugs.python.org/issue30418 | |
| 68 if PY2: | |
| 69 def _is_broken_pipe_error(exc_class, exc): | |
| 70 """See the docstring for non-Windows Python 3 below.""" | |
| 71 return (exc_class is IOError and | |
| 72 exc.errno in (errno.EINVAL, errno.EPIPE)) | |
| 73 else: | |
| 74 # In Windows, a broken pipe IOError became OSError in Python 3. | |
| 75 def _is_broken_pipe_error(exc_class, exc): | |
| 76 """See the docstring for non-Windows Python 3 below.""" | |
| 77 return ((exc_class is BrokenPipeError) or # noqa: F821 | |
| 78 (exc_class is OSError and | |
| 79 exc.errno in (errno.EINVAL, errno.EPIPE))) | |
| 80 elif PY2: | |
| 81 def _is_broken_pipe_error(exc_class, exc): | |
| 82 """See the docstring for non-Windows Python 3 below.""" | |
| 83 return (exc_class is IOError and exc.errno == errno.EPIPE) | |
| 84 else: | |
| 85 # Then we are in the non-Windows Python 3 case. | |
| 86 def _is_broken_pipe_error(exc_class, exc): | |
| 87 """ | |
| 88 Return whether an exception is a broken pipe error. | |
| 89 | |
| 90 Args: | |
| 91 exc_class: an exception class. | |
| 92 exc: an exception instance. | |
| 93 """ | |
| 94 return (exc_class is BrokenPipeError) # noqa: F821 | |
| 95 | |
| 96 | |
| 97 @contextlib.contextmanager | |
| 98 def indent_log(num=2): | |
| 99 """ | |
| 100 A context manager which will cause the log output to be indented for any | |
| 101 log messages emitted inside it. | |
| 102 """ | |
| 103 _log_state.indentation += num | |
| 104 try: | |
| 105 yield | |
| 106 finally: | |
| 107 _log_state.indentation -= num | |
| 108 | |
| 109 | |
| 110 def get_indentation(): | |
| 111 return getattr(_log_state, 'indentation', 0) | |
| 112 | |
| 113 | |
| 114 class IndentingFormatter(logging.Formatter): | |
| 115 | |
| 116 def __init__(self, *args, **kwargs): | |
| 117 """ | |
| 118 A logging.Formatter that obeys the indent_log() context manager. | |
| 119 | |
| 120 :param add_timestamp: A bool indicating output lines should be prefixed | |
| 121 with their record's timestamp. | |
| 122 """ | |
| 123 self.add_timestamp = kwargs.pop("add_timestamp", False) | |
| 124 super(IndentingFormatter, self).__init__(*args, **kwargs) | |
| 125 | |
| 126 def get_message_start(self, formatted, levelno): | |
| 127 """ | |
| 128 Return the start of the formatted log message (not counting the | |
| 129 prefix to add to each line). | |
| 130 """ | |
| 131 if levelno < logging.WARNING: | |
| 132 return '' | |
| 133 if formatted.startswith(DEPRECATION_MSG_PREFIX): | |
| 134 # Then the message already has a prefix. We don't want it to | |
| 135 # look like "WARNING: DEPRECATION: ...." | |
| 136 return '' | |
| 137 if levelno < logging.ERROR: | |
| 138 return 'WARNING: ' | |
| 139 | |
| 140 return 'ERROR: ' | |
| 141 | |
| 142 def format(self, record): | |
| 143 """ | |
| 144 Calls the standard formatter, but will indent all of the log message | |
| 145 lines by our current indentation level. | |
| 146 """ | |
| 147 formatted = super(IndentingFormatter, self).format(record) | |
| 148 message_start = self.get_message_start(formatted, record.levelno) | |
| 149 formatted = message_start + formatted | |
| 150 | |
| 151 prefix = '' | |
| 152 if self.add_timestamp: | |
| 153 # TODO: Use Formatter.default_time_format after dropping PY2. | |
| 154 t = self.formatTime(record, "%Y-%m-%dT%H:%M:%S") | |
| 155 prefix = '%s,%03d ' % (t, record.msecs) | |
| 156 prefix += " " * get_indentation() | |
| 157 formatted = "".join([ | |
| 158 prefix + line | |
| 159 for line in formatted.splitlines(True) | |
| 160 ]) | |
| 161 return formatted | |
| 162 | |
| 163 | |
| 164 def _color_wrap(*colors): | |
| 165 def wrapped(inp): | |
| 166 return "".join(list(colors) + [inp, colorama.Style.RESET_ALL]) | |
| 167 return wrapped | |
| 168 | |
| 169 | |
| 170 class ColorizedStreamHandler(logging.StreamHandler): | |
| 171 | |
| 172 # Don't build up a list of colors if we don't have colorama | |
| 173 if colorama: | |
| 174 COLORS = [ | |
| 175 # This needs to be in order from highest logging level to lowest. | |
| 176 (logging.ERROR, _color_wrap(Fore.RED)), | |
| 177 (logging.WARNING, _color_wrap(Fore.YELLOW)), | |
| 178 ] | |
| 179 else: | |
| 180 COLORS = [] | |
| 181 | |
| 182 def __init__(self, stream=None, no_color=None): | |
| 183 logging.StreamHandler.__init__(self, stream) | |
| 184 self._no_color = no_color | |
| 185 | |
| 186 if WINDOWS and colorama: | |
| 187 self.stream = colorama.AnsiToWin32(self.stream) | |
| 188 | |
| 189 def _using_stdout(self): | |
| 190 """ | |
| 191 Return whether the handler is using sys.stdout. | |
| 192 """ | |
| 193 if WINDOWS and colorama: | |
| 194 # Then self.stream is an AnsiToWin32 object. | |
| 195 return self.stream.wrapped is sys.stdout | |
| 196 | |
| 197 return self.stream is sys.stdout | |
| 198 | |
| 199 def should_color(self): | |
| 200 # Don't colorize things if we do not have colorama or if told not to | |
| 201 if not colorama or self._no_color: | |
| 202 return False | |
| 203 | |
| 204 real_stream = ( | |
| 205 self.stream if not isinstance(self.stream, colorama.AnsiToWin32) | |
| 206 else self.stream.wrapped | |
| 207 ) | |
| 208 | |
| 209 # If the stream is a tty we should color it | |
| 210 if hasattr(real_stream, "isatty") and real_stream.isatty(): | |
| 211 return True | |
| 212 | |
| 213 # If we have an ANSI term we should color it | |
| 214 if os.environ.get("TERM") == "ANSI": | |
| 215 return True | |
| 216 | |
| 217 # If anything else we should not color it | |
| 218 return False | |
| 219 | |
| 220 def format(self, record): | |
| 221 msg = logging.StreamHandler.format(self, record) | |
| 222 | |
| 223 if self.should_color(): | |
| 224 for level, color in self.COLORS: | |
| 225 if record.levelno >= level: | |
| 226 msg = color(msg) | |
| 227 break | |
| 228 | |
| 229 return msg | |
| 230 | |
| 231 # The logging module says handleError() can be customized. | |
| 232 def handleError(self, record): | |
| 233 exc_class, exc = sys.exc_info()[:2] | |
| 234 # If a broken pipe occurred while calling write() or flush() on the | |
| 235 # stdout stream in logging's Handler.emit(), then raise our special | |
| 236 # exception so we can handle it in main() instead of logging the | |
| 237 # broken pipe error and continuing. | |
| 238 if (exc_class and self._using_stdout() and | |
| 239 _is_broken_pipe_error(exc_class, exc)): | |
| 240 raise BrokenStdoutLoggingError() | |
| 241 | |
| 242 return super(ColorizedStreamHandler, self).handleError(record) | |
| 243 | |
| 244 | |
| 245 class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): | |
| 246 | |
| 247 def _open(self): | |
| 248 ensure_dir(os.path.dirname(self.baseFilename)) | |
| 249 return logging.handlers.RotatingFileHandler._open(self) | |
| 250 | |
| 251 | |
| 252 class MaxLevelFilter(Filter): | |
| 253 | |
| 254 def __init__(self, level): | |
| 255 self.level = level | |
| 256 | |
| 257 def filter(self, record): | |
| 258 return record.levelno < self.level | |
| 259 | |
| 260 | |
| 261 class ExcludeLoggerFilter(Filter): | |
| 262 | |
| 263 """ | |
| 264 A logging Filter that excludes records from a logger (or its children). | |
| 265 """ | |
| 266 | |
| 267 def filter(self, record): | |
| 268 # The base Filter class allows only records from a logger (or its | |
| 269 # children). | |
| 270 return not super(ExcludeLoggerFilter, self).filter(record) | |
| 271 | |
| 272 | |
| 273 def setup_logging(verbosity, no_color, user_log_file): | |
| 274 """Configures and sets up all of the logging | |
| 275 | |
| 276 Returns the requested logging level, as its integer value. | |
| 277 """ | |
| 278 | |
| 279 # Determine the level to be logging at. | |
| 280 if verbosity >= 1: | |
| 281 level = "DEBUG" | |
| 282 elif verbosity == -1: | |
| 283 level = "WARNING" | |
| 284 elif verbosity == -2: | |
| 285 level = "ERROR" | |
| 286 elif verbosity <= -3: | |
| 287 level = "CRITICAL" | |
| 288 else: | |
| 289 level = "INFO" | |
| 290 | |
| 291 level_number = getattr(logging, level) | |
| 292 | |
| 293 # The "root" logger should match the "console" level *unless* we also need | |
| 294 # to log to a user log file. | |
| 295 include_user_log = user_log_file is not None | |
| 296 if include_user_log: | |
| 297 additional_log_file = user_log_file | |
| 298 root_level = "DEBUG" | |
| 299 else: | |
| 300 additional_log_file = "/dev/null" | |
| 301 root_level = level | |
| 302 | |
| 303 # Disable any logging besides WARNING unless we have DEBUG level logging | |
| 304 # enabled for vendored libraries. | |
| 305 vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG" | |
| 306 | |
| 307 # Shorthands for clarity | |
| 308 log_streams = { | |
| 309 "stdout": "ext://sys.stdout", | |
| 310 "stderr": "ext://sys.stderr", | |
| 311 } | |
| 312 handler_classes = { | |
| 313 "stream": "pip._internal.utils.logging.ColorizedStreamHandler", | |
| 314 "file": "pip._internal.utils.logging.BetterRotatingFileHandler", | |
| 315 } | |
| 316 handlers = ["console", "console_errors", "console_subprocess"] + ( | |
| 317 ["user_log"] if include_user_log else [] | |
| 318 ) | |
| 319 | |
| 320 logging.config.dictConfig({ | |
| 321 "version": 1, | |
| 322 "disable_existing_loggers": False, | |
| 323 "filters": { | |
| 324 "exclude_warnings": { | |
| 325 "()": "pip._internal.utils.logging.MaxLevelFilter", | |
| 326 "level": logging.WARNING, | |
| 327 }, | |
| 328 "restrict_to_subprocess": { | |
| 329 "()": "logging.Filter", | |
| 330 "name": subprocess_logger.name, | |
| 331 }, | |
| 332 "exclude_subprocess": { | |
| 333 "()": "pip._internal.utils.logging.ExcludeLoggerFilter", | |
| 334 "name": subprocess_logger.name, | |
| 335 }, | |
| 336 }, | |
| 337 "formatters": { | |
| 338 "indent": { | |
| 339 "()": IndentingFormatter, | |
| 340 "format": "%(message)s", | |
| 341 }, | |
| 342 "indent_with_timestamp": { | |
| 343 "()": IndentingFormatter, | |
| 344 "format": "%(message)s", | |
| 345 "add_timestamp": True, | |
| 346 }, | |
| 347 }, | |
| 348 "handlers": { | |
| 349 "console": { | |
| 350 "level": level, | |
| 351 "class": handler_classes["stream"], | |
| 352 "no_color": no_color, | |
| 353 "stream": log_streams["stdout"], | |
| 354 "filters": ["exclude_subprocess", "exclude_warnings"], | |
| 355 "formatter": "indent", | |
| 356 }, | |
| 357 "console_errors": { | |
| 358 "level": "WARNING", | |
| 359 "class": handler_classes["stream"], | |
| 360 "no_color": no_color, | |
| 361 "stream": log_streams["stderr"], | |
| 362 "filters": ["exclude_subprocess"], | |
| 363 "formatter": "indent", | |
| 364 }, | |
| 365 # A handler responsible for logging to the console messages | |
| 366 # from the "subprocessor" logger. | |
| 367 "console_subprocess": { | |
| 368 "level": level, | |
| 369 "class": handler_classes["stream"], | |
| 370 "no_color": no_color, | |
| 371 "stream": log_streams["stderr"], | |
| 372 "filters": ["restrict_to_subprocess"], | |
| 373 "formatter": "indent", | |
| 374 }, | |
| 375 "user_log": { | |
| 376 "level": "DEBUG", | |
| 377 "class": handler_classes["file"], | |
| 378 "filename": additional_log_file, | |
| 379 "delay": True, | |
| 380 "formatter": "indent_with_timestamp", | |
| 381 }, | |
| 382 }, | |
| 383 "root": { | |
| 384 "level": root_level, | |
| 385 "handlers": handlers, | |
| 386 }, | |
| 387 "loggers": { | |
| 388 "pip._vendor": { | |
| 389 "level": vendored_log_level | |
| 390 } | |
| 391 }, | |
| 392 }) | |
| 393 | |
| 394 return level_number |
