##// END OF EJS Templates
revlog: move censor logic out of censor extension...
revlog: move censor logic out of censor extension The censor extension is doing very low-level things with revlogs. It is fundamentally impossible for this logic to remain in the censor extension while support multiple storage backends: we need each storage backend to implement censor in its own storage-specific way. This commit effectively moves the revlog-specific censoring code to be a method of revlogs themselves. We've defined a new API on the file storage interface for censoring an individual node. Even though the current censoring code doesn't use it, the API requires a transaction instance because it logically makes sense for storage backends to require an active transaction (which implies a held write lock) in order to rewrite storage. After this commit, the censor extension has been reduced to boilerplate precondition checking before invoking the generic storage API. I tried to keep the code as similar as possible. But some minor changes were made: * We use self._io instead of instantiating a new revlogio instance. * We compare self.version against REVLOGV0 instead of != REVLOGV1 because presumably all future revlog versions will support censoring. * We use self.opener instead of going through repo.svfs (we don't have a handle on the repo instance from a revlog). * "revlog" dropped * Replace "flog" with "self". Differential Revision: https://phab.mercurial-scm.org/D4656

File last commit:

r39291:4019b454 default
r39814:a6b3c4c1 default
Show More
dispatch.py
1070 lines | 39.0 KiB | text/x-python | PythonLexer
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 # dispatch.py - command dispatching for mercurial
#
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Gregory Szorc
dispatch: use print function...
r27615 from __future__ import absolute_import, print_function
Gregory Szorc
dispatch: use absolute_import...
r25932
Augie Fackler
dispatch: offer near-edit-distance suggestions for {file,rev}set functions...
r24221 import difflib
Gregory Szorc
dispatch: use absolute_import...
r25932 import errno
Pulkit Goyal
fancyopts: switch from fancyopts.getopt.* to getopt.*...
r30576 import getopt
Gregory Szorc
dispatch: use absolute_import...
r25932 import os
import pdb
import re
import signal
import sys
import time
import traceback
from .i18n import _
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 from hgdemandimport import tracing
Gregory Szorc
dispatch: use absolute_import...
r25932 from . import (
cmdutil,
Pierre-Yves David
color: load 'colortable' from extension using an 'extraloader'...
r30653 color,
Gregory Szorc
dispatch: use absolute_import...
r25932 commands,
demandimport,
encoding,
error,
extensions,
fancyopts,
Augie Fackler
dispatch: rearrange 'unknown command' code to better employ pager...
r31060 help,
Gregory Szorc
dispatch: use absolute_import...
r25932 hg,
hook,
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 profiling,
Pulkit Goyal
py3: use pycompat.sysargv in dispatch.run()...
r30468 pycompat,
Jun Wu
dispatch: move part of callcatch to scmutil...
r30520 scmutil,
Gregory Szorc
dispatch: use absolute_import...
r25932 ui as uimod,
util,
)
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 from .utils import (
Yuya Nishihara
procutil: bulk-replace util.std* to point to new module
r37137 procutil,
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 stringutil,
)
Idan Kamara
dispatch: wrap dispatch related information in a request class...
r14438 class request(object):
Brodie Rao
cleanup: eradicate long lines
r16683 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
Jun Wu
dispatch: make request accept additional reposetups...
r32379 ferr=None, prereposetups=None):
Idan Kamara
dispatch: wrap dispatch related information in a request class...
r14438 self.args = args
Idan Kamara
dispatch: use the request to store the ui object...
r14439 self.ui = ui
Idan Kamara
dispatch: add repo to the request...
r14510 self.repo = repo
Idan Kamara
dispatch: wrap dispatch related information in a request class...
r14438
Idan Kamara
dispatch: add I/O descriptors to the request
r14613 # input/output/error streams
self.fin = fin
self.fout = fout
self.ferr = ferr
Yuya Nishihara
dispatch: replace _earlyreq*() with new fancyopts-based parser
r35224 # remember options pre-parsed by _earlyparseopts()
Yuya Nishihara
dispatch: abort if early boolean options can't be parsed...
r35059 self.earlyoptions = {}
Jun Wu
dispatch: make request accept additional reposetups...
r32379 # reposetups which run before extensions, useful for chg to pre-fill
# low-level repo state (for example, changelog) before extensions.
self.prereposetups = prereposetups or []
Bryan O'Sullivan
ui: add special-purpose atexit functionality...
r31956 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
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 def run():
"run the command in sys.argv"
Yuya Nishihara
sshserver: do setbinary() by caller (API)...
r37963 initstdio()
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 with tracing.log('parse args into request'):
req = request(pycompat.sysargv[1:])
Bryan O'Sullivan
stdio: catch StdioError in dispatch.run and clean up appropriately...
r31960 err = None
try:
Yuya Nishihara
dispatch: unify handling of None returned by a command function...
r38015 status = dispatch(req)
Yuya Nishihara
py3: work around the scope of exception variable in dispatch.run()...
r34533 except error.StdioError as e:
err = e
Bryan O'Sullivan
stdio: catch StdioError in dispatch.run and clean up appropriately...
r31960 status = -1
Gregory Szorc
dispatch: minor code refactor...
r38014
# In all cases we try to flush stdio streams.
Bryan O'Sullivan
stdio: catch StdioError in dispatch.run and clean up appropriately...
r31960 if util.safehasattr(req.ui, 'fout'):
try:
Yuya Nishihara
dispatch: do not close stdout and stderr, just flush() instead...
r32687 req.ui.fout.flush()
Yuya Nishihara
py3: work around the scope of exception variable in dispatch.run()...
r34533 except IOError as e:
err = e
Bryan O'Sullivan
stdio: catch StdioError in dispatch.run and clean up appropriately...
r31960 status = -1
Gregory Szorc
dispatch: minor code refactor...
r38014
Bryan O'Sullivan
stdio: catch StdioError in dispatch.run and clean up appropriately...
r31960 if util.safehasattr(req.ui, 'ferr'):
Gregory Szorc
dispatch: handle IOError when writing to stderr...
r35671 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
Yuya Nishihara
py3: silence the final IOError by closing stdout/err slightly early...
r36655
_silencestdio()
Bryan O'Sullivan
stdio: catch StdioError in dispatch.run and clean up appropriately...
r31960 sys.exit(status & 255)
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Yuya Nishihara
py3: conditionalize initialization of stdio flags...
r36654 if pycompat.ispy3:
Yuya Nishihara
sshserver: do setbinary() by caller (API)...
r37963 def initstdio():
Yuya Nishihara
py3: conditionalize initialization of stdio flags...
r36654 pass
Yuya Nishihara
py3: silence the final IOError by closing stdout/err slightly early...
r36655
def _silencestdio():
for fp in (sys.stdout, sys.stderr):
# Check if the file is okay
try:
fp.flush()
continue
except IOError:
pass
# Otherwise mark it as closed to silence "Exception ignored in"
# message emitted by the interpreter finalizer. Be careful to
Yuya Nishihara
procutil: bulk-replace util.std* to point to new module
r37137 # not close procutil.stdout, which may be a fdopen-ed file object
# and its close() actually closes the underlying file descriptor.
Yuya Nishihara
py3: silence the final IOError by closing stdout/err slightly early...
r36655 try:
fp.close()
except IOError:
pass
Yuya Nishihara
py3: conditionalize initialization of stdio flags...
r36654 else:
Yuya Nishihara
sshserver: do setbinary() by caller (API)...
r37963 def initstdio():
Yuya Nishihara
py3: conditionalize initialization of stdio flags...
r36654 for fp in (sys.stdin, sys.stdout, sys.stderr):
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 procutil.setbinary(fp)
Yuya Nishihara
dispatch: move initialization of sys.std* files...
r34534
Yuya Nishihara
py3: silence the final IOError by closing stdout/err slightly early...
r36655 def _silencestdio():
pass
Augie Fackler
dispatch: offer near-edit-distance suggestions for {file,rev}set functions...
r24221 def _getsimilar(symbols, value):
sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
# The cutoff for similarity here is pretty arbitrary. It should
# probably be investigated and tweaked.
return [s for s in symbols if sim(s) > 0.6]
Bryan O'Sullivan
dispatch: report similar names consistently
r27623 def _reportsimilar(write, similar):
if len(similar) == 1:
write(_("(did you mean %s?)\n") % similar[0])
elif similar:
ss = ", ".join(sorted(similar))
write(_("(did you mean one of %s?)\n") % ss)
Augie Fackler
dispatch: consolidate formatting of ParseErrors
r24039 def _formatparse(write, inst):
Augie Fackler
dispatch: offer near-edit-distance suggestions for {file,rev}set functions...
r24221 similar = []
if isinstance(inst, error.UnknownIdentifier):
# make sure to check fileset first, as revset can invoke fileset
similar = _getsimilar(inst.symbols, inst.function)
Augie Fackler
dispatch: consolidate formatting of ParseErrors
r24039 if len(inst.args) > 1:
write(_("hg: parse error at %s: %s\n") %
Yuya Nishihara
py3: use bytestr() to coerce position carried by ParseError to string...
r36521 (pycompat.bytestr(inst.args[1]), inst.args[0]))
Yuya Nishihara
py3: use startswith() instead of slicing to detect leading whitespace
r36747 if inst.args[0].startswith(' '):
Augie Fackler
dispatch: consolidate formatting of ParseErrors
r24039 write(_("unexpected leading whitespace\n"))
else:
write(_("hg: parse error: %s\n") % inst.args[0])
Bryan O'Sullivan
dispatch: report similar names consistently
r27623 _reportsimilar(write, similar)
Jun Wu
dispatch: extract common logic for handling ParseError...
r28515 if inst.hint:
write(_("(%s)\n") % inst.hint)
Augie Fackler
dispatch: consolidate formatting of ParseErrors
r24039
Augie Fackler
dispatch: consolidate formatting of arguments...
r31492 def _formatargs(args):
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 return ' '.join(procutil.shellquote(a) for a in args)
Augie Fackler
dispatch: consolidate formatting of arguments...
r31492
Idan Kamara
dispatch: wrap dispatch related information in a request class...
r14438 def dispatch(req):
Yuya Nishihara
dispatch: unify handling of None returned by a command function...
r38015 """run the command specified in req.args; returns an integer status code"""
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 with tracing.log('dispatch.dispatch'):
if req.ferr:
ferr = req.ferr
elif req.ui:
ferr = req.ui.ferr
else:
ferr = procutil.stderr
Idan Kamara
dispatch: assign I/O descriptors from the request to the ui
r14615
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 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')
Idan Kamara
dispatch: assign I/O descriptors from the request to the ui
r14615
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 # 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
Idan Kamara
dispatch: assign I/O descriptors from the request to the ui
r14615
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 msg = _formatargs(req.args)
starttime = util.timer()
ret = 1 # default of Python exit code on unhandled exception
Yuya Nishihara
dispatch: catch KeyboardInterrupt more broadly...
r28520 try:
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 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)
try:
req._runexithandlers()
except: # exiting, so no re-raises
ret = ret or -1
return ret
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Idan Kamara
dispatch: use the request to store the ui object...
r14439 def _runcatch(req):
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 with tracing.log('dispatch._runcatch'):
def catchterm(*args):
raise error.SignalInterrupt
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 ui = req.ui
Augie Fackler
dispatch: protect against malicious 'hg serve --stdio' invocations (sec)...
r32050 try:
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 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
Augie Fackler
dispatch: protect against malicious 'hg serve --stdio' invocations (sec)...
r32050
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 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),))
Sean Farley
dispatch: move command line --config argument parsing to _runcatch()...
r19639
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 try:
debugger = 'pdb'
debugtrace = {
'pdb': pdb.set_trace
}
debugmortem = {
'pdb': pdb.post_mortem
}
Sean Farley
dispatch: move command line --config argument parsing to _runcatch()...
r19639
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 # 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')
Sean Farley
dispatch: add ability to specify a custom pdb module as a debugger...
r19640
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 # 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
Sean Farley
dispatch: add ability to specify a custom pdb module as a debugger...
r19640
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 debugtrace[debugger] = debugmod.set_trace
debugmortem[debugger] = debugmod.post_mortem
Sean Farley
dispatch: add ability to specify a custom pdb module as a debugger...
r19640
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 # 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"))
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Augie Fackler
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
r39291 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)
Jun Wu
dispatch: split global error handling out so it can be reused...
r29761
Yuya Nishihara
dispatch: mark callcatch() as a private function
r32040 def _callcatch(ui, func):
Jun Wu
dispatch: move part of callcatch to scmutil...
r30520 """like scmutil.callcatch but handles more high-level exceptions about
config parsing and commands. besides, use handlecommandexception to handle
uncaught exceptions.
Jun Wu
dispatch: split global error handling out so it can be reused...
r29761 """
try:
Jun Wu
dispatch: move part of callcatch to scmutil...
r30520 return scmutil.callcatch(ui, func)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except error.AmbiguousCommand as inst:
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
(inst.args[0], " ".join(inst.args[1])))
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except error.CommandError as inst:
Matt Mackall
dispatch: sort exception handlers
r7645 if inst.args[0]:
Augie Fackler
dispatch: add pagination of two more help cases...
r31266 ui.pager('help')
Augie Fackler
dispatch: convert exception payload to bytes more carefully...
r32620 msgbytes = pycompat.bytestr(inst.args[1])
ui.warn(_("hg %s: %s\n") % (inst.args[0], msgbytes))
Martin Geisler
help: add -c/--command flag to only show command help (issue2799)
r14286 commands.help_(ui, inst.args[0], full=False, command=True)
Matt Mackall
dispatch: sort exception handlers
r7645 else:
ui.warn(_("hg: %s\n") % inst.args[1])
Martin von Zweigbergk
dispatch: show a short error message when invalid global option given...
r38824 ui.warn(_("(use 'hg help -v' for a list of global options)\n"))
Jun Wu
dispatch: move part of callcatch to scmutil...
r30520 except error.ParseError as inst:
_formatparse(ui.warn, inst)
return -1
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except error.UnknownCommand as inst:
Augie Fackler
dispatch: rearrange 'unknown command' code to better employ pager...
r31060 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
Brodie Rao
dispatch: provide help for disabled extensions and commands...
r10364 try:
# check if the command is in a disabled extension
# (but don't check for extensions themselves)
Yuya Nishihara
help: pass commands module by argument...
r32567 formatted = help.formattedhelp(ui, commands, inst.args[0],
unknowncmd=True)
Augie Fackler
dispatch: rearrange 'unknown command' code to better employ pager...
r31060 ui.warn(nocmdmsg)
ui.write(formatted)
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 except (error.UnknownCommand, error.Abort):
Augie Fackler
dispatch: offer suggestions of similar-named commands...
r24222 suggested = False
if len(inst.args) == 2:
sim = _getsimilar(inst.args[1], inst.args[0])
if sim:
Augie Fackler
dispatch: rearrange 'unknown command' code to better employ pager...
r31060 ui.warn(nocmdmsg)
Bryan O'Sullivan
dispatch: report similar names consistently
r27623 _reportsimilar(ui.warn, sim)
Augie Fackler
dispatch: offer suggestions of similar-named commands...
r24222 suggested = True
if not suggested:
Augie Fackler
dispatch: rearrange 'unknown command' code to better employ pager...
r31060 ui.warn(nocmdmsg)
Martin von Zweigbergk
dispatch: don't show list of commands on bogus command...
r38823 ui.warn(_("(use 'hg help' for a list of commands)\n"))
Jun Wu
dispatch: move part of callcatch to scmutil...
r30520 except IOError:
raise
Matt Mackall
dispatch: sort exception handlers
r7645 except KeyboardInterrupt:
Yuya Nishihara
dispatch: catch KeyboardInterrupt more broadly...
r28520 raise
Jun Wu
dispatch: move part of callcatch to scmutil...
r30520 except: # probably re-raises
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784 if not handlecommandexception(ui):
raise
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
return -1
Alexander Solovyov
add positional arguments to non-shell aliases
r14265 def aliasargs(fn, givenargs):
Jun Wu
wrapfunction: use functools.partial if possible...
r34089 args = []
# only care about alias 'args', ignore 'args' set by extensions.wrapfunction
if not util.safehasattr(fn, '_origfunc'):
args = getattr(fn, 'args', args)
Matt Mackall
alias: abort on missing positional args (issue3331)
r16294 if args:
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 cmd = ' '.join(map(procutil.shellquote, args))
Alexander Solovyov
add positional arguments to non-shell aliases
r14265
nums = []
def replacer(m):
num = int(m.group(1)) - 1
nums.append(num)
Matt Mackall
aliases: use empty string for missing position parameters (issue3331)
r16277 if num < len(givenargs):
return givenargs[num]
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('too few arguments for command alias'))
Pulkit Goyal
py3: make the regular expression bytes to prevent TypeError
r31491 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
Alexander Solovyov
add positional arguments to non-shell aliases
r14265 givenargs = [x for i, x in enumerate(givenargs)
if i not in nums]
Pulkit Goyal
py3: have a bytes version of shlex.split()...
r30678 args = pycompat.shlexsplit(cmd)
Alexander Solovyov
add positional arguments to non-shell aliases
r14265 return args + givenargs
Brendan Cully
Move alias into core
r8655
Siddharth Agarwal
alias: expand "$@" as list of parameters quoted individually (BC) (issue4200)...
r22158 def aliasinterpolate(name, args, cmd):
'''interpolate args into cmd for shell aliases
This also handles $0, $@ and "$@".
'''
# util.interpolate can't deal with "$@" (with quotes) because it's only
# built to match prefix + patterns.
replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
replacemap['$0'] = name
replacemap['$$'] = '$'
replacemap['$@'] = ' '.join(args)
# Typical Unix shells interpolate "$@" (with quotes) as all the positional
# parameters, separated out into words. Emulate the same behavior here by
# quoting the arguments individually. POSIX shells will then typically
# tokenize each argument into exactly one word.
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 replacemap['"$@"'] = ' '.join(procutil.shellquote(arg) for arg in args)
Siddharth Agarwal
alias: expand "$@" as list of parameters quoted individually (BC) (issue4200)...
r22158 # escape '\$' for regex
Pulkit Goyal
py3: add b'' to regular expressions which are raw strings...
r35145 regex = '|'.join(replacemap.keys()).replace('$', br'\$')
Siddharth Agarwal
alias: expand "$@" as list of parameters quoted individually (BC) (issue4200)...
r22158 r = re.compile(regex)
return r.sub(lambda x: replacemap[x.group()], cmd)
Brendan Cully
Move alias into core
r8655 class cmdalias(object):
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 def __init__(self, ui, name, definition, cmdtable, source):
Brodie Rao
alias: make shadowing behavior more consistent (issue2054)...
r12039 self.name = self.cmd = name
Brodie Rao
alias: print what command is being shadowed in debug message
r12092 self.cmdname = ''
Brendan Cully
Move alias into core
r8655 self.definition = definition
Yuya Nishihara
alias: keep error message in "badalias" so that help can see it...
r22160 self.fn = None
Jun Wu
dispatch: defer environment variable resolution in alias commands (BC)...
r29087 self.givenargs = []
Brendan Cully
Move alias into core
r8655 self.opts = []
self.help = ''
Yuya Nishihara
alias: keep error message in "badalias" so that help can see it...
r22160 self.badalias = None
Yuya Nishihara
alias: provide "unknowncmd" flag to tell help to look for disabled command...
r22161 self.unknowncmd = False
timeless
help: report source of aliases
r28828 self.source = source
Brendan Cully
Move alias into core
r8655
try:
Brodie Rao
alias: make shadowing behavior more consistent (issue2054)...
r12039 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
for alias, e in cmdtable.iteritems():
if e is entry:
self.cmd = alias
break
Brendan Cully
Move alias into core
r8655 self.shadows = True
except error.UnknownCommand:
self.shadows = False
if not self.definition:
Yuya Nishihara
alias: keep error message in "badalias" so that help can see it...
r22160 self.badalias = _("no definition for alias '%s'") % self.name
Brendan Cully
Move alias into core
r8655 return
Steve Losh
dispatch: add shell aliases...
r11524 if self.definition.startswith('!'):
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 shdef = self.definition[1:]
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536 self.shell = True
Steve Losh
dispatch: add shell aliases...
r11524 def fn(ui, *args):
Steve Losh
aliases: provide more flexible ways to work with shell alias arguments...
r11989 env = {'HG_ARGS': ' '.join((self.name,) + args)}
def _checkvar(m):
Roman Sokolov
dispatch: support for $ escaping in shell-alias definition...
r13392 if m.groups()[0] == '$':
return m.group()
elif int(m.groups()[0]) <= len(args):
Steve Losh
aliases: provide more flexible ways to work with shell alias arguments...
r11989 return m.group()
else:
David Soria Parra
i18n: remove translation of debug messages
r14708 ui.debug("No argument found for substitution "
Kyle Lippincott
debug: add newlines at the end of three locations that appear to need it...
r35482 "of %i variable in alias '%s' definition.\n"
Roman Sokolov
dispatch: debug message for missing arguments in shell alias...
r13393 % (int(m.groups()[0]), self.name))
Steve Losh
aliases: provide more flexible ways to work with shell alias arguments...
r11989 return ''
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 cmd = re.sub(br'\$(\d+|\$)', _checkvar, shdef)
Siddharth Agarwal
alias: expand "$@" as list of parameters quoted individually (BC) (issue4200)...
r22158 cmd = aliasinterpolate(self.name, args, cmd)
Simon Farnsworth
dispatch: set a blockedtag when running an external alias
r31199 return ui.system(cmd, environ=env,
blockedtag='alias_%s' % self.name)
Steve Losh
dispatch: add shell aliases...
r11524 self.fn = fn
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 self._populatehelp(ui, name, shdef, self.fn)
Steve Losh
dispatch: add shell aliases...
r11524 return
Yuya Nishihara
alias: handle shlex error in command aliases...
r21569 try:
Pulkit Goyal
py3: have a bytes version of shlex.split()...
r30678 args = pycompat.shlexsplit(self.definition)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except ValueError as inst:
Yuya Nishihara
alias: keep error message in "badalias" so that help can see it...
r22160 self.badalias = (_("error in definition for alias '%s': %s")
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 % (self.name, stringutil.forcebytestr(inst)))
Yuya Nishihara
alias: handle shlex error in command aliases...
r21569 return
Yuya Nishihara
dispatch: replace _earlygetopt(strip=True) with new parser...
r35225 earlyopts, args = _earlysplitopts(args)
if earlyopts:
self.badalias = (_("error in definition for alias '%s': %s may "
"only be given on the command line")
Pulkit Goyal
py3: use pycompat.ziplist instead of zip...
r35960 % (self.name, '/'.join(pycompat.ziplist(*earlyopts)
[0])))
Yuya Nishihara
dispatch: replace _earlygetopt(strip=True) with new parser...
r35225 return
Brodie Rao
alias: print what command is being shadowed in debug message
r12092 self.cmdname = cmd = args.pop(0)
Jun Wu
dispatch: defer environment variable resolution in alias commands (BC)...
r29087 self.givenargs = args
Brendan Cully
Move alias into core
r8655
try:
Nicolas Dumazet
alias: do not crash when aliased command has no usage help text
r9993 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
if len(tableentry) > 2:
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 self.fn, self.opts, cmdhelp = tableentry
Nicolas Dumazet
alias: do not crash when aliased command has no usage help text
r9993 else:
self.fn, self.opts = tableentry
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 cmdhelp = None
Nicolas Dumazet
alias: do not crash when aliased command has no usage help text
r9993
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 self._populatehelp(ui, name, cmd, self.fn, cmdhelp)
Peter Arrenbrecht
alias: improve help text for command aliases...
r9876
Brendan Cully
Move alias into core
r8655 except error.UnknownCommand:
Yuya Nishihara
alias: keep error message in "badalias" so that help can see it...
r22160 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
% (self.name, cmd))
Yuya Nishihara
alias: provide "unknowncmd" flag to tell help to look for disabled command...
r22161 self.unknowncmd = True
Brendan Cully
Move alias into core
r8655 except error.AmbiguousCommand:
Yuya Nishihara
alias: keep error message in "badalias" so that help can see it...
r22160 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
% (self.name, cmd))
Brendan Cully
Move alias into core
r8655
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 def _populatehelp(self, ui, name, cmd, fn, defaulthelp=None):
Yuya Nishihara
alias: reject non-ascii characters in user help/doc strings...
r37157 # confine strings to be passed to i18n.gettext()
cfg = {}
for k in ('doc', 'help'):
v = ui.config('alias', '%s:%s' % (name, k), None)
if v is None:
continue
if not encoding.isasciistr(v):
self.badalias = (_("non-ASCII character in alias definition "
"'%s:%s'") % (name, k))
return
cfg[k] = v
self.help = cfg.get('help', defaulthelp or '')
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 if self.help and self.help.startswith("hg " + cmd):
# drop prefix in old-style help lines so hg shows the alias
self.help = self.help[4 + len(cmd):]
Yuya Nishihara
py3: bytes/unicode dance on __doc__ of cmdalias
r37158 doc = cfg.get('doc', pycompat.getdoc(fn))
if doc is not None:
doc = pycompat.sysstr(doc)
self.__doc__ = doc
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152
Jun Wu
dispatch: defer environment variable resolution in alias commands (BC)...
r29087 @property
def args(self):
Pulkit Goyal
dispatch: use pycompat.maplist() instead of map() to get a list
r31629 args = pycompat.maplist(util.expandpath, self.givenargs)
Jun Wu
dispatch: defer environment variable resolution in alias commands (BC)...
r29087 return aliasargs(self.fn, args)
Yuya Nishihara
dispatch: make cmdalias forward command attributes to function...
r28621 def __getattr__(self, name):
Gregory Szorc
registrar: replace "cmdtype" with an intent-based mechanism (API)...
r37734 adefaults = {r'norepo': True, r'intents': set(),
Pulkit Goyal
py3: make adefaults keys str to be compatible with getattr...
r32158 r'optionalrepo': False, r'inferrepo': False}
Yuya Nishihara
dispatch: make cmdalias forward command attributes to function...
r28621 if name not in adefaults:
raise AttributeError(name)
if self.badalias or util.safehasattr(self, 'shell'):
return adefaults[name]
return getattr(self.fn, name)
Brendan Cully
Move alias into core
r8655 def __call__(self, ui, *args, **opts):
Yuya Nishihara
alias: keep error message in "badalias" so that help can see it...
r22160 if self.badalias:
Yuya Nishihara
alias: exit from bad definition by Abort
r22164 hint = None
Yuya Nishihara
alias: provide "unknowncmd" flag to tell help to look for disabled command...
r22161 if self.unknowncmd:
Brodie Rao
dispatch: provide help for disabled extensions and commands...
r10364 try:
# check if the command is in a disabled extension
Yuya Nishihara
alias: show one-line hint for command provided by disabled extension...
r22163 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
Yuya Nishihara
alias: exit from bad definition by Abort
r22164 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
Brodie Rao
dispatch: provide help for disabled extensions and commands...
r10364 except error.UnknownCommand:
pass
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(self.badalias, hint=hint)
Brendan Cully
Move alias into core
r8655 if self.shadows:
Martin Geisler
Backed out changeset 1ec8bd909ac3...
r14704 ui.debug("alias '%s' shadows command '%s'\n" %
Brodie Rao
alias: print what command is being shadowed in debug message
r12092 (self.name, self.cmdname))
Brendan Cully
Move alias into core
r8655
Augie Fackler
blackbox: also log alias expansions...
r29846 ui.log('commandalias', "alias '%s' expands to '%s'\n",
self.name, self.definition)
Augie Fackler
dispatch: use safehasattr instead of hasattr
r14950 if util.safehasattr(self, 'shell'):
Steve Losh
aliases: provide more flexible ways to work with shell alias arguments...
r11989 return self.fn(ui, *args, **opts)
else:
Brodie Rao
alias: on --debug, print expansion when it has invalid arguments
r12093 try:
Yuya Nishihara
alias: fix loss of non-zero return code in command aliases...
r21556 return util.checksignature(self.fn)(ui, *args, **opts)
Brodie Rao
alias: on --debug, print expansion when it has invalid arguments
r12093 except error.SignatureError:
args = ' '.join([self.cmdname] + self.args)
Martin Geisler
Backed out changeset 1ec8bd909ac3...
r14704 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
Brodie Rao
alias: on --debug, print expansion when it has invalid arguments
r12093 raise
Brendan Cully
Move alias into core
r8655
Jun Wu
alias: make alias command lazily resolved...
r34307 class lazyaliasentry(object):
"""like a typical command entry (func, opts, help), but is lazy"""
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 def __init__(self, ui, name, definition, cmdtable, source):
self.ui = ui
Jun Wu
alias: make alias command lazily resolved...
r34307 self.name = name
self.definition = definition
self.cmdtable = cmdtable.copy()
self.source = source
@util.propertycache
def _aliasdef(self):
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 return cmdalias(self.ui, self.name, self.definition, self.cmdtable,
self.source)
Jun Wu
alias: make alias command lazily resolved...
r34307
def __getitem__(self, n):
aliasdef = self._aliasdef
if n == 0:
return aliasdef
elif n == 1:
return aliasdef.opts
elif n == 2:
return aliasdef.help
else:
raise IndexError
def __iter__(self):
for i in range(3):
yield self[i]
def __len__(self):
return 3
Brendan Cully
Move alias into core
r8655 def addaliases(ui, cmdtable):
# aliases are processed after extensions have been loaded, so they
# may use extension commands. Aliases can also use other alias definitions,
# but only if they have been defined prior to the current definition.
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 for alias, definition in ui.configitems('alias', ignoresub=True):
Idan Kamara
dispatch: don't rewrap aliases that have the same definition...
r15019 try:
Jun Wu
alias: make alias command lazily resolved...
r34307 if cmdtable[alias].definition == definition:
Idan Kamara
dispatch: don't rewrap aliases that have the same definition...
r15019 continue
except (KeyError, AttributeError):
# definition might not exist or it might not be a cmdalias
pass
Jun Wu
alias: test duplicated definition earlier...
r34306 source = ui.configsource('alias', alias)
Rodrigo Damazio
help: supporting both help and doc for aliases...
r37152 entry = lazyaliasentry(ui, alias, definition, cmdtable, source)
Jun Wu
alias: make alias command lazily resolved...
r34307 cmdtable[alias] = entry
Brendan Cully
Move alias into core
r8655
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 def _parse(ui, args):
options = {}
cmdoptions = {}
try:
args = fancyopts.fancyopts(args, commands.globalopts, options)
Pulkit Goyal
fancyopts: switch from fancyopts.getopt.* to getopt.*...
r30576 except getopt.GetoptError as inst:
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 raise error.CommandError(None, stringutil.forcebytestr(inst))
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
if args:
cmd, args = args[0], args[1:]
Henri Wiechers
dispatch: minor refactoring...
r9875 aliases, entry = cmdutil.findcmd(cmd, commands.table,
Yuya Nishihara
commands: parse ui.strict config item as bool
r16591 ui.configbool("ui", "strict"))
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 cmd = aliases[0]
Alexander Solovyov
add positional arguments to non-shell aliases
r14265 args = aliasargs(entry[0], args)
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 defaults = ui.config("defaults", cmd)
if defaults:
Augie Fackler
dispatch: use pycompat.maplist to allow summing with args
r31502 args = pycompat.maplist(
util.expandpath, pycompat.shlexsplit(defaults)) + args
Henri Wiechers
dispatch: minor refactoring...
r9875 c = list(entry[1])
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 else:
cmd = None
c = []
# combine global options into local
for o in commands.globalopts:
c.append((o[0], o[1], options[o[1]], o[3]))
try:
Augie Fackler
dispatch: explicitly pass fancyopts optional arg as a keyword...
r29822 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
Pulkit Goyal
fancyopts: switch from fancyopts.getopt.* to getopt.*...
r30576 except getopt.GetoptError as inst:
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 raise error.CommandError(cmd, stringutil.forcebytestr(inst))
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
# separate global options back out
for o in commands.globalopts:
n = o[1]
options[n] = cmdoptions[n]
del cmdoptions[n]
Henri Wiechers
dispatch: minor refactoring...
r9875 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Matt Mackall
ui: kill updateopts...
r8137 def _parseconfig(ui, config):
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 """parse the --config options from the command line"""
Idan Kamara
dispatch: return read config options
r14753 configs = []
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 for cfg in config:
try:
Tony Tung
dispatch: strip command line options like config file options...
r28081 name, value = [cfgelem.strip()
for cfgelem in cfg.split('=', 1)]
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 section, name = name.split('.', 1)
if not section or not name:
raise IndexError
Mads Kiilerich
config: give a useful hint of source for the most common command line settings...
r20788 ui.setconfig(section, name, value, '--config')
Idan Kamara
dispatch: return read config options
r14753 configs.append((section, name, value))
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 except (IndexError, ValueError):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('malformed --config option: %r '
Yuya Nishihara
py3: fix some unicode madness in global exception catcher
r36659 '(use --config section.name=value)')
% pycompat.bytestr(cfg))
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Idan Kamara
dispatch: return read config options
r14753 return configs
Yuya Nishihara
dispatch: replace _earlyreq*() with new fancyopts-based parser
r35224 def _earlyparseopts(ui, args):
Yuya Nishihara
dispatch: add HGPLAIN=+strictflags to restrict early parsing of global options...
r35180 options = {}
fancyopts.fancyopts(args, commands.globalopts, options,
Yuya Nishihara
dispatch: replace _earlyreq*() with new fancyopts-based parser
r35224 gnu=not ui.plain('strictflags'), early=True,
Yuya Nishihara
dispatch: alias --repo to --repository while parsing early options...
r35223 optaliases={'repository': ['repo']})
Yuya Nishihara
dispatch: add HGPLAIN=+strictflags to restrict early parsing of global options...
r35180 return options
Yuya Nishihara
dispatch: replace _earlygetopt(strip=True) with new parser...
r35225 def _earlysplitopts(args):
"""Split args into a list of possible early options and remainder args"""
shortoptions = 'R:'
# TODO: perhaps 'debugger' should be included
longoptions = ['cwd=', 'repository=', 'repo=', 'config=']
return fancyopts.earlygetopt(args, shortoptions, longoptions,
gnu=True, keepsep=True)
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Chad Dombrova
provide pre- and post- hooks with parsed command line arguments....
r11330 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
Bill Barry
dispatch: extract command execution block into method...
r7819 # run pre-hook, and abort if it fails
Siddharth Agarwal
dispatch: print 'abort:' when a pre-command hook fails (BC)...
r19011 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
pats=cmdpats, opts=cmdoptions)
Jordi Gutiérrez Hermoso
dispatch: add fail-* family of hooks...
r29129 try:
ret = _runcommand(ui, options, cmd, d)
# run post-hook, passing command result
hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
result=ret, pats=cmdpats, opts=cmdoptions)
except Exception:
# run failure hook and re-raise
hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
pats=cmdpats, opts=cmdoptions)
raise
Bill Barry
dispatch: extract command execution block into method...
r7819 return ret
Jun Wu
dispatch: add wd parameter to _getlocal...
r28263 def _getlocal(ui, rpath, wd=None):
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536 """Return (path, local ui object) for the given target path.
Martin Geisler
check-code: find trailing whitespace
r12770
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536 Takes paths in [cwd]/.hg/hgrc into account."
"""
Jun Wu
dispatch: add wd parameter to _getlocal...
r28263 if wd is None:
try:
Pulkit Goyal
py3: add os.getcwdb() to have bytes path...
r30500 wd = pycompat.getcwd()
Jun Wu
dispatch: add wd parameter to _getlocal...
r28263 except OSError as e:
raise error.Abort(_("error getting current working directory: %s") %
Augie Fackler
python3: wrap all uses of <exception>.strerror with strtolocal...
r34024 encoding.strtolocal(e.strerror))
Mads Kiilerich
dispatch: give better error message when cwd doesn't exist (issue2293)...
r11675 path = cmdutil.findrepo(wd) or ""
Matt Mackall
dispatch: move command dispatching into its own module...
r5178 if not path:
lui = ui
Andrey Somov
improve code readability
r9436 else:
Brodie Rao
dispatch: remove superfluous try/except when reading local ui config...
r12636 lui = ui.copy()
Brodie Rao
dispatch: properly handle relative path aliases used with -R (issue2376)...
r12637 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Yuya Nishihara
dispatch: convert non-list option parsed by _earlygetopt() to string...
r35062 if rpath:
path = lui.expandpath(rpath)
Matt Mackall
ui: kill most users of parentui name and arg, replace with .copy()
r8190 lui = ui.copy()
Brodie Rao
dispatch: properly handle relative path aliases used with -R (issue2376)...
r12637 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536 return path, lui
Jun Wu
dispatch: always load extensions before running shell aliases (issue5230)...
r29132 def _checkshellalias(lui, ui, args):
"""Return the function to run the shell alias, if it is required"""
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536 options = {}
Steve Losh
alias: fail gracefully when invalid global options are given (issue2442)...
r12748
try:
args = fancyopts.fancyopts(args, commands.globalopts, options)
Pulkit Goyal
fancyopts: switch from fancyopts.getopt.* to getopt.*...
r30576 except getopt.GetoptError:
Steve Losh
alias: fail gracefully when invalid global options are given (issue2442)...
r12748 return
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536
if not args:
return
Jun Wu
dispatch: always load extensions before running shell aliases (issue5230)...
r29132 cmdtable = commands.table
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536
cmd = args[0]
try:
Jun Wu
dispatch: always load extensions before running shell aliases (issue5230)...
r29132 strict = ui.configbool("ui", "strict")
FUJIWARA Katsunori
dispatch: check shell alias again after loading extensions (issue4355)...
r22377 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
Steve Losh
alias: fall back to normal error handling for ambigious commands (fixes issue2475)
r12932 except (error.AmbiguousCommand, error.UnknownCommand):
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536 return
cmd = aliases[0]
fn = entry[0]
Augie Fackler
dispatch: use safehasattr instead of hasattr
r14950 if cmd and util.safehasattr(fn, 'shell'):
Yuya Nishihara
dispatch: verify result of early command parsing...
r35063 # shell alias shouldn't receive early options which are consumed by hg
Yuya Nishihara
dispatch: replace _earlygetopt(strip=True) with new parser...
r35225 _earlyopts, args = _earlysplitopts(args)
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536 d = lambda: fn(ui, *args[1:])
Brodie Rao
cleanup: eradicate long lines
r16683 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
[], {})
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536
Idan Kamara
dispatch: use the request to store the ui object...
r14439 def _dispatch(req):
Idan Kamara
dispatch: wrap dispatch related information in a request class...
r14438 args = req.args
Idan Kamara
dispatch: use the request to store the ui object...
r14439 ui = req.ui
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536 # check for cwd
Yuya Nishihara
dispatch: replace _earlyreq*() with new fancyopts-based parser
r35224 cwd = req.earlyoptions['cwd']
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536 if cwd:
Yuya Nishihara
dispatch: convert non-list option parsed by _earlygetopt() to string...
r35062 os.chdir(cwd)
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536
Yuya Nishihara
dispatch: replace _earlyreq*() with new fancyopts-based parser
r35224 rpath = req.earlyoptions['repository']
Steve Losh
alias: only allow global options before a shell alias, pass later ones through...
r12536 path, lui = _getlocal(ui, rpath)
Martin von Zweigbergk
cleanup: use set literals...
r32291 uis = {ui, lui}
Bryan O'Sullivan
dispatch: move detection of profiling earlier during startup
r30933
if req.repo:
uis.add(req.repo.ui)
Boris Feld
debug: process --debug flag earlier...
r38552 if (req.earlyoptions['verbose'] or req.earlyoptions['debug']
or req.earlyoptions['quiet']):
for opt in ('verbose', 'debug', 'quiet'):
val = pycompat.bytestr(bool(req.earlyoptions[opt]))
for ui_ in uis:
ui_.setconfig('ui', opt, val, '--' + opt)
Yuya Nishihara
dispatch: replace _earlyreq*() with new fancyopts-based parser
r35224 if req.earlyoptions['profile']:
Bryan O'Sullivan
dispatch: move detection of profiling earlier during startup
r30933 for ui_ in uis:
ui_.setconfig('profiling', 'enabled', 'true', '--profile')
profile: drop maybeprofile...
r32788 profile = lui.configbool('profiling', 'enabled')
with profiling.profile(lui, enabled=profile) as profiler:
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
FUJIWARA Katsunori
dispatch: remove unused _loaded...
r33053 # reposetup
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 extensions.loadall(lui)
# Propagate any changes to lui.__class__ by extensions
ui.__class__ = lui.__class__
Kirill Smelkov
dispatch: allow extensions to provide setup code...
r5828
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 # (uisetup and extsetup are handled in extensions.loadall)
Kirill Smelkov
dispatch: allow extensions to provide setup code...
r5828
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 # (reposetup is handled in hg.repository)
Martin Geisler
extensions: load and configure extensions in well-defined phases...
r9410
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 addaliases(lui, commands.table)
Brendan Cully
Move alias into core
r8655
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 # All aliases and commands are completely defined, now.
# Check abbreviation/ambiguity of shell alias.
shellaliasfn = _checkshellalias(lui, ui, args)
if shellaliasfn:
Arun Kulshreshtha
dispatch: make hg --profile wrap reposetup...
r30006 return shellaliasfn()
FUJIWARA Katsunori
dispatch: check shell alias again after loading extensions (issue4355)...
r22377
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 # check for fallback encoding
fallback = lui.config('ui', 'fallbackencoding')
if fallback:
encoding.fallbackencoding = fallback
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 fullargs = args
cmd, func, args, options, cmdoptions = _parse(lui, args)
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Yuya Nishihara
dispatch: verify result of early command parsing...
r35063 if options["config"] != req.earlyoptions["config"]:
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 raise error.Abort(_("option --config may not be abbreviated!"))
Yuya Nishihara
dispatch: verify result of early command parsing...
r35063 if options["cwd"] != req.earlyoptions["cwd"]:
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 raise error.Abort(_("option --cwd may not be abbreviated!"))
Yuya Nishihara
dispatch: verify result of early command parsing...
r35063 if options["repository"] != req.earlyoptions["repository"]:
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 raise error.Abort(_(
"option -R has to be separated from other options (e.g. not "
"-qR) and --repository may only be abbreviated as --repo!"))
Yuya Nishihara
dispatch: abort if early boolean options can't be parsed...
r35059 if options["debugger"] != req.earlyoptions["debugger"]:
raise error.Abort(_("option --debugger may not be abbreviated!"))
# don't validate --profile/--traceback, which can be enabled from now
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 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]))
Bryan O'Sullivan
atexit: switch to home-grown implementation
r31958 ui.atexit(print_time)
profile: support --profile in alias and abbreviated version (--prof)...
r32787 if options["profile"]:
profiler.start()
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Boris Feld
debug: process --debug flag earlier...
r38552 # if abbreviated version of this were used, take them in account, now
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 if options['verbose'] or options['debug'] or options['quiet']:
for opt in ('verbose', 'debug', 'quiet'):
Boris Feld
debug: process --debug flag earlier...
r38552 if options[opt] == req.earlyoptions[opt]:
continue
Yuya Nishihara
py3: replace "if ispy3" by pycompat.bytestr()
r35916 val = pycompat.bytestr(bool(options[opt]))
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 for ui_ in uis:
ui_.setconfig('ui', opt, val, '--' + opt)
if options['traceback']:
Idan Kamara
dispatch: set global options on the request repo.ui...
r14752 for ui_ in uis:
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
Idan Kamara
dispatch: make sure global options on the command line take precedence...
r14992
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 if options['noninteractive']:
for ui_ in uis:
ui_.setconfig('ui', 'interactive', 'off', '-y')
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 if cmdoptions.get('insecure', False):
for ui_ in uis:
ui_.insecureconnections = True
Yuya Nishihara
url: add --insecure option to bypass verification of ssl certificates...
r13328
FUJIWARA Katsunori
dispatch: setup color before pager for correct console information on windows...
r32404 # setup color handling before pager, because setting up pager
# might cause incorrect console information
Pierre-Yves David
color: add a 'ui.color' option to control color behavior...
r31110 coloropt = options['color']
Pierre-Yves David
color: move triggering of the initialisation logic in core...
r31105 for ui_ in uis:
Pierre-Yves David
color: add a 'ui.color' option to control color behavior...
r31110 if coloropt:
ui_.setconfig('ui', 'color', coloropt, '--color')
color.setup(ui_)
Pierre-Yves David
color: move triggering of the initialisation logic in core...
r31105
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 if stringutil.parsebool(options['pager']):
FUJIWARA Katsunori
ui: enable pager always for explicit --pager=on (issue5580)...
r33622 # ui.pager() expects 'internal-always-' prefix in this case
FUJIWARA Katsunori
dispatch: setup color before pager for correct console information on windows...
r32404 ui.pager('internal-always-' + cmd)
elif options['pager'] != 'auto':
Jun Wu
dispatch: when --pager=no is passed, also disable pager on req.repo.ui...
r34639 for ui_ in uis:
ui_.disablepager()
FUJIWARA Katsunori
dispatch: setup color before pager for correct console information on windows...
r32404
Bryan O'Sullivan
dispatch: start profiling earlier...
r30934 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')
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 repo = None
cmdpats = args[:]
Augie Fackler
dispatch: stop supporting non-use of @command...
r30485 if not func.norepo:
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 # use the repo from the request only if we don't have -R
if not rpath and not cwd:
repo = req.repo
Idan Kamara
dispatch: add repo to the request...
r14510
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 if repo:
# set the descriptors of the repo ui to those of ui
repo.ui.fin = ui.fin
repo.ui.fout = ui.fout
repo.ui.ferr = ui.ferr
else:
try:
Jun Wu
dispatch: make request accept additional reposetups...
r32379 repo = hg.repository(ui, path=path,
Gregory Szorc
hg: pass command intents to repo/peer creation (API)...
r37735 presetupfuncs=req.prereposetups,
intents=func.intents)
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 if not repo.local():
raise error.Abort(_("repository '%s' is not local")
% path)
repo.ui.setconfig("bundle", "mainreporoot", repo.root,
'repo')
except error.RequirementError:
Yuya Nishihara
dispatch: error out on invalid -R path even if optionalrepo (issue4805) (BC)...
r26142 raise
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 except error.RepoError:
Yuya Nishihara
dispatch: convert non-list option parsed by _earlygetopt() to string...
r35062 if rpath: # invalid -R path
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 raise
Augie Fackler
dispatch: stop supporting non-use of @command...
r30485 if not func.optionalrepo:
if func.inferrepo and args and not path:
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 # try to infer -R from command args
Pulkit Goyal
py3: use pycompat.maplist() instead of map()...
r35150 repos = pycompat.maplist(cmdutil.findrepo, args)
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 guess = repos[0]
if guess and repos.count(guess) == len(repos):
req.args = ['--repository', guess] + fullargs
Yuya Nishihara
dispatch: replace _earlyreq*() with new fancyopts-based parser
r35224 req.earlyoptions['repository'] = guess
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 return _dispatch(req)
if not path:
raise error.RepoError(_("no repository found in"
" '%s' (.hg not found)")
Pulkit Goyal
py3: use pycompat.getcwd() instead of os.getcwd()...
r30519 % pycompat.getcwd())
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 raise
if repo:
ui = repo.ui
if options['hidden']:
repo = repo.unfiltered()
args.insert(0, repo)
elif rpath:
ui.warn(_("warning: --repository ignored\n"))
Matt Mackall
dispatch: generalize signature checking for extension command wrapping
r7388
Augie Fackler
dispatch: consolidate formatting of arguments...
r31492 msg = _formatargs(fullargs)
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 ui.log("command", '%s\n', msg)
Pulkit Goyal
py3: make keys of keyword arguments strings...
r30586 strcmdopt = pycompat.strkwargs(cmdoptions)
d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
Arun Kulshreshtha
dispatch: change indentation level in _dispatch()...
r30005 try:
return runcommand(lui, repo, cmd, fullargs, ui, options, d,
cmdpats, cmdoptions)
finally:
if repo and repo != req.repo:
repo.close()
Matt Mackall
dispatch: move command dispatching into its own module...
r5178
def _runcommand(ui, options, cmd, cmdfunc):
Gregory Szorc
profiling: add a context manager that no-ops if profiling isn't enabled...
r29784 """Run a command function, possibly with profiling enabled."""
Arun Kulshreshtha
dispatch: make hg --profile wrap reposetup...
r30006 try:
Boris Feld
tracing: trace command function execution...
r39548 with tracing.log("Running %s command" % cmd):
return cmdfunc()
Arun Kulshreshtha
dispatch: make hg --profile wrap reposetup...
r30006 except error.SignatureError:
raise error.CommandError(cmd, _('invalid arguments'))
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784
Martijn Pieters
dispatch: split out warning message generation to separate function...
r28821 def _exceptionwarning(ui):
"""Produce a warning message for the current active exception"""
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784
# For compatibility checking, we discard the portion of the hg
# version after the + on the assumption that if a "normal
# user" is running a build with a + in it the packager
# probably built from fairly close to a tag and anyone with a
# 'make local' copy of hg (where the version number can be out
# of date) will be clueful enough to notice the implausible
# version number and try updating.
ct = util.versiontuple(n=2)
worst = None, ct, ''
Jun Wu
codemod: register core configitems using a script...
r33499 if ui.config('ui', 'supportcontact') is None:
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784 for name, mod in extensions.extensions():
Yuya Nishihara
py3: replace "if ispy3" by pycompat.sysbytes() or util.forcebytestr()
r35917 # 'testedwith' should be bytes, but not all extensions are ported
# to py3 and we don't want UnicodeException because of that.
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 testedwith = stringutil.forcebytestr(getattr(mod, 'testedwith', ''))
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784 report = getattr(mod, 'buglink', _('the extension author.'))
if not testedwith.strip():
# We found an untested extension. It's likely the culprit.
worst = name, 'unknown', report
break
# Never blame on extensions bundled with Mercurial.
Yuya Nishihara
extensions: use ismoduleinternal() thoroughly...
r29884 if extensions.ismoduleinternal(mod):
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784 continue
tested = [util.versiontuple(t, 2) for t in testedwith.split()]
if ct in tested:
continue
lower = [t for t in tested if t < ct]
nearest = max(lower or tested)
if worst[0] is None or nearest < worst[1]:
worst = name, nearest, report
if worst[0] is not None:
name, testedwith, report = worst
Augie Fackler
dispatch: allow testedwith to be bytes or str
r31179 if not isinstance(testedwith, (bytes, str)):
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 testedwith = '.'.join([stringutil.forcebytestr(c)
for c in testedwith])
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784 warning = (_('** Unknown exception encountered with '
'possibly-broken third-party extension %s\n'
'** which supports versions %s of Mercurial.\n'
'** Please disable %s and try your action again.\n'
'** If that fixes the bug please report it to %s\n')
Pulkit Goyal
py3: convert the report to bytes...
r38045 % (name, testedwith, name, stringutil.forcebytestr(report)))
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784 else:
Jun Wu
codemod: register core configitems using a script...
r33499 bugtracker = ui.config('ui', 'supportcontact')
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784 if bugtracker is None:
bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
warning = (_("** unknown exception encountered, "
"please report by visiting\n** ") + bugtracker + '\n')
Yuya Nishihara
py3: replace "if ispy3" by pycompat.sysbytes() or util.forcebytestr()
r35917 sysversion = pycompat.sysbytes(sys.version).replace('\n', '')
Augie Fackler
dispatch: cope with sys.version being unicode on Python 3
r31180 warning += ((_("** Python %s\n") % sysversion) +
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784 (_("** Mercurial Distributed SCM (version %s)\n") %
util.version()) +
(_("** Extensions loaded: %s\n") %
", ".join([x[0] for x in extensions.extensions()])))
Martijn Pieters
dispatch: split out warning message generation to separate function...
r28821 return warning
def handlecommandexception(ui):
"""Produce a warning message for broken commands
Called when handling an exception; the exception is reraised if
this function returns False, ignored otherwise.
"""
warning = _exceptionwarning(ui)
Gregory Szorc
py3: convert traceback representation to bytes when logging...
r36141 ui.log("commandexception", "%s\n%s\n", warning,
pycompat.sysbytes(traceback.format_exc()))
Martijn Pieters
dispatch: factor out command failure handling into a function...
r28784 ui.warn(warning)
return False # re-raise the exception