def run(): "run the command in sys.argv" initstdio() with tracing.log('parse args into request'): req = request(pycompat.sysargv[1:]) err = None try: status = dispatch(req) except error.StdioError as e: err = e status = -1 # In all cases we try to flush stdio streams. if util.safehasattr(req.ui, 'fout'): try: req.ui.fout.flush() except IOError as e: err = e status = -1 if util.safehasattr(req.ui, 'ferr'): try: if err is not None and err.errno != errno.EPIPE: req.ui.ferr.write('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 _silencestdio() sys.exit(status & 255)
def _runcommand(ui, options, cmd, cmdfunc): """Run a command function, possibly with profiling enabled.""" try: with tracing.log("Running %s command" % cmd): return cmdfunc() except error.SignatureError: raise error.CommandError(cmd, _('invalid arguments'))
def finalextsetup(self, ui): """Method to be used as a the extension extsetup The following operations belong here: - Changes depending on the status of other extensions. (if extensions.find('mq')) - Add a global option to all commands """ knownexts = {} for ext, command, wrapper, opts in self._extcommandwrappers: if ext not in knownexts: try: e = extensions.find(ext) except KeyError: # Extension isn't enabled, so don't bother trying to wrap # it. continue knownexts[ext] = e.cmdtable entry = extensions.wrapcommand(knownexts[ext], command, wrapper) if opts: for opt in opts: entry[1].append(opt) for c in self._extcallables: with tracing.log('finalextsetup: %s', repr(c)): c(ui)
def finaluisetup(self, ui): """Method to be used as the extension uisetup The following operations belong here: - Changes to ui.__class__ . The ui object that will be used to run the command has not yet been created. Changes made here will affect ui objects created after this, and in particular the ui that will be passed to runcommand - Command wraps (extensions.wrapcommand) - Changes that need to be visible to other extensions: because initialization occurs in phases (all extensions run uisetup, then all run extsetup), a change made here will be visible to other extensions during extsetup - Monkeypatch or wrap function (extensions.wrapfunction) of dispatch module members - Setup of pre-* and post-* hooks - pushkey setup """ for command, wrapper, opts in self._commandwrappers: entry = extensions.wrapcommand(commands.table, command, wrapper) if opts: for opt in opts: entry[1].append(opt) for cont, funcname, wrapper in self._functionwrappers: extensions.wrapfunction(cont, funcname, wrapper) for c in self._uicallables: with tracing.log('finaluisetup: %s', repr(c)): c(ui)
def finalreposetup(self, ui, repo): """Method to be used as the extension reposetup The following operations belong here: - All hooks but pre-* and post-* - Modify configuration variables - Changes to repo.__class__, repo.dirstate.__class__ """ for c in self._repocallables: with tracing.log('finalreposetup: %s', repr(c)): c(ui, repo)
def run(): """run the command in sys.argv""" try: initstdio() with tracing.log('parse args into request'): req = request(pycompat.sysargv[1:]) status = dispatch(req) _silencestdio() except KeyboardInterrupt: # Catch early/late KeyboardInterrupt as last ditch. Here nothing will # be printed to console to avoid another IOError/KeyboardInterrupt. status = -1 sys.exit(status & 255)
def _runcatch(req): with tracing.log('dispatch._runcatch'): def catchterm(*args): raise error.SignalInterrupt ui = req.ui try: for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': num = getattr(signal, name, None) if num: signal.signal(num, catchterm) except ValueError: pass # happens if called in a thread def _runcatchfunc(): realcmd = None try: cmdargs = fancyopts.fancyopts( req.args[:], commands.globalopts, {}) cmd = cmdargs[0] aliases, entry = cmdutil.findcmd(cmd, commands.table, False) realcmd = aliases[0] except (error.UnknownCommand, error.AmbiguousCommand, IndexError, getopt.GetoptError): # Don't handle this here. We know the command is # invalid, but all we're worried about for now is that # it's not a command that server operators expect to # be safe to offer to users in a sandbox. pass if realcmd == 'serve' and '--stdio' in cmdargs: # We want to constrain 'hg serve --stdio' instances pretty # closely, as many shared-ssh access tools want to grant # access to run *only* 'hg -R $repo serve --stdio'. We # restrict to exactly that set of arguments, and prohibit # any repo name that starts with '--' to prevent # shenanigans wherein a user does something like pass # --debugger or --config=ui.debugger=1 as a repo # name. This used to actually run the debugger. if (len(req.args) != 4 or req.args[0] != '-R' or req.args[1].startswith('--') or req.args[2] != 'serve' or req.args[3] != '--stdio'): raise error.Abort( _('potentially unsafe serve --stdio invocation: %s') % (stringutil.pprint(req.args),)) try: debugger = 'pdb' debugtrace = { 'pdb': pdb.set_trace } debugmortem = { 'pdb': pdb.post_mortem } # read --config before doing anything else # (e.g. to change trust settings for reading .hg/hgrc) cfgs = _parseconfig(req.ui, req.earlyoptions['config']) if req.repo: # copy configs that were passed on the cmdline (--config) to # the repo ui for sec, name, val in cfgs: req.repo.ui.setconfig(sec, name, val, source='--config') # developer config: ui.debugger debugger = ui.config("ui", "debugger") debugmod = pdb if not debugger or ui.plain(): # if we are in HGPLAIN mode, then disable custom debugging debugger = 'pdb' elif req.earlyoptions['debugger']: # This import can be slow for fancy debuggers, so only # do it when absolutely necessary, i.e. when actual # debugging has been requested with demandimport.deactivated(): try: debugmod = __import__(debugger) except ImportError: pass # Leave debugmod = pdb debugtrace[debugger] = debugmod.set_trace debugmortem[debugger] = debugmod.post_mortem # enter the debugger before command execution if req.earlyoptions['debugger']: ui.warn(_("entering debugger - " "type c to continue starting hg or h for help\n")) if (debugger != 'pdb' and debugtrace[debugger] == debugtrace['pdb']): ui.warn(_("%s debugger specified " "but its module was not found\n") % debugger) with demandimport.deactivated(): debugtrace[debugger]() try: return _dispatch(req) finally: ui.flush() except: # re-raises # enter the debugger when we hit an exception if req.earlyoptions['debugger']: traceback.print_exc() debugmortem[debugger](sys.exc_info()[2]) raise return _callcatch(ui, _runcatchfunc)
def dispatch(req): """run the command specified in req.args; returns an integer status code""" with tracing.log('dispatch.dispatch'): if req.ferr: ferr = req.ferr elif req.ui: ferr = req.ui.ferr else: ferr = procutil.stderr try: if not req.ui: req.ui = uimod.ui.load() req.earlyoptions.update(_earlyparseopts(req.ui, req.args)) if req.earlyoptions['traceback']: req.ui.setconfig('ui', 'traceback', 'on', '--traceback') # set ui streams from the request if req.fin: req.ui.fin = req.fin if req.fout: req.ui.fout = req.fout if req.ferr: req.ui.ferr = req.ferr except error.Abort as inst: ferr.write(_("abort: %s\n") % inst) if inst.hint: ferr.write(_("(%s)\n") % inst.hint) return -1 except error.ParseError as inst: _formatparse(ferr.write, inst) return -1 msg = _formatargs(req.args) starttime = util.timer() ret = 1 # default of Python exit code on unhandled exception try: ret = _runcatch(req) or 0 except error.ProgrammingError as inst: req.ui.error(_('** ProgrammingError: %s\n') % inst) if inst.hint: req.ui.error(_('** (%s)\n') % inst.hint) raise except KeyboardInterrupt as inst: try: if isinstance(inst, error.SignalInterrupt): msg = _("killed!\n") else: msg = _("interrupted!\n") req.ui.error(msg) 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 = util.timer() - starttime req.ui.flush() if req.ui.logblockedtimes: req.ui._blockedtimes['command_duration'] = duration * 1000 req.ui.log('uiblocked', 'ui blocked ms', **pycompat.strkwargs(req.ui._blockedtimes)) req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n", msg, ret & 255, duration, canonical_command=req.canonical_command) try: req._runexithandlers() except: # exiting, so no re-raises ret = ret or -1 return ret
def _rundispatch(req): with tracing.log('dispatch._rundispatch'): if req.ferr: ferr = req.ferr elif req.ui: ferr = req.ui.ferr else: ferr = procutil.stderr try: if not req.ui: req.ui = uimod.ui.load() req.earlyoptions.update(_earlyparseopts(req.ui, req.args)) if req.earlyoptions[b'traceback']: req.ui.setconfig(b'ui', b'traceback', b'on', b'--traceback') # set ui streams from the request if req.fin: req.ui.fin = req.fin if req.fout: req.ui.fout = req.fout if req.ferr: req.ui.ferr = req.ferr if req.fmsg: req.ui.fmsg = req.fmsg except error.Abort as inst: ferr.write(inst.format()) return -1 msg = _formatargs(req.args) starttime = util.timer() ret = 1 # default of Python exit code on unhandled exception try: ret = _runcatch(req) or 0 except error.ProgrammingError as inst: req.ui.error(_(b'** ProgrammingError: %s\n') % inst) if inst.hint: req.ui.error(_(b'** (%s)\n') % inst.hint) raise except KeyboardInterrupt as inst: try: if isinstance(inst, error.SignalInterrupt): msg = _(b"killed!\n") else: msg = _(b"interrupted!\n") req.ui.error(msg) 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 = util.timer() - starttime req.ui.flush() # record blocked times if req.ui.logblockedtimes: req.ui._blockedtimes[b'command_duration'] = duration * 1000 req.ui.log(b'uiblocked', b'ui blocked ms\n', **pycompat.strkwargs(req.ui._blockedtimes)) return_code = ret & 255 req.ui.log( b"commandfinish", b"%s exited %d after %0.2f seconds\n", msg, return_code, duration, return_code=return_code, duration=duration, canonical_command=req.canonical_command, ) try: req._runexithandlers() 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() return ret