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