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