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