diff --git a/click/core.py b/click/core.py index 30aec62e6..cb453ba65 100644 --- a/click/core.py +++ b/click/core.py @@ -7,7 +7,8 @@ from functools import update_wrapper from .types import convert_type, IntRange, BOOL -from .utils import make_str, make_default_short_help, echo, get_os_args +from .utils import PacifyFlushWrapper, make_str, make_default_short_help, \ + echo, get_os_args from .exceptions import ClickException, UsageError, BadParameter, Abort, \ MissingParameter, Exit from .termui import prompt, confirm, style @@ -732,6 +733,8 @@ def main(self, args=None, prog_name=None, complete_var=None, sys.exit(e.exit_code) except IOError as e: if e.errno == errno.EPIPE: + sys.stdout = PacifyFlushWrapper(sys.stdout) + sys.stderr = PacifyFlushWrapper(sys.stderr) sys.exit(1) else: raise diff --git a/click/utils.py b/click/utils.py index 9f175eb27..fc84369fc 100644 --- a/click/utils.py +++ b/click/utils.py @@ -414,3 +414,27 @@ def get_app_dir(app_name, roaming=True, force_posix=False): return os.path.join( os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), _posixify(app_name)) + + +class PacifyFlushWrapper(object): + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped): + self.wrapped = wrapped + + def flush(self): + try: + self.wrapped.flush() + except IOError as e: + import errno + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr): + return getattr(self.wrapped, attr)