|
1 | 1 | from __future__ import absolute_import
|
2 | 2 |
|
3 | 3 | import contextlib
|
| 4 | +import errno |
4 | 5 | import logging
|
5 | 6 | import logging.handlers
|
6 | 7 | import os
|
| 8 | +import sys |
| 9 | + |
| 10 | +from pip._vendor.six import PY2 |
7 | 11 |
|
8 | 12 | from pip._internal.utils.compat import WINDOWS
|
9 | 13 | from pip._internal.utils.misc import ensure_dir
|
@@ -33,6 +37,23 @@ class BrokenStdoutLoggingError(Exception):
|
33 | 37 | pass
|
34 | 38 |
|
35 | 39 |
|
| 40 | +if PY2: |
| 41 | + # BrokenPipeError does not exist in Python 2. |
| 42 | + def _is_broken_pipe_error(exc_class, exc): |
| 43 | + """ |
| 44 | + Return whether an exception is a broken pipe error. |
| 45 | +
|
| 46 | + Args: |
| 47 | + exc_class: an exception class. |
| 48 | + exc: an exception instance. |
| 49 | + """ |
| 50 | + return (exc_class is IOError and exc.errno == errno.EPIPE) |
| 51 | + |
| 52 | +else: |
| 53 | + def _is_broken_pipe_error(exc_class, exc): |
| 54 | + return (exc_class is BrokenPipeError) # noqa: F821 |
| 55 | + |
| 56 | + |
36 | 57 | @contextlib.contextmanager
|
37 | 58 | def indent_log(num=2):
|
38 | 59 | """
|
@@ -90,6 +111,16 @@ def __init__(self, stream=None, no_color=None):
|
90 | 111 | if WINDOWS and colorama:
|
91 | 112 | self.stream = colorama.AnsiToWin32(self.stream)
|
92 | 113 |
|
| 114 | + def _using_stdout(self): |
| 115 | + """ |
| 116 | + Return whether the handler is using sys.stdout. |
| 117 | + """ |
| 118 | + if WINDOWS and colorama: |
| 119 | + # Then self.stream is an AnsiToWin32 object. |
| 120 | + return self.stream.wrapped is sys.stdout |
| 121 | + |
| 122 | + return self.stream is sys.stdout |
| 123 | + |
93 | 124 | def should_color(self):
|
94 | 125 | # Don't colorize things if we do not have colorama or if told not to
|
95 | 126 | if not colorama or self._no_color:
|
@@ -122,6 +153,19 @@ def format(self, record):
|
122 | 153 |
|
123 | 154 | return msg
|
124 | 155 |
|
| 156 | + # The logging module says handleError() can be customized. |
| 157 | + def handleError(self, record): |
| 158 | + exc_class, exc = sys.exc_info()[:2] |
| 159 | + # If a broken pipe occurred while calling write() or flush() on the |
| 160 | + # stdout stream in logging's Handler.emit(), then raise our special |
| 161 | + # exception so we can handle it in main() instead of logging the |
| 162 | + # broken pipe error and continuing. |
| 163 | + if (exc_class and self._using_stdout() and |
| 164 | + _is_broken_pipe_error(exc_class, exc)): |
| 165 | + raise BrokenStdoutLoggingError() |
| 166 | + |
| 167 | + return super(ColorizedStreamHandler, self).handleError(record) |
| 168 | + |
125 | 169 |
|
126 | 170 | class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler):
|
127 | 171 |
|
|
0 commit comments