dispatch: move part of callcatch to scmutil
Per discussion at 39149b6036e6 [1], we need "callcatch" in worker.py. Move
it to scmutil.py to avoid cycles.
Note that dispatch's callcatch handles some additional high-level exceptions
related to config parsing, and commands. Moving them to scmutil will make
scmutil depend on "commands" or require "_formatparse" and "_getsimilar"
(and "difflib") to be moved as well. In the worker use-case, it is forked
when config and commands are fully loaded. So it should not care about those
exceptions.
[1]: https://www.mercurial-scm.org/pipermail/mercurial-devel/2016-August/087116.html
--- a/mercurial/dispatch.py Wed Nov 23 00:03:11 2016 +0530
+++ b/mercurial/dispatch.py Thu Nov 24 00:48:40 2016 +0000
@@ -15,7 +15,6 @@
import re
import shlex
import signal
-import socket
import sys
import time
import traceback
@@ -38,6 +37,7 @@
profiling,
pycompat,
revset,
+ scmutil,
templatefilters,
templatekw,
templater,
@@ -218,30 +218,15 @@
return callcatch(ui, _runcatchfunc)
def callcatch(ui, func):
- """call func() with global exception handling
-
- return func() if no exception happens. otherwise do some error handling
- and return an exit code accordingly.
+ """like scmutil.callcatch but handles more high-level exceptions about
+ config parsing and commands. besides, use handlecommandexception to handle
+ uncaught exceptions.
"""
try:
- return func()
- # Global exception handling, alphabetically
- # Mercurial-specific first, followed by built-in and library exceptions
+ return scmutil.callcatch(ui, func)
except error.AmbiguousCommand as inst:
ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
(inst.args[0], " ".join(inst.args[1])))
- except error.ParseError as inst:
- _formatparse(ui.warn, inst)
- return -1
- except error.LockHeld as inst:
- if inst.errno == errno.ETIMEDOUT:
- reason = _('timed out waiting for lock held by %s') % inst.locker
- else:
- reason = _('lock held by %s') % inst.locker
- ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
- except error.LockUnavailable as inst:
- ui.warn(_("abort: could not lock %s: %s\n") %
- (inst.desc or inst.filename, inst.strerror))
except error.CommandError as inst:
if inst.args[0]:
ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
@@ -249,34 +234,9 @@
else:
ui.warn(_("hg: %s\n") % inst.args[1])
commands.help_(ui, 'shortlist')
- except error.OutOfBandError as inst:
- if inst.args:
- msg = _("abort: remote error:\n")
- else:
- msg = _("abort: remote error\n")
- ui.warn(msg)
- if inst.args:
- ui.warn(''.join(inst.args))
- if inst.hint:
- ui.warn('(%s)\n' % inst.hint)
- except error.RepoError as inst:
- ui.warn(_("abort: %s!\n") % inst)
- if inst.hint:
- ui.warn(_("(%s)\n") % inst.hint)
- except error.ResponseError as inst:
- ui.warn(_("abort: %s") % inst.args[0])
- if not isinstance(inst.args[1], basestring):
- ui.warn(" %r\n" % (inst.args[1],))
- elif not inst.args[1]:
- ui.warn(_(" empty string\n"))
- else:
- ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
- except error.CensoredNodeError as inst:
- ui.warn(_("abort: file censored %s!\n") % inst)
- except error.RevlogError as inst:
- ui.warn(_("abort: %s!\n") % inst)
- except error.SignalInterrupt:
- ui.warn(_("killed!\n"))
+ except error.ParseError as inst:
+ _formatparse(ui.warn, inst)
+ return -1
except error.UnknownCommand as inst:
ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
try:
@@ -292,61 +252,11 @@
suggested = True
if not suggested:
commands.help_(ui, 'shortlist')
- except error.InterventionRequired as inst:
- ui.warn("%s\n" % inst)
- if inst.hint:
- ui.warn(_("(%s)\n") % inst.hint)
- return 1
- except error.Abort as inst:
- ui.warn(_("abort: %s\n") % inst)
- if inst.hint:
- ui.warn(_("(%s)\n") % inst.hint)
- except ImportError as inst:
- ui.warn(_("abort: %s!\n") % inst)
- m = str(inst).split()[-1]
- if m in "mpatch bdiff".split():
- ui.warn(_("(did you forget to compile extensions?)\n"))
- elif m in "zlib".split():
- ui.warn(_("(is your Python install correct?)\n"))
- except IOError as inst:
- if util.safehasattr(inst, "code"):
- ui.warn(_("abort: %s\n") % inst)
- elif util.safehasattr(inst, "reason"):
- try: # usually it is in the form (errno, strerror)
- reason = inst.reason.args[1]
- except (AttributeError, IndexError):
- # it might be anything, for example a string
- reason = inst.reason
- if isinstance(reason, unicode):
- # SSLError of Python 2.7.9 contains a unicode
- reason = reason.encode(encoding.encoding, 'replace')
- ui.warn(_("abort: error: %s\n") % reason)
- elif (util.safehasattr(inst, "args")
- and inst.args and inst.args[0] == errno.EPIPE):
- pass
- elif getattr(inst, "strerror", None):
- if getattr(inst, "filename", None):
- ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
- else:
- ui.warn(_("abort: %s\n") % inst.strerror)
- else:
- raise
- except OSError as inst:
- if getattr(inst, "filename", None) is not None:
- ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
- else:
- ui.warn(_("abort: %s\n") % inst.strerror)
+ except IOError:
+ raise
except KeyboardInterrupt:
raise
- except MemoryError:
- ui.warn(_("abort: out of memory\n"))
- except SystemExit as inst:
- # Commands shouldn't sys.exit directly, but give a return code.
- # Just in case catch this and and pass exit code to caller.
- return inst.code
- except socket.error as inst:
- ui.warn(_("abort: %s\n") % inst.args[-1])
- except: # perhaps re-raises
+ except: # probably re-raises
if not handlecommandexception(ui):
raise
--- a/mercurial/scmutil.py Wed Nov 23 00:03:11 2016 +0530
+++ b/mercurial/scmutil.py Thu Nov 24 00:48:40 2016 +0000
@@ -14,6 +14,7 @@
import os
import re
import shutil
+import socket
import stat
import tempfile
import threading
@@ -141,6 +142,108 @@
else:
ui.status(_("no changes found\n"))
+def callcatch(ui, func):
+ """call func() with global exception handling
+
+ return func() if no exception happens. otherwise do some error handling
+ and return an exit code accordingly. does not handle all exceptions.
+ """
+ try:
+ return func()
+ # Global exception handling, alphabetically
+ # Mercurial-specific first, followed by built-in and library exceptions
+ except error.LockHeld as inst:
+ if inst.errno == errno.ETIMEDOUT:
+ reason = _('timed out waiting for lock held by %s') % inst.locker
+ else:
+ reason = _('lock held by %s') % inst.locker
+ ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
+ except error.LockUnavailable as inst:
+ ui.warn(_("abort: could not lock %s: %s\n") %
+ (inst.desc or inst.filename, inst.strerror))
+ except error.OutOfBandError as inst:
+ if inst.args:
+ msg = _("abort: remote error:\n")
+ else:
+ msg = _("abort: remote error\n")
+ ui.warn(msg)
+ if inst.args:
+ ui.warn(''.join(inst.args))
+ if inst.hint:
+ ui.warn('(%s)\n' % inst.hint)
+ except error.RepoError as inst:
+ ui.warn(_("abort: %s!\n") % inst)
+ if inst.hint:
+ ui.warn(_("(%s)\n") % inst.hint)
+ except error.ResponseError as inst:
+ ui.warn(_("abort: %s") % inst.args[0])
+ if not isinstance(inst.args[1], basestring):
+ ui.warn(" %r\n" % (inst.args[1],))
+ elif not inst.args[1]:
+ ui.warn(_(" empty string\n"))
+ else:
+ ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
+ except error.CensoredNodeError as inst:
+ ui.warn(_("abort: file censored %s!\n") % inst)
+ except error.RevlogError as inst:
+ ui.warn(_("abort: %s!\n") % inst)
+ except error.SignalInterrupt:
+ ui.warn(_("killed!\n"))
+ except error.InterventionRequired as inst:
+ ui.warn("%s\n" % inst)
+ if inst.hint:
+ ui.warn(_("(%s)\n") % inst.hint)
+ return 1
+ except error.Abort as inst:
+ ui.warn(_("abort: %s\n") % inst)
+ if inst.hint:
+ ui.warn(_("(%s)\n") % inst.hint)
+ except ImportError as inst:
+ ui.warn(_("abort: %s!\n") % inst)
+ m = str(inst).split()[-1]
+ if m in "mpatch bdiff".split():
+ ui.warn(_("(did you forget to compile extensions?)\n"))
+ elif m in "zlib".split():
+ ui.warn(_("(is your Python install correct?)\n"))
+ except IOError as inst:
+ if util.safehasattr(inst, "code"):
+ ui.warn(_("abort: %s\n") % inst)
+ elif util.safehasattr(inst, "reason"):
+ try: # usually it is in the form (errno, strerror)
+ reason = inst.reason.args[1]
+ except (AttributeError, IndexError):
+ # it might be anything, for example a string
+ reason = inst.reason
+ if isinstance(reason, unicode):
+ # SSLError of Python 2.7.9 contains a unicode
+ reason = reason.encode(encoding.encoding, 'replace')
+ ui.warn(_("abort: error: %s\n") % reason)
+ elif (util.safehasattr(inst, "args")
+ and inst.args and inst.args[0] == errno.EPIPE):
+ pass
+ elif getattr(inst, "strerror", None):
+ if getattr(inst, "filename", None):
+ ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
+ else:
+ ui.warn(_("abort: %s\n") % inst.strerror)
+ else:
+ raise
+ except OSError as inst:
+ if getattr(inst, "filename", None) is not None:
+ ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
+ else:
+ ui.warn(_("abort: %s\n") % inst.strerror)
+ except MemoryError:
+ ui.warn(_("abort: out of memory\n"))
+ except SystemExit as inst:
+ # Commands shouldn't sys.exit directly, but give a return code.
+ # Just in case catch this and and pass exit code to caller.
+ return inst.code
+ except socket.error as inst:
+ ui.warn(_("abort: %s\n") % inst.args[-1])
+
+ return -1
+
def checknewlabel(repo, lbl, kind):
# Do not use the "kind" parameter in ui output.
# It makes strings difficult to translate.
--- a/tests/test-devel-warnings.t Wed Nov 23 00:03:11 2016 +0530
+++ b/tests/test-devel-warnings.t Thu Nov 24 00:48:40 2016 +0000
@@ -92,6 +92,7 @@
*/mercurial/dispatch.py:* in dispatch (glob)
*/mercurial/dispatch.py:* in _runcatch (glob)
*/mercurial/dispatch.py:* in callcatch (glob)
+ */mercurial/scmutil.py* in callcatch (glob)
*/mercurial/dispatch.py:* in _runcatchfunc (glob)
*/mercurial/dispatch.py:* in _dispatch (glob)
*/mercurial/dispatch.py:* in runcommand (glob)
@@ -127,6 +128,7 @@
*/mercurial/dispatch.py:* in dispatch (glob)
*/mercurial/dispatch.py:* in _runcatch (glob)
*/mercurial/dispatch.py:* in callcatch (glob)
+ */mercurial/scmutil.py* in callcatch (glob)
*/mercurial/dispatch.py:* in _runcatchfunc (glob)
*/mercurial/dispatch.py:* in _dispatch (glob)
*/mercurial/dispatch.py:* in runcommand (glob)
@@ -150,6 +152,7 @@
*/mercurial/dispatch.py:* in dispatch (glob)
*/mercurial/dispatch.py:* in _runcatch (glob)
*/mercurial/dispatch.py:* in callcatch (glob)
+ */mercurial/scmutil.py* in callcatch (glob)
*/mercurial/dispatch.py:* in _runcatchfunc (glob)
*/mercurial/dispatch.py:* in _dispatch (glob)
*/mercurial/dispatch.py:* in runcommand (glob)