##// END OF EJS Templates
alias: on --debug, print expansion when it has invalid arguments
Brodie Rao -
r12093:cd895084 default
parent child Browse files
Show More
@@ -1,588 +1,593
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 193 self.cmdname = ''
194 194 self.definition = definition
195 195 self.args = []
196 196 self.opts = []
197 197 self.help = ''
198 198 self.norepo = True
199 199 self.badalias = False
200 200
201 201 try:
202 202 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
203 203 for alias, e in cmdtable.iteritems():
204 204 if e is entry:
205 205 self.cmd = alias
206 206 break
207 207 self.shadows = True
208 208 except error.UnknownCommand:
209 209 self.shadows = False
210 210
211 211 if not self.definition:
212 212 def fn(ui, *args):
213 213 ui.warn(_("no definition for alias '%s'\n") % self.name)
214 214 return 1
215 215 self.fn = fn
216 216 self.badalias = True
217 217
218 218 return
219 219
220 220 if self.definition.startswith('!'):
221 221 def fn(ui, *args):
222 222 env = {'HG_ARGS': ' '.join((self.name,) + args)}
223 223 def _checkvar(m):
224 224 if int(m.groups()[0]) <= len(args):
225 225 return m.group()
226 226 else:
227 227 return ''
228 228 cmd = re.sub(r'\$(\d+)', _checkvar, self.definition[1:])
229 229 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
230 230 replace['0'] = self.name
231 231 replace['@'] = ' '.join(args)
232 232 cmd = util.interpolate(r'\$', replace, cmd)
233 233 return util.system(cmd, environ=env)
234 234 self.fn = fn
235 235 return
236 236
237 237 args = shlex.split(self.definition)
238 238 self.cmdname = cmd = args.pop(0)
239 239 args = map(util.expandpath, args)
240 240
241 241 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
242 242 if _earlygetopt([invalidarg], args):
243 243 def fn(ui, *args):
244 244 ui.warn(_("error in definition for alias '%s': %s may only "
245 245 "be given on the command line\n")
246 246 % (self.name, invalidarg))
247 247 return 1
248 248
249 249 self.fn = fn
250 250 self.badalias = True
251 251 return
252 252
253 253 try:
254 254 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
255 255 if len(tableentry) > 2:
256 256 self.fn, self.opts, self.help = tableentry
257 257 else:
258 258 self.fn, self.opts = tableentry
259 259
260 260 self.args = aliasargs(self.fn) + args
261 261 if cmd not in commands.norepo.split(' '):
262 262 self.norepo = False
263 263 if self.help.startswith("hg " + cmd):
264 264 # drop prefix in old-style help lines so hg shows the alias
265 265 self.help = self.help[4 + len(cmd):]
266 266 self.__doc__ = self.fn.__doc__
267 267
268 268 except error.UnknownCommand:
269 269 def fn(ui, *args):
270 270 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
271 271 % (self.name, cmd))
272 272 try:
273 273 # check if the command is in a disabled extension
274 274 commands.help_(ui, cmd, unknowncmd=True)
275 275 except error.UnknownCommand:
276 276 pass
277 277 return 1
278 278 self.fn = fn
279 279 self.badalias = True
280 280 except error.AmbiguousCommand:
281 281 def fn(ui, *args):
282 282 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
283 283 % (self.name, cmd))
284 284 return 1
285 285 self.fn = fn
286 286 self.badalias = True
287 287
288 288 def __call__(self, ui, *args, **opts):
289 289 if self.shadows:
290 290 ui.debug("alias '%s' shadows command '%s'\n" %
291 291 (self.name, self.cmdname))
292 292
293 293 if self.definition.startswith('!'):
294 294 return self.fn(ui, *args, **opts)
295 295 else:
296 return util.checksignature(self.fn)(ui, *args, **opts)
296 try:
297 util.checksignature(self.fn)(ui, *args, **opts)
298 except error.SignatureError:
299 args = ' '.join([self.cmdname] + self.args)
300 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
301 raise
297 302
298 303 def addaliases(ui, cmdtable):
299 304 # aliases are processed after extensions have been loaded, so they
300 305 # may use extension commands. Aliases can also use other alias definitions,
301 306 # but only if they have been defined prior to the current definition.
302 307 for alias, definition in ui.configitems('alias'):
303 308 aliasdef = cmdalias(alias, definition, cmdtable)
304 309 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
305 310 if aliasdef.norepo:
306 311 commands.norepo += ' %s' % alias
307 312
308 313 def _parse(ui, args):
309 314 options = {}
310 315 cmdoptions = {}
311 316
312 317 try:
313 318 args = fancyopts.fancyopts(args, commands.globalopts, options)
314 319 except fancyopts.getopt.GetoptError, inst:
315 320 raise error.CommandError(None, inst)
316 321
317 322 if args:
318 323 cmd, args = args[0], args[1:]
319 324 aliases, entry = cmdutil.findcmd(cmd, commands.table,
320 325 ui.config("ui", "strict"))
321 326 cmd = aliases[0]
322 327 args = aliasargs(entry[0]) + args
323 328 defaults = ui.config("defaults", cmd)
324 329 if defaults:
325 330 args = map(util.expandpath, shlex.split(defaults)) + args
326 331 c = list(entry[1])
327 332 else:
328 333 cmd = None
329 334 c = []
330 335
331 336 # combine global options into local
332 337 for o in commands.globalopts:
333 338 c.append((o[0], o[1], options[o[1]], o[3]))
334 339
335 340 try:
336 341 args = fancyopts.fancyopts(args, c, cmdoptions, True)
337 342 except fancyopts.getopt.GetoptError, inst:
338 343 raise error.CommandError(cmd, inst)
339 344
340 345 # separate global options back out
341 346 for o in commands.globalopts:
342 347 n = o[1]
343 348 options[n] = cmdoptions[n]
344 349 del cmdoptions[n]
345 350
346 351 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
347 352
348 353 def _parseconfig(ui, config):
349 354 """parse the --config options from the command line"""
350 355 for cfg in config:
351 356 try:
352 357 name, value = cfg.split('=', 1)
353 358 section, name = name.split('.', 1)
354 359 if not section or not name:
355 360 raise IndexError
356 361 ui.setconfig(section, name, value)
357 362 except (IndexError, ValueError):
358 363 raise util.Abort(_('malformed --config option: %r '
359 364 '(use --config section.name=value)') % cfg)
360 365
361 366 def _earlygetopt(aliases, args):
362 367 """Return list of values for an option (or aliases).
363 368
364 369 The values are listed in the order they appear in args.
365 370 The options and values are removed from args.
366 371 """
367 372 try:
368 373 argcount = args.index("--")
369 374 except ValueError:
370 375 argcount = len(args)
371 376 shortopts = [opt for opt in aliases if len(opt) == 2]
372 377 values = []
373 378 pos = 0
374 379 while pos < argcount:
375 380 if args[pos] in aliases:
376 381 if pos + 1 >= argcount:
377 382 # ignore and let getopt report an error if there is no value
378 383 break
379 384 del args[pos]
380 385 values.append(args.pop(pos))
381 386 argcount -= 2
382 387 elif args[pos][:2] in shortopts:
383 388 # short option can have no following space, e.g. hg log -Rfoo
384 389 values.append(args.pop(pos)[2:])
385 390 argcount -= 1
386 391 else:
387 392 pos += 1
388 393 return values
389 394
390 395 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
391 396 # run pre-hook, and abort if it fails
392 397 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
393 398 pats=cmdpats, opts=cmdoptions)
394 399 if ret:
395 400 return ret
396 401 ret = _runcommand(ui, options, cmd, d)
397 402 # run post-hook, passing command result
398 403 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
399 404 result=ret, pats=cmdpats, opts=cmdoptions)
400 405 return ret
401 406
402 407 _loaded = set()
403 408 def _dispatch(ui, args):
404 409 # read --config before doing anything else
405 410 # (e.g. to change trust settings for reading .hg/hgrc)
406 411 _parseconfig(ui, _earlygetopt(['--config'], args))
407 412
408 413 # check for cwd
409 414 cwd = _earlygetopt(['--cwd'], args)
410 415 if cwd:
411 416 os.chdir(cwd[-1])
412 417
413 418 # read the local repository .hgrc into a local ui object
414 419 try:
415 420 wd = os.getcwd()
416 421 except OSError, e:
417 422 raise util.Abort(_("error getting current working directory: %s") %
418 423 e.strerror)
419 424 path = cmdutil.findrepo(wd) or ""
420 425 if not path:
421 426 lui = ui
422 427 else:
423 428 try:
424 429 lui = ui.copy()
425 430 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
426 431 except IOError:
427 432 pass
428 433
429 434 # now we can expand paths, even ones in .hg/hgrc
430 435 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
431 436 if rpath:
432 437 path = lui.expandpath(rpath[-1])
433 438 lui = ui.copy()
434 439 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
435 440
436 441 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
437 442 # reposetup. Programs like TortoiseHg will call _dispatch several
438 443 # times so we keep track of configured extensions in _loaded.
439 444 extensions.loadall(lui)
440 445 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
441 446 # Propagate any changes to lui.__class__ by extensions
442 447 ui.__class__ = lui.__class__
443 448
444 449 # (uisetup and extsetup are handled in extensions.loadall)
445 450
446 451 for name, module in exts:
447 452 cmdtable = getattr(module, 'cmdtable', {})
448 453 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
449 454 if overrides:
450 455 ui.warn(_("extension '%s' overrides commands: %s\n")
451 456 % (name, " ".join(overrides)))
452 457 commands.table.update(cmdtable)
453 458 _loaded.add(name)
454 459
455 460 # (reposetup is handled in hg.repository)
456 461
457 462 addaliases(lui, commands.table)
458 463
459 464 # check for fallback encoding
460 465 fallback = lui.config('ui', 'fallbackencoding')
461 466 if fallback:
462 467 encoding.fallbackencoding = fallback
463 468
464 469 fullargs = args
465 470 cmd, func, args, options, cmdoptions = _parse(lui, args)
466 471
467 472 if options["config"]:
468 473 raise util.Abort(_("option --config may not be abbreviated!"))
469 474 if options["cwd"]:
470 475 raise util.Abort(_("option --cwd may not be abbreviated!"))
471 476 if options["repository"]:
472 477 raise util.Abort(_(
473 478 "Option -R has to be separated from other options (e.g. not -qR) "
474 479 "and --repository may only be abbreviated as --repo!"))
475 480
476 481 if options["encoding"]:
477 482 encoding.encoding = options["encoding"]
478 483 if options["encodingmode"]:
479 484 encoding.encodingmode = options["encodingmode"]
480 485 if options["time"]:
481 486 def get_times():
482 487 t = os.times()
483 488 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
484 489 t = (t[0], t[1], t[2], t[3], time.clock())
485 490 return t
486 491 s = get_times()
487 492 def print_time():
488 493 t = get_times()
489 494 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
490 495 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
491 496 atexit.register(print_time)
492 497
493 498 if options['verbose'] or options['debug'] or options['quiet']:
494 499 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
495 500 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
496 501 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
497 502 if options['traceback']:
498 503 ui.setconfig('ui', 'traceback', 'on')
499 504 if options['noninteractive']:
500 505 ui.setconfig('ui', 'interactive', 'off')
501 506
502 507 if options['help']:
503 508 return commands.help_(ui, cmd, options['version'])
504 509 elif options['version']:
505 510 return commands.version_(ui)
506 511 elif not cmd:
507 512 return commands.help_(ui, 'shortlist')
508 513
509 514 repo = None
510 515 cmdpats = args[:]
511 516 if cmd not in commands.norepo.split():
512 517 try:
513 518 repo = hg.repository(ui, path=path)
514 519 ui = repo.ui
515 520 if not repo.local():
516 521 raise util.Abort(_("repository '%s' is not local") % path)
517 522 ui.setconfig("bundle", "mainreporoot", repo.root)
518 523 except error.RepoError:
519 524 if cmd not in commands.optionalrepo.split():
520 525 if args and not path: # try to infer -R from command args
521 526 repos = map(cmdutil.findrepo, args)
522 527 guess = repos[0]
523 528 if guess and repos.count(guess) == len(repos):
524 529 return _dispatch(ui, ['--repository', guess] + fullargs)
525 530 if not path:
526 531 raise error.RepoError(_("There is no Mercurial repository"
527 532 " here (.hg not found)"))
528 533 raise
529 534 args.insert(0, repo)
530 535 elif rpath:
531 536 ui.warn(_("warning: --repository ignored\n"))
532 537
533 538 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
534 539 ui.log("command", msg + "\n")
535 540 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
536 541 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
537 542 cmdpats, cmdoptions)
538 543
539 544 def _runcommand(ui, options, cmd, cmdfunc):
540 545 def checkargs():
541 546 try:
542 547 return cmdfunc()
543 548 except error.SignatureError:
544 549 raise error.CommandError(cmd, _("invalid arguments"))
545 550
546 551 if options['profile']:
547 552 format = ui.config('profiling', 'format', default='text')
548 553
549 554 if not format in ['text', 'kcachegrind']:
550 555 ui.warn(_("unrecognized profiling format '%s'"
551 556 " - Ignored\n") % format)
552 557 format = 'text'
553 558
554 559 output = ui.config('profiling', 'output')
555 560
556 561 if output:
557 562 path = ui.expandpath(output)
558 563 ostream = open(path, 'wb')
559 564 else:
560 565 ostream = sys.stderr
561 566
562 567 try:
563 568 from mercurial import lsprof
564 569 except ImportError:
565 570 raise util.Abort(_(
566 571 'lsprof not available - install from '
567 572 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
568 573 p = lsprof.Profiler()
569 574 p.enable(subcalls=True)
570 575 try:
571 576 return checkargs()
572 577 finally:
573 578 p.disable()
574 579
575 580 if format == 'kcachegrind':
576 581 import lsprofcalltree
577 582 calltree = lsprofcalltree.KCacheGrind(p)
578 583 calltree.output(ostream)
579 584 else:
580 585 # format == 'text'
581 586 stats = lsprof.Stats(p.getstats())
582 587 stats.sort()
583 588 stats.pprint(top=10, file=ostream, climit=5)
584 589
585 590 if output:
586 591 ostream.close()
587 592 else:
588 593 return checkargs()
General Comments 0
You need to be logged in to leave comments. Login now