##// END OF EJS Templates
alias: improve help text for command aliases...
Peter Arrenbrecht -
r9876:6e8a16dd default
parent child Browse files
Show More
@@ -1,500 +1,506
1 1 # dispatch.py - command dispatching for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 from i18n import _
9 9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
10 10 import util, commands, hg, fancyopts, extensions, hook, error
11 11 import cmdutil, encoding
12 12 import ui as _ui
13 13
14 14 def run():
15 15 "run the command in sys.argv"
16 16 sys.exit(dispatch(sys.argv[1:]))
17 17
18 18 def dispatch(args):
19 19 "run the command specified in args"
20 20 try:
21 21 u = _ui.ui()
22 22 if '--traceback' in args:
23 23 u.setconfig('ui', 'traceback', 'on')
24 24 except util.Abort, inst:
25 25 sys.stderr.write(_("abort: %s\n") % inst)
26 26 return -1
27 27 except error.ConfigError, inst:
28 28 sys.stderr.write(_("hg: %s\n") % inst)
29 29 return -1
30 30 return _runcatch(u, args)
31 31
32 32 def _runcatch(ui, args):
33 33 def catchterm(*args):
34 34 raise error.SignalInterrupt
35 35
36 36 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
37 37 num = getattr(signal, name, None)
38 38 if num: signal.signal(num, catchterm)
39 39
40 40 try:
41 41 try:
42 42 # enter the debugger before command execution
43 43 if '--debugger' in args:
44 44 pdb.set_trace()
45 45 try:
46 46 return _dispatch(ui, args)
47 47 finally:
48 48 ui.flush()
49 49 except:
50 50 # enter the debugger when we hit an exception
51 51 if '--debugger' in args:
52 52 pdb.post_mortem(sys.exc_info()[2])
53 53 ui.traceback()
54 54 raise
55 55
56 56 # Global exception handling, alphabetically
57 57 # Mercurial-specific first, followed by built-in and library exceptions
58 58 except error.AmbiguousCommand, inst:
59 59 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
60 60 (inst.args[0], " ".join(inst.args[1])))
61 61 except error.ConfigError, inst:
62 62 ui.warn(_("hg: %s\n") % inst.args[0])
63 63 except error.LockHeld, inst:
64 64 if inst.errno == errno.ETIMEDOUT:
65 65 reason = _('timed out waiting for lock held by %s') % inst.locker
66 66 else:
67 67 reason = _('lock held by %s') % inst.locker
68 68 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
69 69 except error.LockUnavailable, inst:
70 70 ui.warn(_("abort: could not lock %s: %s\n") %
71 71 (inst.desc or inst.filename, inst.strerror))
72 72 except error.ParseError, inst:
73 73 if inst.args[0]:
74 74 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
75 75 commands.help_(ui, inst.args[0])
76 76 else:
77 77 ui.warn(_("hg: %s\n") % inst.args[1])
78 78 commands.help_(ui, 'shortlist')
79 79 except error.RepoError, inst:
80 80 ui.warn(_("abort: %s!\n") % inst)
81 81 except error.ResponseError, inst:
82 82 ui.warn(_("abort: %s") % inst.args[0])
83 83 if not isinstance(inst.args[1], basestring):
84 84 ui.warn(" %r\n" % (inst.args[1],))
85 85 elif not inst.args[1]:
86 86 ui.warn(_(" empty string\n"))
87 87 else:
88 88 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
89 89 except error.RevlogError, inst:
90 90 ui.warn(_("abort: %s!\n") % inst)
91 91 except error.SignalInterrupt:
92 92 ui.warn(_("killed!\n"))
93 93 except error.UnknownCommand, inst:
94 94 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
95 95 commands.help_(ui, 'shortlist')
96 96 except util.Abort, inst:
97 97 ui.warn(_("abort: %s\n") % inst)
98 98 except ImportError, inst:
99 99 m = str(inst).split()[-1]
100 100 ui.warn(_("abort: could not import module %s!\n") % m)
101 101 if m in "mpatch bdiff".split():
102 102 ui.warn(_("(did you forget to compile extensions?)\n"))
103 103 elif m in "zlib".split():
104 104 ui.warn(_("(is your Python install correct?)\n"))
105 105 except IOError, inst:
106 106 if hasattr(inst, "code"):
107 107 ui.warn(_("abort: %s\n") % inst)
108 108 elif hasattr(inst, "reason"):
109 109 try: # usually it is in the form (errno, strerror)
110 110 reason = inst.reason.args[1]
111 111 except: # it might be anything, for example a string
112 112 reason = inst.reason
113 113 ui.warn(_("abort: error: %s\n") % reason)
114 114 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
115 115 if ui.debugflag:
116 116 ui.warn(_("broken pipe\n"))
117 117 elif getattr(inst, "strerror", None):
118 118 if getattr(inst, "filename", None):
119 119 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
120 120 else:
121 121 ui.warn(_("abort: %s\n") % inst.strerror)
122 122 else:
123 123 raise
124 124 except OSError, inst:
125 125 if getattr(inst, "filename", None):
126 126 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
127 127 else:
128 128 ui.warn(_("abort: %s\n") % inst.strerror)
129 129 except KeyboardInterrupt:
130 130 try:
131 131 ui.warn(_("interrupted!\n"))
132 132 except IOError, inst:
133 133 if inst.errno == errno.EPIPE:
134 134 if ui.debugflag:
135 135 ui.warn(_("\nbroken pipe\n"))
136 136 else:
137 137 raise
138 138 except MemoryError:
139 139 ui.warn(_("abort: out of memory\n"))
140 140 except SystemExit, inst:
141 141 # Commands shouldn't sys.exit directly, but give a return code.
142 142 # Just in case catch this and and pass exit code to caller.
143 143 return inst.code
144 144 except socket.error, inst:
145 145 ui.warn(_("abort: %s\n") % inst.args[-1])
146 146 except:
147 147 ui.warn(_("** unknown exception encountered, details follow\n"))
148 148 ui.warn(_("** report bug details to "
149 149 "http://mercurial.selenic.com/bts/\n"))
150 150 ui.warn(_("** or mercurial@selenic.com\n"))
151 151 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
152 152 % util.version())
153 153 ui.warn(_("** Extensions loaded: %s\n")
154 154 % ", ".join([x[0] for x in extensions.extensions()]))
155 155 raise
156 156
157 157 return -1
158 158
159 159 def _findrepo(p):
160 160 while not os.path.isdir(os.path.join(p, ".hg")):
161 161 oldp, p = p, os.path.dirname(p)
162 162 if p == oldp:
163 163 return None
164 164
165 165 return p
166 166
167 167 def aliasargs(fn):
168 168 if hasattr(fn, 'args'):
169 169 return fn.args
170 170 return []
171 171
172 172 class cmdalias(object):
173 173 def __init__(self, name, definition, cmdtable):
174 174 self.name = name
175 175 self.definition = definition
176 176 self.args = []
177 177 self.opts = []
178 178 self.help = ''
179 179 self.norepo = True
180 180
181 181 try:
182 182 cmdutil.findcmd(self.name, cmdtable, True)
183 183 self.shadows = True
184 184 except error.UnknownCommand:
185 185 self.shadows = False
186 186
187 187 if not self.definition:
188 188 def fn(ui, *args):
189 189 ui.warn(_("no definition for alias '%s'\n") % self.name)
190 190 return 1
191 191 self.fn = fn
192 192
193 193 return
194 194
195 195 args = shlex.split(self.definition)
196 196 cmd = args.pop(0)
197 197
198 198 try:
199 199 self.fn, self.opts, self.help = cmdutil.findcmd(cmd, cmdtable, False)[1]
200 200 self.args = aliasargs(self.fn) + args
201 201 if cmd not in commands.norepo.split(' '):
202 202 self.norepo = False
203 if self.help.startswith("hg " + cmd):
204 # drop prefix in old-style help lines so hg shows the alias
205 self.help = self.help[4 + len(cmd):]
206 self.__doc__ = _("alias for: hg %s\n\n%s") \
207 % (definition, self.fn.__doc__)
208
203 209 except error.UnknownCommand:
204 210 def fn(ui, *args):
205 211 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
206 212 % (self.name, cmd))
207 213 return 1
208 214 self.fn = fn
209 215 except error.AmbiguousCommand:
210 216 def fn(ui, *args):
211 217 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
212 218 % (self.name, cmd))
213 219 return 1
214 220 self.fn = fn
215 221
216 222 def __call__(self, ui, *args, **opts):
217 223 if self.shadows:
218 224 ui.debug("alias '%s' shadows command\n" % self.name)
219 225
220 226 return self.fn(ui, *args, **opts)
221 227
222 228 def addaliases(ui, cmdtable):
223 229 # aliases are processed after extensions have been loaded, so they
224 230 # may use extension commands. Aliases can also use other alias definitions,
225 231 # but only if they have been defined prior to the current definition.
226 232 for alias, definition in ui.configitems('alias'):
227 233 aliasdef = cmdalias(alias, definition, cmdtable)
228 234 cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help)
229 235 if aliasdef.norepo:
230 236 commands.norepo += ' %s' % alias
231 237
232 238 def _parse(ui, args):
233 239 options = {}
234 240 cmdoptions = {}
235 241
236 242 try:
237 243 args = fancyopts.fancyopts(args, commands.globalopts, options)
238 244 except fancyopts.getopt.GetoptError, inst:
239 245 raise error.ParseError(None, inst)
240 246
241 247 if args:
242 248 cmd, args = args[0], args[1:]
243 249 aliases, entry = cmdutil.findcmd(cmd, commands.table,
244 250 ui.config("ui", "strict"))
245 251 cmd = aliases[0]
246 252 args = aliasargs(entry[0]) + args
247 253 defaults = ui.config("defaults", cmd)
248 254 if defaults:
249 255 args = map(util.expandpath, shlex.split(defaults)) + args
250 256 c = list(entry[1])
251 257 else:
252 258 cmd = None
253 259 c = []
254 260
255 261 # combine global options into local
256 262 for o in commands.globalopts:
257 263 c.append((o[0], o[1], options[o[1]], o[3]))
258 264
259 265 try:
260 266 args = fancyopts.fancyopts(args, c, cmdoptions, True)
261 267 except fancyopts.getopt.GetoptError, inst:
262 268 raise error.ParseError(cmd, inst)
263 269
264 270 # separate global options back out
265 271 for o in commands.globalopts:
266 272 n = o[1]
267 273 options[n] = cmdoptions[n]
268 274 del cmdoptions[n]
269 275
270 276 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
271 277
272 278 def _parseconfig(ui, config):
273 279 """parse the --config options from the command line"""
274 280 for cfg in config:
275 281 try:
276 282 name, value = cfg.split('=', 1)
277 283 section, name = name.split('.', 1)
278 284 if not section or not name:
279 285 raise IndexError
280 286 ui.setconfig(section, name, value)
281 287 except (IndexError, ValueError):
282 288 raise util.Abort(_('malformed --config option: %r '
283 289 '(use --config section.name=value)') % cfg)
284 290
285 291 def _earlygetopt(aliases, args):
286 292 """Return list of values for an option (or aliases).
287 293
288 294 The values are listed in the order they appear in args.
289 295 The options and values are removed from args.
290 296 """
291 297 try:
292 298 argcount = args.index("--")
293 299 except ValueError:
294 300 argcount = len(args)
295 301 shortopts = [opt for opt in aliases if len(opt) == 2]
296 302 values = []
297 303 pos = 0
298 304 while pos < argcount:
299 305 if args[pos] in aliases:
300 306 if pos + 1 >= argcount:
301 307 # ignore and let getopt report an error if there is no value
302 308 break
303 309 del args[pos]
304 310 values.append(args.pop(pos))
305 311 argcount -= 2
306 312 elif args[pos][:2] in shortopts:
307 313 # short option can have no following space, e.g. hg log -Rfoo
308 314 values.append(args.pop(pos)[2:])
309 315 argcount -= 1
310 316 else:
311 317 pos += 1
312 318 return values
313 319
314 320 def runcommand(lui, repo, cmd, fullargs, ui, options, d):
315 321 # run pre-hook, and abort if it fails
316 322 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
317 323 if ret:
318 324 return ret
319 325 ret = _runcommand(ui, options, cmd, d)
320 326 # run post-hook, passing command result
321 327 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
322 328 result = ret)
323 329 return ret
324 330
325 331 _loaded = set()
326 332 def _dispatch(ui, args):
327 333 # read --config before doing anything else
328 334 # (e.g. to change trust settings for reading .hg/hgrc)
329 335 _parseconfig(ui, _earlygetopt(['--config'], args))
330 336
331 337 # check for cwd
332 338 cwd = _earlygetopt(['--cwd'], args)
333 339 if cwd:
334 340 os.chdir(cwd[-1])
335 341
336 342 # read the local repository .hgrc into a local ui object
337 343 path = _findrepo(os.getcwd()) or ""
338 344 if not path:
339 345 lui = ui
340 346 else:
341 347 try:
342 348 lui = ui.copy()
343 349 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
344 350 except IOError:
345 351 pass
346 352
347 353 # now we can expand paths, even ones in .hg/hgrc
348 354 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
349 355 if rpath:
350 356 path = lui.expandpath(rpath[-1])
351 357 lui = ui.copy()
352 358 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
353 359
354 360 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
355 361 # reposetup. Programs like TortoiseHg will call _dispatch several
356 362 # times so we keep track of configured extensions in _loaded.
357 363 extensions.loadall(lui)
358 364 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
359 365
360 366 # (uisetup and extsetup are handled in extensions.loadall)
361 367
362 368 for name, module in exts:
363 369 cmdtable = getattr(module, 'cmdtable', {})
364 370 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
365 371 if overrides:
366 372 ui.warn(_("extension '%s' overrides commands: %s\n")
367 373 % (name, " ".join(overrides)))
368 374 commands.table.update(cmdtable)
369 375 _loaded.add(name)
370 376
371 377 # (reposetup is handled in hg.repository)
372 378
373 379 addaliases(lui, commands.table)
374 380
375 381 # check for fallback encoding
376 382 fallback = lui.config('ui', 'fallbackencoding')
377 383 if fallback:
378 384 encoding.fallbackencoding = fallback
379 385
380 386 fullargs = args
381 387 cmd, func, args, options, cmdoptions = _parse(lui, args)
382 388
383 389 if options["config"]:
384 390 raise util.Abort(_("Option --config may not be abbreviated!"))
385 391 if options["cwd"]:
386 392 raise util.Abort(_("Option --cwd may not be abbreviated!"))
387 393 if options["repository"]:
388 394 raise util.Abort(_(
389 395 "Option -R has to be separated from other options (e.g. not -qR) "
390 396 "and --repository may only be abbreviated as --repo!"))
391 397
392 398 if options["encoding"]:
393 399 encoding.encoding = options["encoding"]
394 400 if options["encodingmode"]:
395 401 encoding.encodingmode = options["encodingmode"]
396 402 if options["time"]:
397 403 def get_times():
398 404 t = os.times()
399 405 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
400 406 t = (t[0], t[1], t[2], t[3], time.clock())
401 407 return t
402 408 s = get_times()
403 409 def print_time():
404 410 t = get_times()
405 411 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
406 412 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
407 413 atexit.register(print_time)
408 414
409 415 if options['verbose'] or options['debug'] or options['quiet']:
410 416 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
411 417 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
412 418 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
413 419 if options['traceback']:
414 420 ui.setconfig('ui', 'traceback', 'on')
415 421 if options['noninteractive']:
416 422 ui.setconfig('ui', 'interactive', 'off')
417 423
418 424 if options['help']:
419 425 return commands.help_(ui, cmd, options['version'])
420 426 elif options['version']:
421 427 return commands.version_(ui)
422 428 elif not cmd:
423 429 return commands.help_(ui, 'shortlist')
424 430
425 431 repo = None
426 432 if cmd not in commands.norepo.split():
427 433 try:
428 434 repo = hg.repository(ui, path=path)
429 435 ui = repo.ui
430 436 if not repo.local():
431 437 raise util.Abort(_("repository '%s' is not local") % path)
432 438 ui.setconfig("bundle", "mainreporoot", repo.root)
433 439 except error.RepoError:
434 440 if cmd not in commands.optionalrepo.split():
435 441 if args and not path: # try to infer -R from command args
436 442 repos = map(_findrepo, args)
437 443 guess = repos[0]
438 444 if guess and repos.count(guess) == len(repos):
439 445 return _dispatch(ui, ['--repository', guess] + fullargs)
440 446 if not path:
441 447 raise error.RepoError(_("There is no Mercurial repository"
442 448 " here (.hg not found)"))
443 449 raise
444 450 args.insert(0, repo)
445 451 elif rpath:
446 452 ui.warn("warning: --repository ignored\n")
447 453
448 454 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
449 455 return runcommand(lui, repo, cmd, fullargs, ui, options, d)
450 456
451 457 def _runcommand(ui, options, cmd, cmdfunc):
452 458 def checkargs():
453 459 try:
454 460 return cmdfunc()
455 461 except error.SignatureError:
456 462 raise error.ParseError(cmd, _("invalid arguments"))
457 463
458 464 if options['profile']:
459 465 format = ui.config('profiling', 'format', default='text')
460 466
461 467 if not format in ['text', 'kcachegrind']:
462 468 ui.warn(_("unrecognized profiling format '%s'"
463 469 " - Ignored\n") % format)
464 470 format = 'text'
465 471
466 472 output = ui.config('profiling', 'output')
467 473
468 474 if output:
469 475 path = ui.expandpath(output)
470 476 ostream = open(path, 'wb')
471 477 else:
472 478 ostream = sys.stderr
473 479
474 480 try:
475 481 from mercurial import lsprof
476 482 except ImportError:
477 483 raise util.Abort(_(
478 484 'lsprof not available - install from '
479 485 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
480 486 p = lsprof.Profiler()
481 487 p.enable(subcalls=True)
482 488 try:
483 489 return checkargs()
484 490 finally:
485 491 p.disable()
486 492
487 493 if format == 'kcachegrind':
488 494 import lsprofcalltree
489 495 calltree = lsprofcalltree.KCacheGrind(p)
490 496 calltree.output(ostream)
491 497 else:
492 498 # format == 'text'
493 499 stats = lsprof.Stats(p.getstats())
494 500 stats.sort()
495 501 stats.pprint(top=10, file=ostream, climit=5)
496 502
497 503 if output:
498 504 ostream.close()
499 505 else:
500 506 return checkargs()
General Comments 0
You need to be logged in to leave comments. Login now