# HG changeset patch # User Martin von Zweigbergk # Date 2021-10-08 20:36:02 # Node ID 2f2107c01dee8d9cd0ded70ea319a72e74738ad0 # Parent 8fae2cc6ee8688b882d381d2b8d62e17b4c8411d dispatch: ignore failure to flush ui When the pager dies, we get a `SIGPIPE`. That causes `error.SignalInterrupt` to be raised ` (from `ui._catchterm()`). Any further writes or flushes will cause further `SIGPIPE`s and furhter `error.SignalInterrupt`. If we write or flush outside of the try/except that handle `KeyboardInterrupt` (which `error.SignalInterrupt` is a subclass of), then control will escape from the `dispatch` module. Let's fix that by ignoring errors from flushing the ui. I would have rather fixed this by restoring the stdout and stderr streams when the pager dies, but it gets complicated because of multiple ui instances (ui/lui) and different pager setups between regular hg and chg. This changes a test in `test-pager.t`, but I don't understand why. I would have thought that all the output from the command should have gone to the broken pager. Differential Revision: https://phab.mercurial-scm.org/D11627 diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py --- a/mercurial/dispatch.py +++ b/mercurial/dispatch.py @@ -252,9 +252,14 @@ def dispatch(req): err = e status = -1 - ret = _flushstdio(req.ui, err) - if ret and not status: - status = ret + # Somehow we have to catcht he exception here; catching it inside + # _flushstdio() doesn't work. + try: + ret = _flushstdio(req.ui, err) + if ret and not status: + status = ret + except BaseException: + pass return status @@ -314,7 +319,10 @@ def _rundispatch(req): ret = -1 finally: duration = util.timer() - starttime - req.ui.flush() # record blocked times + try: + req.ui.flush() # record blocked times + except BaseException: + pass if req.ui.logblockedtimes: req.ui._blockedtimes[b'command_duration'] = duration * 1000 req.ui.log( @@ -338,7 +346,10 @@ def _rundispatch(req): except: # exiting, so no re-raises ret = ret or -1 # do flush again since ui.log() and exit handlers may write to ui - req.ui.flush() + try: + req.ui.flush() + except BaseException: + pass return ret @@ -459,7 +470,10 @@ def _runcatch(req): try: return _dispatch(req) finally: - ui.flush() + try: + ui.flush() # record blocked times + except BaseException: + pass except: # re-raises # enter the debugger when we hit an exception if req.earlyoptions[b'debugger']: diff --git a/tests/test-pager.t b/tests/test-pager.t --- a/tests/test-pager.t +++ b/tests/test-pager.t @@ -219,8 +219,7 @@ use shell=True in the subprocess call: #endif A complicated pager command gets worse behavior. Bonus points if you can -improve this. Windows apparently does this better, but only sometimes? -#if windows +improve this. $ hg log --limit 3 \ > --config pager.pager='this-command-better-never-exist --seriously' \ > 2>/dev/null || true @@ -240,11 +239,6 @@ improve this. Windows apparently does th date: Thu Jan 01 00:00:00 1970 +0000 (?) summary: modify a 8 (?) (?) -#else - $ hg log --limit 3 \ - > --config pager.pager='this-command-better-never-exist --seriously' \ - > 2>/dev/null || true -#endif Pager works with shell aliases.