Mercurial > public > mercurial-scm > hg-stable
diff mercurial/dispatch.py @ 32054:616e788321cc stable 4.2-rc
freeze: merge default into stable for 4.2 code freeze
author | Augie Fackler <augie@google.com> |
---|---|
date | Tue, 18 Apr 2017 12:24:34 -0400 |
parents | 77eaf9539499 cde72a195f32 |
children | f928d53b687c 1208b74841ff |
line wrap: on
line diff
--- a/mercurial/dispatch.py Tue Apr 18 11:22:42 2017 -0400 +++ b/mercurial/dispatch.py Tue Apr 18 12:24:34 2017 -0400 @@ -7,7 +7,6 @@ from __future__ import absolute_import, print_function -import atexit import difflib import errno import getopt @@ -33,6 +32,7 @@ extensions, fancyopts, fileset, + help, hg, hook, profiling, @@ -58,9 +58,41 @@ self.fout = fout self.ferr = ferr + def _runexithandlers(self): + exc = None + handlers = self.ui._exithandlers + try: + while handlers: + func, args, kwargs = handlers.pop() + try: + func(*args, **kwargs) + except: # re-raises below + if exc is None: + exc = sys.exc_info()[1] + self.ui.warn(('error in exit handlers:\n')) + self.ui.traceback(force=True) + finally: + if exc is not None: + raise exc + def run(): "run the command in sys.argv" - sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255) + req = request(pycompat.sysargv[1:]) + err = None + try: + status = (dispatch(req) or 0) & 255 + except error.StdioError as err: + status = -1 + if util.safehasattr(req.ui, 'fout'): + try: + req.ui.fout.close() + except IOError as err: + status = -1 + if util.safehasattr(req.ui, 'ferr'): + if err is not None and err.errno != errno.EPIPE: + req.ui.ferr.write('abort: %s\n' % err.strerror) + req.ui.ferr.close() + sys.exit(status & 255) def _getsimilar(symbols, value): sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio() @@ -91,6 +123,9 @@ if inst.hint: write(_("(%s)\n") % inst.hint) +def _formatargs(args): + return ' '.join(util.shellquote(a) for a in args) + def dispatch(req): "run the command specified in req.args" if req.ferr: @@ -122,23 +157,34 @@ _formatparse(ferr.write, inst) return -1 - msg = ' '.join(' ' in a and repr(a) or a for a in req.args) - starttime = time.time() + msg = _formatargs(req.args) + starttime = util.timer() ret = None try: ret = _runcatch(req) except KeyboardInterrupt: try: req.ui.warn(_("interrupted!\n")) + except error.SignalInterrupt: + # maybe pager would quit without consuming all the output, and + # SIGPIPE was raised. we cannot print anything in this case. + pass except IOError as inst: if inst.errno != errno.EPIPE: raise ret = -1 finally: - duration = time.time() - starttime + duration = util.timer() - starttime req.ui.flush() + if req.ui.logblockedtimes: + req.ui._blockedtimes['command_duration'] = duration * 1000 + req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes) req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n", msg, ret or 0, duration) + try: + req._runexithandlers() + except: # exiting, so no re-raises + ret = ret or -1 return ret def _runcatch(req): @@ -244,12 +290,11 @@ if '--debugger' in req.args: traceback.print_exc() debugmortem[debugger](sys.exc_info()[2]) - ui.traceback() raise - return callcatch(ui, _runcatchfunc) + return _callcatch(ui, _runcatchfunc) -def callcatch(ui, func): +def _callcatch(ui, func): """like scmutil.callcatch but handles more high-level exceptions about config parsing and commands. besides, use handlecommandexception to handle uncaught exceptions. @@ -261,28 +306,35 @@ (inst.args[0], " ".join(inst.args[1]))) except error.CommandError as inst: if inst.args[0]: + ui.pager('help') ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1])) commands.help_(ui, inst.args[0], full=False, command=True) else: + ui.pager('help') ui.warn(_("hg: %s\n") % inst.args[1]) commands.help_(ui, 'shortlist') 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]) + nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0] try: # check if the command is in a disabled extension # (but don't check for extensions themselves) - commands.help_(ui, inst.args[0], unknowncmd=True) + formatted = help.formattedhelp(ui, inst.args[0], unknowncmd=True) + ui.warn(nocmdmsg) + ui.write(formatted) except (error.UnknownCommand, error.Abort): suggested = False if len(inst.args) == 2: sim = _getsimilar(inst.args[1], inst.args[0]) if sim: + ui.warn(nocmdmsg) _reportsimilar(ui.warn, sim) suggested = True if not suggested: + ui.pager('help') + ui.warn(nocmdmsg) commands.help_(ui, 'shortlist') except IOError: raise @@ -306,7 +358,7 @@ if num < len(givenargs): return givenargs[num] raise error.Abort(_('too few arguments for command alias')) - cmd = re.sub(r'\$(\d+|\$)', replacer, cmd) + cmd = re.sub(br'\$(\d+|\$)', replacer, cmd) givenargs = [x for i, x in enumerate(givenargs) if i not in nums] args = pycompat.shlexsplit(cmd) @@ -376,7 +428,8 @@ return '' cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:]) cmd = aliasinterpolate(self.name, args, cmd) - return ui.system(cmd, environ=env) + return ui.system(cmd, environ=env, + blockedtag='alias_%s' % self.name) self.fn = fn return @@ -418,7 +471,7 @@ @property def args(self): - args = map(util.expandpath, self.givenargs) + args = pycompat.maplist(util.expandpath, self.givenargs) return aliasargs(self.fn, args) def __getattr__(self, name): @@ -491,7 +544,8 @@ args = aliasargs(entry[0], args) defaults = ui.config("defaults", cmd) if defaults: - args = map(util.expandpath, pycompat.shlexsplit(defaults)) + args + args = pycompat.maplist( + util.expandpath, pycompat.shlexsplit(defaults)) + args c = list(entry[1]) else: cmd = None @@ -686,107 +740,122 @@ rpath = _earlygetopt(["-R", "--repository", "--repo"], args) path, lui = _getlocal(ui, rpath) - # Configure extensions in phases: uisetup, extsetup, cmdtable, and - # reposetup. Programs like TortoiseHg will call _dispatch several - # times so we keep track of configured extensions in _loaded. - extensions.loadall(lui) - exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded] - # Propagate any changes to lui.__class__ by extensions - ui.__class__ = lui.__class__ - - # (uisetup and extsetup are handled in extensions.loadall) - - for name, module in exts: - for objname, loadermod, loadername in extraloaders: - extraobj = getattr(module, objname, None) - if extraobj is not None: - getattr(loadermod, loadername)(ui, name, extraobj) - _loaded.add(name) - - # (reposetup is handled in hg.repository) - # Side-effect of accessing is debugcommands module is guaranteed to be # imported and commands.table is populated. debugcommands.command - addaliases(lui, commands.table) - - # All aliases and commands are completely defined, now. - # Check abbreviation/ambiguity of shell alias. - shellaliasfn = _checkshellalias(lui, ui, args) - if shellaliasfn: - with profiling.maybeprofile(lui): - return shellaliasfn() - - # check for fallback encoding - fallback = lui.config('ui', 'fallbackencoding') - if fallback: - encoding.fallbackencoding = fallback - - fullargs = args - cmd, func, args, options, cmdoptions = _parse(lui, args) - - if options["config"]: - raise error.Abort(_("option --config may not be abbreviated!")) - if options["cwd"]: - raise error.Abort(_("option --cwd may not be abbreviated!")) - if options["repository"]: - raise error.Abort(_( - "option -R has to be separated from other options (e.g. not -qR) " - "and --repository may only be abbreviated as --repo!")) - - if options["encoding"]: - encoding.encoding = options["encoding"] - if options["encodingmode"]: - encoding.encodingmode = options["encodingmode"] - if options["time"]: - def get_times(): - t = os.times() - if t[4] == 0.0: # Windows leaves this as zero, so use time.clock() - t = (t[0], t[1], t[2], t[3], time.clock()) - return t - s = get_times() - def print_time(): - t = get_times() - ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") % - (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3])) - atexit.register(print_time) - uis = set([ui, lui]) if req.repo: uis.add(req.repo.ui) - if options['verbose'] or options['debug'] or options['quiet']: - for opt in ('verbose', 'debug', 'quiet'): - val = str(bool(options[opt])) - for ui_ in uis: - ui_.setconfig('ui', opt, val, '--' + opt) - - if options['profile']: + if '--profile' in args: for ui_ in uis: ui_.setconfig('profiling', 'enabled', 'true', '--profile') - if options['traceback']: - for ui_ in uis: - ui_.setconfig('ui', 'traceback', 'on', '--traceback') + with profiling.maybeprofile(lui): + # Configure extensions in phases: uisetup, extsetup, cmdtable, and + # reposetup. Programs like TortoiseHg will call _dispatch several + # times so we keep track of configured extensions in _loaded. + extensions.loadall(lui) + exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded] + # Propagate any changes to lui.__class__ by extensions + ui.__class__ = lui.__class__ + + # (uisetup and extsetup are handled in extensions.loadall) + + for name, module in exts: + for objname, loadermod, loadername in extraloaders: + extraobj = getattr(module, objname, None) + if extraobj is not None: + getattr(loadermod, loadername)(ui, name, extraobj) + _loaded.add(name) + + # (reposetup is handled in hg.repository) + + addaliases(lui, commands.table) + + # All aliases and commands are completely defined, now. + # Check abbreviation/ambiguity of shell alias. + shellaliasfn = _checkshellalias(lui, ui, args) + if shellaliasfn: + return shellaliasfn() + + # check for fallback encoding + fallback = lui.config('ui', 'fallbackencoding') + if fallback: + encoding.fallbackencoding = fallback + + fullargs = args + cmd, func, args, options, cmdoptions = _parse(lui, args) + + if options["config"]: + raise error.Abort(_("option --config may not be abbreviated!")) + if options["cwd"]: + raise error.Abort(_("option --cwd may not be abbreviated!")) + if options["repository"]: + raise error.Abort(_( + "option -R has to be separated from other options (e.g. not " + "-qR) and --repository may only be abbreviated as --repo!")) - if options['noninteractive']: - for ui_ in uis: - ui_.setconfig('ui', 'interactive', 'off', '-y') + if options["encoding"]: + encoding.encoding = options["encoding"] + if options["encodingmode"]: + encoding.encodingmode = options["encodingmode"] + if options["time"]: + def get_times(): + t = os.times() + if t[4] == 0.0: + # Windows leaves this as zero, so use time.clock() + t = (t[0], t[1], t[2], t[3], time.clock()) + return t + s = get_times() + def print_time(): + t = get_times() + ui.warn( + _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") % + (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3])) + ui.atexit(print_time) - if cmdoptions.get('insecure', False): + if options['verbose'] or options['debug'] or options['quiet']: + for opt in ('verbose', 'debug', 'quiet'): + val = str(bool(options[opt])) + if pycompat.ispy3: + val = val.encode('ascii') + for ui_ in uis: + ui_.setconfig('ui', opt, val, '--' + opt) + + if options['traceback']: + for ui_ in uis: + ui_.setconfig('ui', 'traceback', 'on', '--traceback') + + if options['noninteractive']: + for ui_ in uis: + ui_.setconfig('ui', 'interactive', 'off', '-y') + + if util.parsebool(options['pager']): + ui.pager('internal-always-' + cmd) + elif options['pager'] != 'auto': + ui.disablepager() + + if cmdoptions.get('insecure', False): + for ui_ in uis: + ui_.insecureconnections = True + + # setup color handling + coloropt = options['color'] for ui_ in uis: - ui_.insecureconnections = True + if coloropt: + ui_.setconfig('ui', 'color', coloropt, '--color') + color.setup(ui_) - if options['version']: - return commands.version_(ui) - if options['help']: - return commands.help_(ui, cmd, command=cmd is not None) - elif not cmd: - return commands.help_(ui, 'shortlist') + if options['version']: + return commands.version_(ui) + if options['help']: + return commands.help_(ui, cmd, command=cmd is not None) + elif not cmd: + return commands.help_(ui, 'shortlist') - with profiling.maybeprofile(lui): repo = None cmdpats = args[:] if not func.norepo: @@ -833,7 +902,7 @@ elif rpath: ui.warn(_("warning: --repository ignored\n")) - msg = ' '.join(' ' in a and repr(a) or a for a in fullargs) + msg = _formatargs(fullargs) ui.log("command", '%s\n', msg) strcmdopt = pycompat.strkwargs(cmdoptions) d = lambda: util.checksignature(func)(ui, *args, **strcmdopt) @@ -866,6 +935,8 @@ if ui.config('ui', 'supportcontact', None) is None: for name, mod in extensions.extensions(): testedwith = getattr(mod, 'testedwith', '') + if pycompat.ispy3 and isinstance(testedwith, str): + testedwith = testedwith.encode(u'utf-8') report = getattr(mod, 'buglink', _('the extension author.')) if not testedwith.strip(): # We found an untested extension. It's likely the culprit. @@ -886,7 +957,7 @@ worst = name, nearest, report if worst[0] is not None: name, testedwith, report = worst - if not isinstance(testedwith, str): + if not isinstance(testedwith, (bytes, str)): testedwith = '.'.join([str(c) for c in testedwith]) warning = (_('** Unknown exception encountered with ' 'possibly-broken third-party extension %s\n' @@ -900,7 +971,12 @@ bugtracker = _("https://mercurial-scm.org/wiki/BugTracker") warning = (_("** unknown exception encountered, " "please report by visiting\n** ") + bugtracker + '\n') - warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) + + if pycompat.ispy3: + sysversion = sys.version.encode(u'utf-8') + else: + sysversion = sys.version + sysversion = sysversion.replace('\n', '') + warning += ((_("** Python %s\n") % sysversion) + (_("** Mercurial Distributed SCM (version %s)\n") % util.version()) + (_("** Extensions loaded: %s\n") %