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