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