1 # dispatch.py - command dispatching for mercurial
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
15 "run the command in sys.argv"
16 sys.exit(dispatch(sys.argv[1:]))
19 "run the command specified in args"
22 if '--traceback' in args:
23 u.setconfig('ui', 'traceback', 'on')
24 except util.Abort, inst:
25 sys.stderr.write(_("abort: %s\n") % inst)
27 return _runcatch(u, args)
29 def _runcatch(ui, args):
31 raise error.SignalInterrupt
33 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
34 num = getattr(signal, name, None)
35 if num: signal.signal(num, catchterm)
39 # enter the debugger before command execution
40 if '--debugger' in args:
43 return _dispatch(ui, args)
47 # enter the debugger when we hit an exception
48 if '--debugger' in args:
49 pdb.post_mortem(sys.exc_info()[2])
53 # Global exception handling, alphabetically
54 # Mercurial-specific first, followed by built-in and library exceptions
55 except error.AmbiguousCommand, inst:
56 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
57 (inst.args[0], " ".join(inst.args[1])))
58 except error.ConfigError, inst:
59 ui.warn(_("hg: %s\n") % inst.args[0])
60 except error.LockHeld, inst:
61 if inst.errno == errno.ETIMEDOUT:
62 reason = _('timed out waiting for lock held by %s') % inst.locker
64 reason = _('lock held by %s') % inst.locker
65 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
66 except error.LockUnavailable, inst:
67 ui.warn(_("abort: could not lock %s: %s\n") %
68 (inst.desc or inst.filename, inst.strerror))
69 except error.ParseError, inst:
71 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
72 commands.help_(ui, inst.args[0])
74 ui.warn(_("hg: %s\n") % inst.args[1])
75 commands.help_(ui, 'shortlist')
76 except error.RepoError, inst:
77 ui.warn(_("abort: %s!\n") % inst)
78 except error.ResponseError, inst:
79 ui.warn(_("abort: %s") % inst.args[0])
80 if not isinstance(inst.args[1], basestring):
81 ui.warn(" %r\n" % (inst.args[1],))
82 elif not inst.args[1]:
83 ui.warn(_(" empty string\n"))
85 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
86 except error.RevlogError, inst:
87 ui.warn(_("abort: %s!\n") % inst)
88 except error.SignalInterrupt:
89 ui.warn(_("killed!\n"))
90 except error.UnknownCommand, inst:
91 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
92 commands.help_(ui, 'shortlist')
93 except util.Abort, inst:
94 ui.warn(_("abort: %s\n") % inst)
95 except ImportError, inst:
96 m = str(inst).split()[-1]
97 ui.warn(_("abort: could not import module %s!\n") % m)
98 if m in "mpatch bdiff".split():
99 ui.warn(_("(did you forget to compile extensions?)\n"))
100 elif m in "zlib".split():
101 ui.warn(_("(is your Python install correct?)\n"))
102 except IOError, inst:
103 if hasattr(inst, "code"):
104 ui.warn(_("abort: %s\n") % inst)
105 elif hasattr(inst, "reason"):
106 try: # usually it is in the form (errno, strerror)
107 reason = inst.reason.args[1]
108 except: # it might be anything, for example a string
110 ui.warn(_("abort: error: %s\n") % reason)
111 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
113 ui.warn(_("broken pipe\n"))
114 elif getattr(inst, "strerror", None):
115 if getattr(inst, "filename", None):
116 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
118 ui.warn(_("abort: %s\n") % inst.strerror)
121 except OSError, inst:
122 if getattr(inst, "filename", None):
123 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
125 ui.warn(_("abort: %s\n") % inst.strerror)
126 except KeyboardInterrupt:
128 ui.warn(_("interrupted!\n"))
129 except IOError, inst:
130 if inst.errno == errno.EPIPE:
132 ui.warn(_("\nbroken pipe\n"))
136 ui.warn(_("abort: out of memory\n"))
137 except SystemExit, inst:
138 # Commands shouldn't sys.exit directly, but give a return code.
139 # Just in case catch this and and pass exit code to caller.
141 except socket.error, inst:
142 ui.warn(_("abort: %s\n") % inst.args[-1])
144 ui.warn(_("** unknown exception encountered, details follow\n"))
145 ui.warn(_("** report bug details to "
146 "http://mercurial.selenic.com/bts/\n"))
147 ui.warn(_("** or mercurial@selenic.com\n"))
148 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
150 ui.warn(_("** Extensions loaded: %s\n")
151 % ", ".join([x[0] for x in extensions.extensions()]))
157 while not os.path.isdir(os.path.join(p, ".hg")):
158 oldp, p = p, os.path.dirname(p)
165 if hasattr(fn, 'args'):
169 class cmdalias(object):
170 def __init__(self, name, definition, cmdtable):
172 self.definition = definition
179 cmdutil.findcmd(self.name, cmdtable, True)
181 except error.UnknownCommand:
184 if not self.definition:
186 ui.warn(_("no definition for alias '%s'\n") % self.name)
192 args = shlex.split(self.definition)
198 self.fn, self.opts, self.help = cmdutil.findcmd(cmd, cmdtable, False)[1]
199 self.args = aliasargs(self.fn) + args
200 if cmd not in commands.norepo.split(' '):
202 except error.UnknownCommand:
204 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
208 except error.AmbiguousCommand:
210 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
215 def __call__(self, ui, *args, **opts):
217 ui.debug(_("alias '%s' shadows command\n") % self.name)
219 return self.fn(ui, *args, **opts)
221 def addaliases(ui, cmdtable):
222 # aliases are processed after extensions have been loaded, so they
223 # may use extension commands. Aliases can also use other alias definitions,
224 # but only if they have been defined prior to the current definition.
225 for alias, definition in ui.configitems('alias'):
226 aliasdef = cmdalias(alias, definition, cmdtable)
227 cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help)
229 commands.norepo += ' %s' % alias
231 def _parse(ui, args):
236 args = fancyopts.fancyopts(args, commands.globalopts, options)
237 except fancyopts.getopt.GetoptError, inst:
238 raise error.ParseError(None, inst)
241 cmd, args = args[0], args[1:]
242 aliases, i = cmdutil.findcmd(cmd, commands.table,
243 ui.config("ui", "strict"))
245 args = aliasargs(i[0]) + args
246 defaults = ui.config("defaults", cmd)
248 args = shlex.split(defaults) + args
254 # combine global options into local
255 for o in commands.globalopts:
256 c.append((o[0], o[1], options[o[1]], o[3]))
259 args = fancyopts.fancyopts(args, c, cmdoptions, True)
260 except fancyopts.getopt.GetoptError, inst:
261 raise error.ParseError(cmd, inst)
263 # separate global options back out
264 for o in commands.globalopts:
266 options[n] = cmdoptions[n]
269 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
271 def _parseconfig(ui, config):
272 """parse the --config options from the command line"""
275 name, value = cfg.split('=', 1)
276 section, name = name.split('.', 1)
277 if not section or not name:
279 ui.setconfig(section, name, value)
280 except (IndexError, ValueError):
281 raise util.Abort(_('malformed --config option: %s') % cfg)
283 def _earlygetopt(aliases, args):
284 """Return list of values for an option (or aliases).
286 The values are listed in the order they appear in args.
287 The options and values are removed from args.
290 argcount = args.index("--")
293 shortopts = [opt for opt in aliases if len(opt) == 2]
296 while pos < argcount:
297 if args[pos] in aliases:
298 if pos + 1 >= argcount:
299 # ignore and let getopt report an error if there is no value
302 values.append(args.pop(pos))
304 elif args[pos][:2] in shortopts:
305 # short option can have no following space, e.g. hg log -Rfoo
306 values.append(args.pop(pos)[2:])
312 def runcommand(lui, repo, cmd, fullargs, ui, options, d):
313 # run pre-hook, and abort if it fails
314 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
317 ret = _runcommand(ui, options, cmd, d)
318 # run post-hook, passing command result
319 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
324 def _dispatch(ui, args):
325 # read --config before doing anything else
326 # (e.g. to change trust settings for reading .hg/hgrc)
327 _parseconfig(ui, _earlygetopt(['--config'], args))
330 cwd = _earlygetopt(['--cwd'], args)
334 # read the local repository .hgrc into a local ui object
335 path = _findrepo(os.getcwd()) or ""
341 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
345 # now we can expand paths, even ones in .hg/hgrc
346 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
348 path = lui.expandpath(rpath[-1])
350 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
352 extensions.loadall(lui)
353 for name, module in extensions.extensions():
358 # TODO this should be generalized to scheme, where extensions can
359 # redepend on other extensions. then we should toposort them, and
360 # do initialization in correct order
361 extsetup = getattr(module, 'extsetup', None)
365 cmdtable = getattr(module, 'cmdtable', {})
366 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
368 ui.warn(_("extension '%s' overrides commands: %s\n")
369 % (name, " ".join(overrides)))
370 commands.table.update(cmdtable)
373 addaliases(lui, commands.table)
375 # check for fallback encoding
376 fallback = lui.config('ui', 'fallbackencoding')
378 encoding.fallbackencoding = fallback
381 cmd, func, args, options, cmdoptions = _parse(lui, args)
383 if options["config"]:
384 raise util.Abort(_("Option --config may not be abbreviated!"))
386 raise util.Abort(_("Option --cwd may not be abbreviated!"))
387 if options["repository"]:
389 "Option -R has to be separated from other options (e.g. not -qR) "
390 "and --repository may only be abbreviated as --repo!"))
392 if options["encoding"]:
393 encoding.encoding = options["encoding"]
394 if options["encodingmode"]:
395 encoding.encodingmode = options["encodingmode"]
399 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
400 t = (t[0], t[1], t[2], t[3], time.clock())
405 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
406 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
407 atexit.register(print_time)
409 if options['verbose'] or options['debug'] or options['quiet']:
410 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
411 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
412 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
413 if options['traceback']:
414 ui.setconfig('ui', 'traceback', 'on')
415 if options['noninteractive']:
416 ui.setconfig('ui', 'interactive', 'off')
419 return commands.help_(ui, cmd, options['version'])
420 elif options['version']:
421 return commands.version_(ui)
423 return commands.help_(ui, 'shortlist')
426 if cmd not in commands.norepo.split():
428 repo = hg.repository(ui, path=path)
431 raise util.Abort(_("repository '%s' is not local") % path)
432 ui.setconfig("bundle", "mainreporoot", repo.root)
433 except error.RepoError:
434 if cmd not in commands.optionalrepo.split():
435 if args and not path: # try to infer -R from command args
436 repos = map(_findrepo, args)
438 if guess and repos.count(guess) == len(repos):
439 return _dispatch(ui, ['--repository', guess] + fullargs)
441 raise error.RepoError(_("There is no Mercurial repository"
442 " here (.hg not found)"))
446 ui.warn("warning: --repository ignored\n")
448 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
449 return runcommand(lui, repo, cmd, fullargs, ui, options, d)
451 def _runcommand(ui, options, cmd, cmdfunc):
455 except error.SignatureError:
456 raise error.ParseError(cmd, _("invalid arguments"))
458 if options['profile']:
459 format = ui.config('profiling', 'format', default='text')
461 if not format in ['text', 'kcachegrind']:
462 ui.warn(_("unrecognized profiling format '%s'"
463 " - Ignored\n") % format)
466 output = ui.config('profiling', 'output')
469 path = os.path.expanduser(output)
470 path = ui.expandpath(path)
471 ostream = open(path, 'wb')
476 from mercurial import lsprof
479 'lsprof not available - install from '
480 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
481 p = lsprof.Profiler()
482 p.enable(subcalls=True)
488 if format == 'kcachegrind':
489 import lsprofcalltree
490 calltree = lsprofcalltree.KCacheGrind(p)
491 calltree.output(ostream)
494 stats = lsprof.Stats(p.getstats())
496 stats.pprint(top=10, file=ostream, climit=5)