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