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 |