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