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