# HG changeset patch # User Pulkit Goyal <7895pulkit@gmail.com> # Date 2020-12-03 11:48:49 # Node ID ac9de799d390911d9858a599208fa5e82a7fef22 # Parent e0866c047e644a4480a3147dd510acc873c87202 commandserver: handle IOError related to flushing of streams After dispatch, without chg we have handling of flushing of streams and exception handling related to it. The exception handling part is important because there can be exceptions when flushing fout or ferr. One such case is in `test-basic.t` which was failing on python3+chg without this patch as this handling was missing from chg. Failure can be seen at https://foss.heptapod.net/octobus/mercurial-devel/-/jobs/128399 Honestly I am not sure which one of `chgserver.py` or `commandserver.py` the change should go in. Differential Revision: https://phab.mercurial-scm.org/D9517 diff --git a/mercurial/commandserver.py b/mercurial/commandserver.py --- a/mercurial/commandserver.py +++ b/mercurial/commandserver.py @@ -355,7 +355,18 @@ class server(object): ) try: - ret = self._dispatchcommand(req) & 255 + err = None + try: + status = self._dispatchcommand(req) + except error.StdioError as e: + status = -1 + err = e + + retval = dispatch.closestdio(req.ui, err) + if retval: + status = retval + + ret = status & 255 # If shutdown-on-interrupt is off, it's important to write the # result code *after* SIGINT handler removed. If the result code # were lost, the client wouldn't be able to continue processing. diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py --- a/mercurial/dispatch.py +++ b/mercurial/dispatch.py @@ -104,6 +104,35 @@ class request(object): raise exc +def closestdio(ui, err): + status = None + # In all cases we try to flush stdio streams. + if util.safehasattr(ui, b'fout'): + assert ui is not None # help pytype + assert ui.fout is not None # help pytype + try: + ui.fout.flush() + except IOError as e: + err = e + status = -1 + + if util.safehasattr(ui, b'ferr'): + assert ui is not None # help pytype + assert ui.ferr is not None # help pytype + try: + if err is not None and err.errno != errno.EPIPE: + ui.ferr.write( + b'abort: %s\n' % encoding.strtolocal(err.strerror) + ) + ui.ferr.flush() + # There's not much we can do about an I/O error here. So (possibly) + # change the status code and move on. + except IOError: + status = -1 + + return status + + def run(): """run the command in sys.argv""" try: @@ -117,30 +146,9 @@ def run(): err = e status = -1 - # In all cases we try to flush stdio streams. - if util.safehasattr(req.ui, b'fout'): - assert req.ui is not None # help pytype - assert req.ui.fout is not None # help pytype - try: - req.ui.fout.flush() - except IOError as e: - err = e - status = -1 - - if util.safehasattr(req.ui, b'ferr'): - assert req.ui is not None # help pytype - assert req.ui.ferr is not None # help pytype - try: - if err is not None and err.errno != errno.EPIPE: - req.ui.ferr.write( - b'abort: %s\n' % encoding.strtolocal(err.strerror) - ) - req.ui.ferr.flush() - # There's not much we can do about an I/O error here. So (possibly) - # change the status code and move on. - except IOError: - status = -1 - + ret = closestdio(req.ui, err) + if ret: + status = ret _silencestdio() except KeyboardInterrupt: # Catch early/late KeyboardInterrupt as last ditch. Here nothing will