##// END OF EJS Templates
dispatch: add ability to specify a custom pdb module as a debugger...
Sean Farley -
r19640:472fa3b7 default
parent child Browse files
Show More
@@ -1,872 +1,897
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 class request(object):
15 15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 16 ferr=None):
17 17 self.args = args
18 18 self.ui = ui
19 19 self.repo = repo
20 20
21 21 # input/output/error streams
22 22 self.fin = fin
23 23 self.fout = fout
24 24 self.ferr = ferr
25 25
26 26 def run():
27 27 "run the command in sys.argv"
28 28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29 29
30 30 def dispatch(req):
31 31 "run the command specified in req.args"
32 32 if req.ferr:
33 33 ferr = req.ferr
34 34 elif req.ui:
35 35 ferr = req.ui.ferr
36 36 else:
37 37 ferr = sys.stderr
38 38
39 39 try:
40 40 if not req.ui:
41 41 req.ui = uimod.ui()
42 42 if '--traceback' in req.args:
43 43 req.ui.setconfig('ui', 'traceback', 'on')
44 44
45 45 # set ui streams from the request
46 46 if req.fin:
47 47 req.ui.fin = req.fin
48 48 if req.fout:
49 49 req.ui.fout = req.fout
50 50 if req.ferr:
51 51 req.ui.ferr = req.ferr
52 52 except util.Abort, inst:
53 53 ferr.write(_("abort: %s\n") % inst)
54 54 if inst.hint:
55 55 ferr.write(_("(%s)\n") % inst.hint)
56 56 return -1
57 57 except error.ParseError, inst:
58 58 if len(inst.args) > 1:
59 59 ferr.write(_("hg: parse error at %s: %s\n") %
60 60 (inst.args[1], inst.args[0]))
61 61 else:
62 62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
63 63 return -1
64 64
65 65 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
66 66 starttime = time.time()
67 67 ret = None
68 68 try:
69 69 ret = _runcatch(req)
70 70 return ret
71 71 finally:
72 72 duration = time.time() - starttime
73 73 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
74 74 msg, ret or 0, duration)
75 75
76 76 def _runcatch(req):
77 77 def catchterm(*args):
78 78 raise error.SignalInterrupt
79 79
80 80 ui = req.ui
81 81 try:
82 82 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
83 83 num = getattr(signal, name, None)
84 84 if num:
85 85 signal.signal(num, catchterm)
86 86 except ValueError:
87 87 pass # happens if called in a thread
88 88
89 89 try:
90 90 try:
91 debugger = 'pdb'
92 debugtrace = {
93 'pdb' : pdb.set_trace
94 }
95 debugmortem = {
96 'pdb' : pdb.post_mortem
97 }
91 98
92 99 # read --config before doing anything else
93 100 # (e.g. to change trust settings for reading .hg/hgrc)
94 101 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
95 102
96 103 if req.repo:
97 104 # copy configs that were passed on the cmdline (--config) to
98 105 # the repo ui
99 106 for cfg in cfgs:
100 107 req.repo.ui.setconfig(*cfg)
101 108
109 debugger = ui.config("ui", "debugger")
110 if not debugger:
111 debugger = 'pdb'
112
113 try:
114 debugmod = __import__(debugger)
115 except ImportError:
116 debugmod = pdb
117
118 debugtrace[debugger] = debugmod.set_trace
119 debugmortem[debugger] = debugmod.post_mortem
120
102 121 # enter the debugger before command execution
103 122 if '--debugger' in req.args:
104 123 ui.warn(_("entering debugger - "
105 124 "type c to continue starting hg or h for help\n"))
106 pdb.set_trace()
125
126 if (debugger != 'pdb' and
127 debugtrace[debugger] == debugtrace['pdb']):
128 ui.warn(_("%s debugger specified "
129 "but its module was not found\n") % debugger)
130
131 debugtrace[debugger]()
107 132 try:
108 133 return _dispatch(req)
109 134 finally:
110 135 ui.flush()
111 136 except: # re-raises
112 137 # enter the debugger when we hit an exception
113 138 if '--debugger' in req.args:
114 139 traceback.print_exc()
115 pdb.post_mortem(sys.exc_info()[2])
140 debugmortem[debugger](sys.exc_info()[2])
116 141 ui.traceback()
117 142 raise
118 143
119 144 # Global exception handling, alphabetically
120 145 # Mercurial-specific first, followed by built-in and library exceptions
121 146 except error.AmbiguousCommand, inst:
122 147 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
123 148 (inst.args[0], " ".join(inst.args[1])))
124 149 except error.ParseError, inst:
125 150 if len(inst.args) > 1:
126 151 ui.warn(_("hg: parse error at %s: %s\n") %
127 152 (inst.args[1], inst.args[0]))
128 153 else:
129 154 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
130 155 return -1
131 156 except error.LockHeld, inst:
132 157 if inst.errno == errno.ETIMEDOUT:
133 158 reason = _('timed out waiting for lock held by %s') % inst.locker
134 159 else:
135 160 reason = _('lock held by %s') % inst.locker
136 161 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
137 162 except error.LockUnavailable, inst:
138 163 ui.warn(_("abort: could not lock %s: %s\n") %
139 164 (inst.desc or inst.filename, inst.strerror))
140 165 except error.CommandError, inst:
141 166 if inst.args[0]:
142 167 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
143 168 commands.help_(ui, inst.args[0], full=False, command=True)
144 169 else:
145 170 ui.warn(_("hg: %s\n") % inst.args[1])
146 171 commands.help_(ui, 'shortlist')
147 172 except error.OutOfBandError, inst:
148 173 ui.warn(_("abort: remote error:\n"))
149 174 ui.warn(''.join(inst.args))
150 175 except error.RepoError, inst:
151 176 ui.warn(_("abort: %s!\n") % inst)
152 177 if inst.hint:
153 178 ui.warn(_("(%s)\n") % inst.hint)
154 179 except error.ResponseError, inst:
155 180 ui.warn(_("abort: %s") % inst.args[0])
156 181 if not isinstance(inst.args[1], basestring):
157 182 ui.warn(" %r\n" % (inst.args[1],))
158 183 elif not inst.args[1]:
159 184 ui.warn(_(" empty string\n"))
160 185 else:
161 186 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
162 187 except error.RevlogError, inst:
163 188 ui.warn(_("abort: %s!\n") % inst)
164 189 except error.SignalInterrupt:
165 190 ui.warn(_("killed!\n"))
166 191 except error.UnknownCommand, inst:
167 192 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
168 193 try:
169 194 # check if the command is in a disabled extension
170 195 # (but don't check for extensions themselves)
171 196 commands.help_(ui, inst.args[0], unknowncmd=True)
172 197 except error.UnknownCommand:
173 198 commands.help_(ui, 'shortlist')
174 199 except error.InterventionRequired, inst:
175 200 ui.warn("%s\n" % inst)
176 201 return 1
177 202 except util.Abort, inst:
178 203 ui.warn(_("abort: %s\n") % inst)
179 204 if inst.hint:
180 205 ui.warn(_("(%s)\n") % inst.hint)
181 206 except ImportError, inst:
182 207 ui.warn(_("abort: %s!\n") % inst)
183 208 m = str(inst).split()[-1]
184 209 if m in "mpatch bdiff".split():
185 210 ui.warn(_("(did you forget to compile extensions?)\n"))
186 211 elif m in "zlib".split():
187 212 ui.warn(_("(is your Python install correct?)\n"))
188 213 except IOError, inst:
189 214 if util.safehasattr(inst, "code"):
190 215 ui.warn(_("abort: %s\n") % inst)
191 216 elif util.safehasattr(inst, "reason"):
192 217 try: # usually it is in the form (errno, strerror)
193 218 reason = inst.reason.args[1]
194 219 except (AttributeError, IndexError):
195 220 # it might be anything, for example a string
196 221 reason = inst.reason
197 222 ui.warn(_("abort: error: %s\n") % reason)
198 223 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
199 224 if ui.debugflag:
200 225 ui.warn(_("broken pipe\n"))
201 226 elif getattr(inst, "strerror", None):
202 227 if getattr(inst, "filename", None):
203 228 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
204 229 else:
205 230 ui.warn(_("abort: %s\n") % inst.strerror)
206 231 else:
207 232 raise
208 233 except OSError, inst:
209 234 if getattr(inst, "filename", None) is not None:
210 235 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
211 236 else:
212 237 ui.warn(_("abort: %s\n") % inst.strerror)
213 238 except KeyboardInterrupt:
214 239 try:
215 240 ui.warn(_("interrupted!\n"))
216 241 except IOError, inst:
217 242 if inst.errno == errno.EPIPE:
218 243 if ui.debugflag:
219 244 ui.warn(_("\nbroken pipe\n"))
220 245 else:
221 246 raise
222 247 except MemoryError:
223 248 ui.warn(_("abort: out of memory\n"))
224 249 except SystemExit, inst:
225 250 # Commands shouldn't sys.exit directly, but give a return code.
226 251 # Just in case catch this and and pass exit code to caller.
227 252 return inst.code
228 253 except socket.error, inst:
229 254 ui.warn(_("abort: %s\n") % inst.args[-1])
230 255 except: # re-raises
231 256 myver = util.version()
232 257 # For compatibility checking, we discard the portion of the hg
233 258 # version after the + on the assumption that if a "normal
234 259 # user" is running a build with a + in it the packager
235 260 # probably built from fairly close to a tag and anyone with a
236 261 # 'make local' copy of hg (where the version number can be out
237 262 # of date) will be clueful enough to notice the implausible
238 263 # version number and try updating.
239 264 compare = myver.split('+')[0]
240 265 ct = tuplever(compare)
241 266 worst = None, ct, ''
242 267 for name, mod in extensions.extensions():
243 268 testedwith = getattr(mod, 'testedwith', '')
244 269 report = getattr(mod, 'buglink', _('the extension author.'))
245 270 if not testedwith.strip():
246 271 # We found an untested extension. It's likely the culprit.
247 272 worst = name, 'unknown', report
248 273 break
249 274 if compare not in testedwith.split() and testedwith != 'internal':
250 275 tested = [tuplever(v) for v in testedwith.split()]
251 276 lower = [t for t in tested if t < ct]
252 277 nearest = max(lower or tested)
253 278 if worst[0] is None or nearest < worst[1]:
254 279 worst = name, nearest, report
255 280 if worst[0] is not None:
256 281 name, testedwith, report = worst
257 282 if not isinstance(testedwith, str):
258 283 testedwith = '.'.join([str(c) for c in testedwith])
259 284 warning = (_('** Unknown exception encountered with '
260 285 'possibly-broken third-party extension %s\n'
261 286 '** which supports versions %s of Mercurial.\n'
262 287 '** Please disable %s and try your action again.\n'
263 288 '** If that fixes the bug please report it to %s\n')
264 289 % (name, testedwith, name, report))
265 290 else:
266 291 warning = (_("** unknown exception encountered, "
267 292 "please report by visiting\n") +
268 293 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
269 294 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
270 295 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
271 296 (_("** Extensions loaded: %s\n") %
272 297 ", ".join([x[0] for x in extensions.extensions()])))
273 298 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
274 299 ui.warn(warning)
275 300 raise
276 301
277 302 return -1
278 303
279 304 def tuplever(v):
280 305 try:
281 306 return tuple([int(i) for i in v.split('.')])
282 307 except ValueError:
283 308 return tuple()
284 309
285 310 def aliasargs(fn, givenargs):
286 311 args = getattr(fn, 'args', [])
287 312 if args:
288 313 cmd = ' '.join(map(util.shellquote, args))
289 314
290 315 nums = []
291 316 def replacer(m):
292 317 num = int(m.group(1)) - 1
293 318 nums.append(num)
294 319 if num < len(givenargs):
295 320 return givenargs[num]
296 321 raise util.Abort(_('too few arguments for command alias'))
297 322 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
298 323 givenargs = [x for i, x in enumerate(givenargs)
299 324 if i not in nums]
300 325 args = shlex.split(cmd)
301 326 return args + givenargs
302 327
303 328 class cmdalias(object):
304 329 def __init__(self, name, definition, cmdtable):
305 330 self.name = self.cmd = name
306 331 self.cmdname = ''
307 332 self.definition = definition
308 333 self.args = []
309 334 self.opts = []
310 335 self.help = ''
311 336 self.norepo = True
312 337 self.optionalrepo = False
313 338 self.badalias = False
314 339
315 340 try:
316 341 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
317 342 for alias, e in cmdtable.iteritems():
318 343 if e is entry:
319 344 self.cmd = alias
320 345 break
321 346 self.shadows = True
322 347 except error.UnknownCommand:
323 348 self.shadows = False
324 349
325 350 if not self.definition:
326 351 def fn(ui, *args):
327 352 ui.warn(_("no definition for alias '%s'\n") % self.name)
328 353 return 1
329 354 self.fn = fn
330 355 self.badalias = True
331 356 return
332 357
333 358 if self.definition.startswith('!'):
334 359 self.shell = True
335 360 def fn(ui, *args):
336 361 env = {'HG_ARGS': ' '.join((self.name,) + args)}
337 362 def _checkvar(m):
338 363 if m.groups()[0] == '$':
339 364 return m.group()
340 365 elif int(m.groups()[0]) <= len(args):
341 366 return m.group()
342 367 else:
343 368 ui.debug("No argument found for substitution "
344 369 "of %i variable in alias '%s' definition."
345 370 % (int(m.groups()[0]), self.name))
346 371 return ''
347 372 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
348 373 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
349 374 replace['0'] = self.name
350 375 replace['@'] = ' '.join(args)
351 376 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
352 377 return util.system(cmd, environ=env, out=ui.fout)
353 378 self.fn = fn
354 379 return
355 380
356 381 args = shlex.split(self.definition)
357 382 self.cmdname = cmd = args.pop(0)
358 383 args = map(util.expandpath, args)
359 384
360 385 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
361 386 if _earlygetopt([invalidarg], args):
362 387 def fn(ui, *args):
363 388 ui.warn(_("error in definition for alias '%s': %s may only "
364 389 "be given on the command line\n")
365 390 % (self.name, invalidarg))
366 391 return 1
367 392
368 393 self.fn = fn
369 394 self.badalias = True
370 395 return
371 396
372 397 try:
373 398 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
374 399 if len(tableentry) > 2:
375 400 self.fn, self.opts, self.help = tableentry
376 401 else:
377 402 self.fn, self.opts = tableentry
378 403
379 404 self.args = aliasargs(self.fn, args)
380 405 if cmd not in commands.norepo.split(' '):
381 406 self.norepo = False
382 407 if cmd in commands.optionalrepo.split(' '):
383 408 self.optionalrepo = True
384 409 if self.help.startswith("hg " + cmd):
385 410 # drop prefix in old-style help lines so hg shows the alias
386 411 self.help = self.help[4 + len(cmd):]
387 412 self.__doc__ = self.fn.__doc__
388 413
389 414 except error.UnknownCommand:
390 415 def fn(ui, *args):
391 416 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
392 417 % (self.name, cmd))
393 418 try:
394 419 # check if the command is in a disabled extension
395 420 commands.help_(ui, cmd, unknowncmd=True)
396 421 except error.UnknownCommand:
397 422 pass
398 423 return 1
399 424 self.fn = fn
400 425 self.badalias = True
401 426 except error.AmbiguousCommand:
402 427 def fn(ui, *args):
403 428 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
404 429 % (self.name, cmd))
405 430 return 1
406 431 self.fn = fn
407 432 self.badalias = True
408 433
409 434 def __call__(self, ui, *args, **opts):
410 435 if self.shadows:
411 436 ui.debug("alias '%s' shadows command '%s'\n" %
412 437 (self.name, self.cmdname))
413 438
414 439 if util.safehasattr(self, 'shell'):
415 440 return self.fn(ui, *args, **opts)
416 441 else:
417 442 try:
418 443 util.checksignature(self.fn)(ui, *args, **opts)
419 444 except error.SignatureError:
420 445 args = ' '.join([self.cmdname] + self.args)
421 446 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
422 447 raise
423 448
424 449 def addaliases(ui, cmdtable):
425 450 # aliases are processed after extensions have been loaded, so they
426 451 # may use extension commands. Aliases can also use other alias definitions,
427 452 # but only if they have been defined prior to the current definition.
428 453 for alias, definition in ui.configitems('alias'):
429 454 aliasdef = cmdalias(alias, definition, cmdtable)
430 455
431 456 try:
432 457 olddef = cmdtable[aliasdef.cmd][0]
433 458 if olddef.definition == aliasdef.definition:
434 459 continue
435 460 except (KeyError, AttributeError):
436 461 # definition might not exist or it might not be a cmdalias
437 462 pass
438 463
439 464 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
440 465 if aliasdef.norepo:
441 466 commands.norepo += ' %s' % alias
442 467 if aliasdef.optionalrepo:
443 468 commands.optionalrepo += ' %s' % alias
444 469
445 470 def _parse(ui, args):
446 471 options = {}
447 472 cmdoptions = {}
448 473
449 474 try:
450 475 args = fancyopts.fancyopts(args, commands.globalopts, options)
451 476 except fancyopts.getopt.GetoptError, inst:
452 477 raise error.CommandError(None, inst)
453 478
454 479 if args:
455 480 cmd, args = args[0], args[1:]
456 481 aliases, entry = cmdutil.findcmd(cmd, commands.table,
457 482 ui.configbool("ui", "strict"))
458 483 cmd = aliases[0]
459 484 args = aliasargs(entry[0], args)
460 485 defaults = ui.config("defaults", cmd)
461 486 if defaults:
462 487 args = map(util.expandpath, shlex.split(defaults)) + args
463 488 c = list(entry[1])
464 489 else:
465 490 cmd = None
466 491 c = []
467 492
468 493 # combine global options into local
469 494 for o in commands.globalopts:
470 495 c.append((o[0], o[1], options[o[1]], o[3]))
471 496
472 497 try:
473 498 args = fancyopts.fancyopts(args, c, cmdoptions, True)
474 499 except fancyopts.getopt.GetoptError, inst:
475 500 raise error.CommandError(cmd, inst)
476 501
477 502 # separate global options back out
478 503 for o in commands.globalopts:
479 504 n = o[1]
480 505 options[n] = cmdoptions[n]
481 506 del cmdoptions[n]
482 507
483 508 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
484 509
485 510 def _parseconfig(ui, config):
486 511 """parse the --config options from the command line"""
487 512 configs = []
488 513
489 514 for cfg in config:
490 515 try:
491 516 name, value = cfg.split('=', 1)
492 517 section, name = name.split('.', 1)
493 518 if not section or not name:
494 519 raise IndexError
495 520 ui.setconfig(section, name, value)
496 521 configs.append((section, name, value))
497 522 except (IndexError, ValueError):
498 523 raise util.Abort(_('malformed --config option: %r '
499 524 '(use --config section.name=value)') % cfg)
500 525
501 526 return configs
502 527
503 528 def _earlygetopt(aliases, args):
504 529 """Return list of values for an option (or aliases).
505 530
506 531 The values are listed in the order they appear in args.
507 532 The options and values are removed from args.
508 533
509 534 >>> args = ['x', '--cwd', 'foo', 'y']
510 535 >>> _earlygetopt(['--cwd'], args), args
511 536 (['foo'], ['x', 'y'])
512 537
513 538 >>> args = ['x', '--cwd=bar', 'y']
514 539 >>> _earlygetopt(['--cwd'], args), args
515 540 (['bar'], ['x', 'y'])
516 541
517 542 >>> args = ['x', '-R', 'foo', 'y']
518 543 >>> _earlygetopt(['-R'], args), args
519 544 (['foo'], ['x', 'y'])
520 545
521 546 >>> args = ['x', '-Rbar', 'y']
522 547 >>> _earlygetopt(['-R'], args), args
523 548 (['bar'], ['x', 'y'])
524 549 """
525 550 try:
526 551 argcount = args.index("--")
527 552 except ValueError:
528 553 argcount = len(args)
529 554 shortopts = [opt for opt in aliases if len(opt) == 2]
530 555 values = []
531 556 pos = 0
532 557 while pos < argcount:
533 558 fullarg = arg = args[pos]
534 559 equals = arg.find('=')
535 560 if equals > -1:
536 561 arg = arg[:equals]
537 562 if arg in aliases:
538 563 del args[pos]
539 564 if equals > -1:
540 565 values.append(fullarg[equals + 1:])
541 566 argcount -= 1
542 567 else:
543 568 if pos + 1 >= argcount:
544 569 # ignore and let getopt report an error if there is no value
545 570 break
546 571 values.append(args.pop(pos))
547 572 argcount -= 2
548 573 elif arg[:2] in shortopts:
549 574 # short option can have no following space, e.g. hg log -Rfoo
550 575 values.append(args.pop(pos)[2:])
551 576 argcount -= 1
552 577 else:
553 578 pos += 1
554 579 return values
555 580
556 581 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
557 582 # run pre-hook, and abort if it fails
558 583 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
559 584 pats=cmdpats, opts=cmdoptions)
560 585 ret = _runcommand(ui, options, cmd, d)
561 586 # run post-hook, passing command result
562 587 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
563 588 result=ret, pats=cmdpats, opts=cmdoptions)
564 589 return ret
565 590
566 591 def _getlocal(ui, rpath):
567 592 """Return (path, local ui object) for the given target path.
568 593
569 594 Takes paths in [cwd]/.hg/hgrc into account."
570 595 """
571 596 try:
572 597 wd = os.getcwd()
573 598 except OSError, e:
574 599 raise util.Abort(_("error getting current working directory: %s") %
575 600 e.strerror)
576 601 path = cmdutil.findrepo(wd) or ""
577 602 if not path:
578 603 lui = ui
579 604 else:
580 605 lui = ui.copy()
581 606 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
582 607
583 608 if rpath and rpath[-1]:
584 609 path = lui.expandpath(rpath[-1])
585 610 lui = ui.copy()
586 611 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
587 612
588 613 return path, lui
589 614
590 615 def _checkshellalias(lui, ui, args):
591 616 options = {}
592 617
593 618 try:
594 619 args = fancyopts.fancyopts(args, commands.globalopts, options)
595 620 except fancyopts.getopt.GetoptError:
596 621 return
597 622
598 623 if not args:
599 624 return
600 625
601 626 norepo = commands.norepo
602 627 optionalrepo = commands.optionalrepo
603 628 def restorecommands():
604 629 commands.norepo = norepo
605 630 commands.optionalrepo = optionalrepo
606 631
607 632 cmdtable = commands.table.copy()
608 633 addaliases(lui, cmdtable)
609 634
610 635 cmd = args[0]
611 636 try:
612 637 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
613 638 lui.configbool("ui", "strict"))
614 639 except (error.AmbiguousCommand, error.UnknownCommand):
615 640 restorecommands()
616 641 return
617 642
618 643 cmd = aliases[0]
619 644 fn = entry[0]
620 645
621 646 if cmd and util.safehasattr(fn, 'shell'):
622 647 d = lambda: fn(ui, *args[1:])
623 648 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
624 649 [], {})
625 650
626 651 restorecommands()
627 652
628 653 _loaded = set()
629 654 def _dispatch(req):
630 655 args = req.args
631 656 ui = req.ui
632 657
633 658 # check for cwd
634 659 cwd = _earlygetopt(['--cwd'], args)
635 660 if cwd:
636 661 os.chdir(cwd[-1])
637 662
638 663 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
639 664 path, lui = _getlocal(ui, rpath)
640 665
641 666 # Now that we're operating in the right directory/repository with
642 667 # the right config settings, check for shell aliases
643 668 shellaliasfn = _checkshellalias(lui, ui, args)
644 669 if shellaliasfn:
645 670 return shellaliasfn()
646 671
647 672 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
648 673 # reposetup. Programs like TortoiseHg will call _dispatch several
649 674 # times so we keep track of configured extensions in _loaded.
650 675 extensions.loadall(lui)
651 676 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
652 677 # Propagate any changes to lui.__class__ by extensions
653 678 ui.__class__ = lui.__class__
654 679
655 680 # (uisetup and extsetup are handled in extensions.loadall)
656 681
657 682 for name, module in exts:
658 683 cmdtable = getattr(module, 'cmdtable', {})
659 684 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
660 685 if overrides:
661 686 ui.warn(_("extension '%s' overrides commands: %s\n")
662 687 % (name, " ".join(overrides)))
663 688 commands.table.update(cmdtable)
664 689 _loaded.add(name)
665 690
666 691 # (reposetup is handled in hg.repository)
667 692
668 693 addaliases(lui, commands.table)
669 694
670 695 # check for fallback encoding
671 696 fallback = lui.config('ui', 'fallbackencoding')
672 697 if fallback:
673 698 encoding.fallbackencoding = fallback
674 699
675 700 fullargs = args
676 701 cmd, func, args, options, cmdoptions = _parse(lui, args)
677 702
678 703 if options["config"]:
679 704 raise util.Abort(_("option --config may not be abbreviated!"))
680 705 if options["cwd"]:
681 706 raise util.Abort(_("option --cwd may not be abbreviated!"))
682 707 if options["repository"]:
683 708 raise util.Abort(_(
684 709 "option -R has to be separated from other options (e.g. not -qR) "
685 710 "and --repository may only be abbreviated as --repo!"))
686 711
687 712 if options["encoding"]:
688 713 encoding.encoding = options["encoding"]
689 714 if options["encodingmode"]:
690 715 encoding.encodingmode = options["encodingmode"]
691 716 if options["time"]:
692 717 def get_times():
693 718 t = os.times()
694 719 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
695 720 t = (t[0], t[1], t[2], t[3], time.clock())
696 721 return t
697 722 s = get_times()
698 723 def print_time():
699 724 t = get_times()
700 725 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
701 726 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
702 727 atexit.register(print_time)
703 728
704 729 uis = set([ui, lui])
705 730
706 731 if req.repo:
707 732 uis.add(req.repo.ui)
708 733
709 734 if options['verbose'] or options['debug'] or options['quiet']:
710 735 for opt in ('verbose', 'debug', 'quiet'):
711 736 val = str(bool(options[opt]))
712 737 for ui_ in uis:
713 738 ui_.setconfig('ui', opt, val)
714 739
715 740 if options['traceback']:
716 741 for ui_ in uis:
717 742 ui_.setconfig('ui', 'traceback', 'on')
718 743
719 744 if options['noninteractive']:
720 745 for ui_ in uis:
721 746 ui_.setconfig('ui', 'interactive', 'off')
722 747
723 748 if cmdoptions.get('insecure', False):
724 749 for ui_ in uis:
725 750 ui_.setconfig('web', 'cacerts', '')
726 751
727 752 if options['version']:
728 753 return commands.version_(ui)
729 754 if options['help']:
730 755 return commands.help_(ui, cmd)
731 756 elif not cmd:
732 757 return commands.help_(ui, 'shortlist')
733 758
734 759 repo = None
735 760 cmdpats = args[:]
736 761 if cmd not in commands.norepo.split():
737 762 # use the repo from the request only if we don't have -R
738 763 if not rpath and not cwd:
739 764 repo = req.repo
740 765
741 766 if repo:
742 767 # set the descriptors of the repo ui to those of ui
743 768 repo.ui.fin = ui.fin
744 769 repo.ui.fout = ui.fout
745 770 repo.ui.ferr = ui.ferr
746 771 else:
747 772 try:
748 773 repo = hg.repository(ui, path=path)
749 774 if not repo.local():
750 775 raise util.Abort(_("repository '%s' is not local") % path)
751 776 if options['hidden']:
752 777 repo = repo.unfiltered()
753 778 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
754 779 except error.RequirementError:
755 780 raise
756 781 except error.RepoError:
757 782 if cmd not in commands.optionalrepo.split():
758 783 if (cmd in commands.inferrepo.split() and
759 784 args and not path): # try to infer -R from command args
760 785 repos = map(cmdutil.findrepo, args)
761 786 guess = repos[0]
762 787 if guess and repos.count(guess) == len(repos):
763 788 req.args = ['--repository', guess] + fullargs
764 789 return _dispatch(req)
765 790 if not path:
766 791 raise error.RepoError(_("no repository found in '%s'"
767 792 " (.hg not found)")
768 793 % os.getcwd())
769 794 raise
770 795 if repo:
771 796 ui = repo.ui
772 797 args.insert(0, repo)
773 798 elif rpath:
774 799 ui.warn(_("warning: --repository ignored\n"))
775 800
776 801 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
777 802 ui.log("command", '%s\n', msg)
778 803 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
779 804 try:
780 805 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
781 806 cmdpats, cmdoptions)
782 807 finally:
783 808 if repo and repo != req.repo:
784 809 repo.close()
785 810
786 811 def lsprofile(ui, func, fp):
787 812 format = ui.config('profiling', 'format', default='text')
788 813 field = ui.config('profiling', 'sort', default='inlinetime')
789 814 limit = ui.configint('profiling', 'limit', default=30)
790 815 climit = ui.configint('profiling', 'nested', default=5)
791 816
792 817 if format not in ['text', 'kcachegrind']:
793 818 ui.warn(_("unrecognized profiling format '%s'"
794 819 " - Ignored\n") % format)
795 820 format = 'text'
796 821
797 822 try:
798 823 from mercurial import lsprof
799 824 except ImportError:
800 825 raise util.Abort(_(
801 826 'lsprof not available - install from '
802 827 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
803 828 p = lsprof.Profiler()
804 829 p.enable(subcalls=True)
805 830 try:
806 831 return func()
807 832 finally:
808 833 p.disable()
809 834
810 835 if format == 'kcachegrind':
811 836 import lsprofcalltree
812 837 calltree = lsprofcalltree.KCacheGrind(p)
813 838 calltree.output(fp)
814 839 else:
815 840 # format == 'text'
816 841 stats = lsprof.Stats(p.getstats())
817 842 stats.sort(field)
818 843 stats.pprint(limit=limit, file=fp, climit=climit)
819 844
820 845 def statprofile(ui, func, fp):
821 846 try:
822 847 import statprof
823 848 except ImportError:
824 849 raise util.Abort(_(
825 850 'statprof not available - install using "easy_install statprof"'))
826 851
827 852 freq = ui.configint('profiling', 'freq', default=1000)
828 853 if freq > 0:
829 854 statprof.reset(freq)
830 855 else:
831 856 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
832 857
833 858 statprof.start()
834 859 try:
835 860 return func()
836 861 finally:
837 862 statprof.stop()
838 863 statprof.display(fp)
839 864
840 865 def _runcommand(ui, options, cmd, cmdfunc):
841 866 def checkargs():
842 867 try:
843 868 return cmdfunc()
844 869 except error.SignatureError:
845 870 raise error.CommandError(cmd, _("invalid arguments"))
846 871
847 872 if options['profile']:
848 873 profiler = os.getenv('HGPROF')
849 874 if profiler is None:
850 875 profiler = ui.config('profiling', 'type', default='ls')
851 876 if profiler not in ('ls', 'stat'):
852 877 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
853 878 profiler = 'ls'
854 879
855 880 output = ui.config('profiling', 'output')
856 881
857 882 if output:
858 883 path = ui.expandpath(output)
859 884 fp = open(path, 'wb')
860 885 else:
861 886 fp = sys.stderr
862 887
863 888 try:
864 889 if profiler == 'ls':
865 890 return lsprofile(ui, checkargs, fp)
866 891 else:
867 892 return statprofile(ui, checkargs, fp)
868 893 finally:
869 894 if output:
870 895 fp.close()
871 896 else:
872 897 return checkargs()
General Comments 0
You need to be logged in to leave comments. Login now