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