comparison mercurial/dispatch.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents dd97354b8891
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
46 from .utils import ( 46 from .utils import (
47 procutil, 47 procutil,
48 stringutil, 48 stringutil,
49 ) 49 )
50 50
51
51 class request(object): 52 class request(object):
52 def __init__(self, args, ui=None, repo=None, fin=None, fout=None, 53 def __init__(
53 ferr=None, fmsg=None, prereposetups=None): 54 self,
55 args,
56 ui=None,
57 repo=None,
58 fin=None,
59 fout=None,
60 ferr=None,
61 fmsg=None,
62 prereposetups=None,
63 ):
54 self.args = args 64 self.args = args
55 self.ui = ui 65 self.ui = ui
56 self.repo = repo 66 self.repo = repo
57 67
58 # input/output/error streams 68 # input/output/error streams
78 try: 88 try:
79 while handlers: 89 while handlers:
80 func, args, kwargs = handlers.pop() 90 func, args, kwargs = handlers.pop()
81 try: 91 try:
82 func(*args, **kwargs) 92 func(*args, **kwargs)
83 except: # re-raises below 93 except: # re-raises below
84 if exc is None: 94 if exc is None:
85 exc = sys.exc_info()[1] 95 exc = sys.exc_info()[1]
86 self.ui.warn(('error in exit handlers:\n')) 96 self.ui.warn('error in exit handlers:\n')
87 self.ui.traceback(force=True) 97 self.ui.traceback(force=True)
88 finally: 98 finally:
89 if exc is not None: 99 if exc is not None:
90 raise exc 100 raise exc
101
91 102
92 def run(): 103 def run():
93 "run the command in sys.argv" 104 "run the command in sys.argv"
94 initstdio() 105 initstdio()
95 with tracing.log('parse args into request'): 106 with tracing.log('parse args into request'):
110 status = -1 121 status = -1
111 122
112 if util.safehasattr(req.ui, 'ferr'): 123 if util.safehasattr(req.ui, 'ferr'):
113 try: 124 try:
114 if err is not None and err.errno != errno.EPIPE: 125 if err is not None and err.errno != errno.EPIPE:
115 req.ui.ferr.write('abort: %s\n' % 126 req.ui.ferr.write(
116 encoding.strtolocal(err.strerror)) 127 'abort: %s\n' % encoding.strtolocal(err.strerror)
128 )
117 req.ui.ferr.flush() 129 req.ui.ferr.flush()
118 # There's not much we can do about an I/O error here. So (possibly) 130 # There's not much we can do about an I/O error here. So (possibly)
119 # change the status code and move on. 131 # change the status code and move on.
120 except IOError: 132 except IOError:
121 status = -1 133 status = -1
122 134
123 _silencestdio() 135 _silencestdio()
124 sys.exit(status & 255) 136 sys.exit(status & 255)
125 137
138
126 if pycompat.ispy3: 139 if pycompat.ispy3:
140
127 def initstdio(): 141 def initstdio():
128 pass 142 pass
129 143
130 def _silencestdio(): 144 def _silencestdio():
131 for fp in (sys.stdout, sys.stderr): 145 for fp in (sys.stdout, sys.stderr):
141 # and its close() actually closes the underlying file descriptor. 155 # and its close() actually closes the underlying file descriptor.
142 try: 156 try:
143 fp.close() 157 fp.close()
144 except IOError: 158 except IOError:
145 pass 159 pass
160
161
146 else: 162 else:
163
147 def initstdio(): 164 def initstdio():
148 for fp in (sys.stdin, sys.stdout, sys.stderr): 165 for fp in (sys.stdin, sys.stdout, sys.stderr):
149 procutil.setbinary(fp) 166 procutil.setbinary(fp)
150 167
151 def _silencestdio(): 168 def _silencestdio():
152 pass 169 pass
170
153 171
154 def _getsimilar(symbols, value): 172 def _getsimilar(symbols, value):
155 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio() 173 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
156 # The cutoff for similarity here is pretty arbitrary. It should 174 # The cutoff for similarity here is pretty arbitrary. It should
157 # probably be investigated and tweaked. 175 # probably be investigated and tweaked.
158 return [s for s in symbols if sim(s) > 0.6] 176 return [s for s in symbols if sim(s) > 0.6]
177
159 178
160 def _reportsimilar(write, similar): 179 def _reportsimilar(write, similar):
161 if len(similar) == 1: 180 if len(similar) == 1:
162 write(_("(did you mean %s?)\n") % similar[0]) 181 write(_("(did you mean %s?)\n") % similar[0])
163 elif similar: 182 elif similar:
164 ss = ", ".join(sorted(similar)) 183 ss = ", ".join(sorted(similar))
165 write(_("(did you mean one of %s?)\n") % ss) 184 write(_("(did you mean one of %s?)\n") % ss)
166 185
186
167 def _formatparse(write, inst): 187 def _formatparse(write, inst):
168 similar = [] 188 similar = []
169 if isinstance(inst, error.UnknownIdentifier): 189 if isinstance(inst, error.UnknownIdentifier):
170 # make sure to check fileset first, as revset can invoke fileset 190 # make sure to check fileset first, as revset can invoke fileset
171 similar = _getsimilar(inst.symbols, inst.function) 191 similar = _getsimilar(inst.symbols, inst.function)
172 if len(inst.args) > 1: 192 if len(inst.args) > 1:
173 write(_("hg: parse error at %s: %s\n") % 193 write(
174 (pycompat.bytestr(inst.args[1]), inst.args[0])) 194 _("hg: parse error at %s: %s\n")
195 % (pycompat.bytestr(inst.args[1]), inst.args[0])
196 )
175 if inst.args[0].startswith(' '): 197 if inst.args[0].startswith(' '):
176 write(_("unexpected leading whitespace\n")) 198 write(_("unexpected leading whitespace\n"))
177 else: 199 else:
178 write(_("hg: parse error: %s\n") % inst.args[0]) 200 write(_("hg: parse error: %s\n") % inst.args[0])
179 _reportsimilar(write, similar) 201 _reportsimilar(write, similar)
180 if inst.hint: 202 if inst.hint:
181 write(_("(%s)\n") % inst.hint) 203 write(_("(%s)\n") % inst.hint)
182 204
205
183 def _formatargs(args): 206 def _formatargs(args):
184 return ' '.join(procutil.shellquote(a) for a in args) 207 return ' '.join(procutil.shellquote(a) for a in args)
208
185 209
186 def dispatch(req): 210 def dispatch(req):
187 """run the command specified in req.args; returns an integer status code""" 211 """run the command specified in req.args; returns an integer status code"""
188 with tracing.log('dispatch.dispatch'): 212 with tracing.log('dispatch.dispatch'):
189 if req.ferr: 213 if req.ferr:
246 finally: 270 finally:
247 duration = util.timer() - starttime 271 duration = util.timer() - starttime
248 req.ui.flush() 272 req.ui.flush()
249 if req.ui.logblockedtimes: 273 if req.ui.logblockedtimes:
250 req.ui._blockedtimes['command_duration'] = duration * 1000 274 req.ui._blockedtimes['command_duration'] = duration * 1000
251 req.ui.log('uiblocked', 'ui blocked ms\n', 275 req.ui.log(
252 **pycompat.strkwargs(req.ui._blockedtimes)) 276 'uiblocked',
277 'ui blocked ms\n',
278 **pycompat.strkwargs(req.ui._blockedtimes)
279 )
253 return_code = ret & 255 280 return_code = ret & 255
254 req.ui.log( 281 req.ui.log(
255 "commandfinish", 282 "commandfinish",
256 "%s exited %d after %0.2f seconds\n", 283 "%s exited %d after %0.2f seconds\n",
257 msg, 284 msg,
261 duration=duration, 288 duration=duration,
262 canonical_command=req.canonical_command, 289 canonical_command=req.canonical_command,
263 ) 290 )
264 try: 291 try:
265 req._runexithandlers() 292 req._runexithandlers()
266 except: # exiting, so no re-raises 293 except: # exiting, so no re-raises
267 ret = ret or -1 294 ret = ret or -1
268 return ret 295 return ret
269 296
297
270 def _runcatch(req): 298 def _runcatch(req):
271 with tracing.log('dispatch._runcatch'): 299 with tracing.log('dispatch._runcatch'):
300
272 def catchterm(*args): 301 def catchterm(*args):
273 raise error.SignalInterrupt 302 raise error.SignalInterrupt
274 303
275 ui = req.ui 304 ui = req.ui
276 try: 305 try:
277 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': 306 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
278 num = getattr(signal, name, None) 307 num = getattr(signal, name, None)
279 if num: 308 if num:
280 signal.signal(num, catchterm) 309 signal.signal(num, catchterm)
281 except ValueError: 310 except ValueError:
282 pass # happens if called in a thread 311 pass # happens if called in a thread
283 312
284 def _runcatchfunc(): 313 def _runcatchfunc():
285 realcmd = None 314 realcmd = None
286 try: 315 try:
287 cmdargs = fancyopts.fancyopts( 316 cmdargs = fancyopts.fancyopts(
288 req.args[:], commands.globalopts, {}) 317 req.args[:], commands.globalopts, {}
318 )
289 cmd = cmdargs[0] 319 cmd = cmdargs[0]
290 aliases, entry = cmdutil.findcmd(cmd, commands.table, False) 320 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
291 realcmd = aliases[0] 321 realcmd = aliases[0]
292 except (error.UnknownCommand, error.AmbiguousCommand, 322 except (
293 IndexError, getopt.GetoptError): 323 error.UnknownCommand,
324 error.AmbiguousCommand,
325 IndexError,
326 getopt.GetoptError,
327 ):
294 # Don't handle this here. We know the command is 328 # Don't handle this here. We know the command is
295 # invalid, but all we're worried about for now is that 329 # invalid, but all we're worried about for now is that
296 # it's not a command that server operators expect to 330 # it's not a command that server operators expect to
297 # be safe to offer to users in a sandbox. 331 # be safe to offer to users in a sandbox.
298 pass 332 pass
303 # restrict to exactly that set of arguments, and prohibit 337 # restrict to exactly that set of arguments, and prohibit
304 # any repo name that starts with '--' to prevent 338 # any repo name that starts with '--' to prevent
305 # shenanigans wherein a user does something like pass 339 # shenanigans wherein a user does something like pass
306 # --debugger or --config=ui.debugger=1 as a repo 340 # --debugger or --config=ui.debugger=1 as a repo
307 # name. This used to actually run the debugger. 341 # name. This used to actually run the debugger.
308 if (len(req.args) != 4 or 342 if (
309 req.args[0] != '-R' or 343 len(req.args) != 4
310 req.args[1].startswith('--') or 344 or req.args[0] != '-R'
311 req.args[2] != 'serve' or 345 or req.args[1].startswith('--')
312 req.args[3] != '--stdio'): 346 or req.args[2] != 'serve'
347 or req.args[3] != '--stdio'
348 ):
313 raise error.Abort( 349 raise error.Abort(
314 _('potentially unsafe serve --stdio invocation: %s') % 350 _('potentially unsafe serve --stdio invocation: %s')
315 (stringutil.pprint(req.args),)) 351 % (stringutil.pprint(req.args),)
352 )
316 353
317 try: 354 try:
318 debugger = 'pdb' 355 debugger = 'pdb'
319 debugtrace = { 356 debugtrace = {'pdb': pdb.set_trace}
320 'pdb': pdb.set_trace 357 debugmortem = {'pdb': pdb.post_mortem}
321 }
322 debugmortem = {
323 'pdb': pdb.post_mortem
324 }
325 358
326 # read --config before doing anything else 359 # read --config before doing anything else
327 # (e.g. to change trust settings for reading .hg/hgrc) 360 # (e.g. to change trust settings for reading .hg/hgrc)
328 cfgs = _parseconfig(req.ui, req.earlyoptions['config']) 361 cfgs = _parseconfig(req.ui, req.earlyoptions['config'])
329 362
345 # debugging has been requested 378 # debugging has been requested
346 with demandimport.deactivated(): 379 with demandimport.deactivated():
347 try: 380 try:
348 debugmod = __import__(debugger) 381 debugmod = __import__(debugger)
349 except ImportError: 382 except ImportError:
350 pass # Leave debugmod = pdb 383 pass # Leave debugmod = pdb
351 384
352 debugtrace[debugger] = debugmod.set_trace 385 debugtrace[debugger] = debugmod.set_trace
353 debugmortem[debugger] = debugmod.post_mortem 386 debugmortem[debugger] = debugmod.post_mortem
354 387
355 # enter the debugger before command execution 388 # enter the debugger before command execution
356 if req.earlyoptions['debugger']: 389 if req.earlyoptions['debugger']:
357 ui.warn(_("entering debugger - " 390 ui.warn(
358 "type c to continue starting hg or h for help\n")) 391 _(
359 392 "entering debugger - "
360 if (debugger != 'pdb' and 393 "type c to continue starting hg or h for help\n"
361 debugtrace[debugger] == debugtrace['pdb']): 394 )
362 ui.warn(_("%s debugger specified " 395 )
363 "but its module was not found\n") % debugger) 396
397 if (
398 debugger != 'pdb'
399 and debugtrace[debugger] == debugtrace['pdb']
400 ):
401 ui.warn(
402 _(
403 "%s debugger specified "
404 "but its module was not found\n"
405 )
406 % debugger
407 )
364 with demandimport.deactivated(): 408 with demandimport.deactivated():
365 debugtrace[debugger]() 409 debugtrace[debugger]()
366 try: 410 try:
367 return _dispatch(req) 411 return _dispatch(req)
368 finally: 412 finally:
369 ui.flush() 413 ui.flush()
370 except: # re-raises 414 except: # re-raises
371 # enter the debugger when we hit an exception 415 # enter the debugger when we hit an exception
372 if req.earlyoptions['debugger']: 416 if req.earlyoptions['debugger']:
373 traceback.print_exc() 417 traceback.print_exc()
374 debugmortem[debugger](sys.exc_info()[2]) 418 debugmortem[debugger](sys.exc_info()[2])
375 raise 419 raise
420
376 return _callcatch(ui, _runcatchfunc) 421 return _callcatch(ui, _runcatchfunc)
422
377 423
378 def _callcatch(ui, func): 424 def _callcatch(ui, func):
379 """like scmutil.callcatch but handles more high-level exceptions about 425 """like scmutil.callcatch but handles more high-level exceptions about
380 config parsing and commands. besides, use handlecommandexception to handle 426 config parsing and commands. besides, use handlecommandexception to handle
381 uncaught exceptions. 427 uncaught exceptions.
382 """ 428 """
383 try: 429 try:
384 return scmutil.callcatch(ui, func) 430 return scmutil.callcatch(ui, func)
385 except error.AmbiguousCommand as inst: 431 except error.AmbiguousCommand as inst:
386 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") % 432 ui.warn(
387 (inst.args[0], " ".join(inst.args[1]))) 433 _("hg: command '%s' is ambiguous:\n %s\n")
434 % (inst.args[0], " ".join(inst.args[1]))
435 )
388 except error.CommandError as inst: 436 except error.CommandError as inst:
389 if inst.args[0]: 437 if inst.args[0]:
390 ui.pager('help') 438 ui.pager('help')
391 msgbytes = pycompat.bytestr(inst.args[1]) 439 msgbytes = pycompat.bytestr(inst.args[1])
392 ui.warn(_("hg %s: %s\n") % (inst.args[0], msgbytes)) 440 ui.warn(_("hg %s: %s\n") % (inst.args[0], msgbytes))
400 except error.UnknownCommand as inst: 448 except error.UnknownCommand as inst:
401 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0] 449 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
402 try: 450 try:
403 # check if the command is in a disabled extension 451 # check if the command is in a disabled extension
404 # (but don't check for extensions themselves) 452 # (but don't check for extensions themselves)
405 formatted = help.formattedhelp(ui, commands, inst.args[0], 453 formatted = help.formattedhelp(
406 unknowncmd=True) 454 ui, commands, inst.args[0], unknowncmd=True
455 )
407 ui.warn(nocmdmsg) 456 ui.warn(nocmdmsg)
408 ui.write(formatted) 457 ui.write(formatted)
409 except (error.UnknownCommand, error.Abort): 458 except (error.UnknownCommand, error.Abort):
410 suggested = False 459 suggested = False
411 if len(inst.args) == 2: 460 if len(inst.args) == 2:
425 if not handlecommandexception(ui): 474 if not handlecommandexception(ui):
426 raise 475 raise
427 476
428 return -1 477 return -1
429 478
479
430 def aliasargs(fn, givenargs): 480 def aliasargs(fn, givenargs):
431 args = [] 481 args = []
432 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction 482 # only care about alias 'args', ignore 'args' set by extensions.wrapfunction
433 if not util.safehasattr(fn, '_origfunc'): 483 if not util.safehasattr(fn, '_origfunc'):
434 args = getattr(fn, 'args', args) 484 args = getattr(fn, 'args', args)
435 if args: 485 if args:
436 cmd = ' '.join(map(procutil.shellquote, args)) 486 cmd = ' '.join(map(procutil.shellquote, args))
437 487
438 nums = [] 488 nums = []
489
439 def replacer(m): 490 def replacer(m):
440 num = int(m.group(1)) - 1 491 num = int(m.group(1)) - 1
441 nums.append(num) 492 nums.append(num)
442 if num < len(givenargs): 493 if num < len(givenargs):
443 return givenargs[num] 494 return givenargs[num]
444 raise error.Abort(_('too few arguments for command alias')) 495 raise error.Abort(_('too few arguments for command alias'))
496
445 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd) 497 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
446 givenargs = [x for i, x in enumerate(givenargs) 498 givenargs = [x for i, x in enumerate(givenargs) if i not in nums]
447 if i not in nums]
448 args = pycompat.shlexsplit(cmd) 499 args = pycompat.shlexsplit(cmd)
449 return args + givenargs 500 return args + givenargs
501
450 502
451 def aliasinterpolate(name, args, cmd): 503 def aliasinterpolate(name, args, cmd):
452 '''interpolate args into cmd for shell aliases 504 '''interpolate args into cmd for shell aliases
453 505
454 This also handles $0, $@ and "$@". 506 This also handles $0, $@ and "$@".
467 # escape '\$' for regex 519 # escape '\$' for regex
468 regex = '|'.join(replacemap.keys()).replace('$', br'\$') 520 regex = '|'.join(replacemap.keys()).replace('$', br'\$')
469 r = re.compile(regex) 521 r = re.compile(regex)
470 return r.sub(lambda x: replacemap[x.group()], cmd) 522 return r.sub(lambda x: replacemap[x.group()], cmd)
471 523
524
472 class cmdalias(object): 525 class cmdalias(object):
473 def __init__(self, ui, name, definition, cmdtable, source): 526 def __init__(self, ui, name, definition, cmdtable, source):
474 self.name = self.cmd = name 527 self.name = self.cmd = name
475 self.cmdname = '' 528 self.cmdname = ''
476 self.definition = definition 529 self.definition = definition
497 return 550 return
498 551
499 if self.definition.startswith('!'): 552 if self.definition.startswith('!'):
500 shdef = self.definition[1:] 553 shdef = self.definition[1:]
501 self.shell = True 554 self.shell = True
555
502 def fn(ui, *args): 556 def fn(ui, *args):
503 env = {'HG_ARGS': ' '.join((self.name,) + args)} 557 env = {'HG_ARGS': ' '.join((self.name,) + args)}
558
504 def _checkvar(m): 559 def _checkvar(m):
505 if m.groups()[0] == '$': 560 if m.groups()[0] == '$':
506 return m.group() 561 return m.group()
507 elif int(m.groups()[0]) <= len(args): 562 elif int(m.groups()[0]) <= len(args):
508 return m.group() 563 return m.group()
509 else: 564 else:
510 ui.debug("No argument found for substitution " 565 ui.debug(
511 "of %i variable in alias '%s' definition.\n" 566 "No argument found for substitution "
512 % (int(m.groups()[0]), self.name)) 567 "of %i variable in alias '%s' definition.\n"
568 % (int(m.groups()[0]), self.name)
569 )
513 return '' 570 return ''
571
514 cmd = re.sub(br'\$(\d+|\$)', _checkvar, shdef) 572 cmd = re.sub(br'\$(\d+|\$)', _checkvar, shdef)
515 cmd = aliasinterpolate(self.name, args, cmd) 573 cmd = aliasinterpolate(self.name, args, cmd)
516 return ui.system(cmd, environ=env, 574 return ui.system(
517 blockedtag='alias_%s' % self.name) 575 cmd, environ=env, blockedtag='alias_%s' % self.name
576 )
577
518 self.fn = fn 578 self.fn = fn
519 self.alias = True 579 self.alias = True
520 self._populatehelp(ui, name, shdef, self.fn) 580 self._populatehelp(ui, name, shdef, self.fn)
521 return 581 return
522 582
523 try: 583 try:
524 args = pycompat.shlexsplit(self.definition) 584 args = pycompat.shlexsplit(self.definition)
525 except ValueError as inst: 585 except ValueError as inst:
526 self.badalias = (_("error in definition for alias '%s': %s") 586 self.badalias = _("error in definition for alias '%s': %s") % (
527 % (self.name, stringutil.forcebytestr(inst))) 587 self.name,
588 stringutil.forcebytestr(inst),
589 )
528 return 590 return
529 earlyopts, args = _earlysplitopts(args) 591 earlyopts, args = _earlysplitopts(args)
530 if earlyopts: 592 if earlyopts:
531 self.badalias = (_("error in definition for alias '%s': %s may " 593 self.badalias = _(
532 "only be given on the command line") 594 "error in definition for alias '%s': %s may "
533 % (self.name, '/'.join(pycompat.ziplist(*earlyopts) 595 "only be given on the command line"
534 [0]))) 596 ) % (self.name, '/'.join(pycompat.ziplist(*earlyopts)[0]))
535 return 597 return
536 self.cmdname = cmd = args.pop(0) 598 self.cmdname = cmd = args.pop(0)
537 self.givenargs = args 599 self.givenargs = args
538 600
539 try: 601 try:
546 608
547 self.alias = True 609 self.alias = True
548 self._populatehelp(ui, name, cmd, self.fn, cmdhelp) 610 self._populatehelp(ui, name, cmd, self.fn, cmdhelp)
549 611
550 except error.UnknownCommand: 612 except error.UnknownCommand:
551 self.badalias = (_("alias '%s' resolves to unknown command '%s'") 613 self.badalias = _("alias '%s' resolves to unknown command '%s'") % (
552 % (self.name, cmd)) 614 self.name,
615 cmd,
616 )
553 self.unknowncmd = True 617 self.unknowncmd = True
554 except error.AmbiguousCommand: 618 except error.AmbiguousCommand:
555 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'") 619 self.badalias = _(
556 % (self.name, cmd)) 620 "alias '%s' resolves to ambiguous command '%s'"
621 ) % (self.name, cmd)
557 622
558 def _populatehelp(self, ui, name, cmd, fn, defaulthelp=None): 623 def _populatehelp(self, ui, name, cmd, fn, defaulthelp=None):
559 # confine strings to be passed to i18n.gettext() 624 # confine strings to be passed to i18n.gettext()
560 cfg = {} 625 cfg = {}
561 for k in ('doc', 'help', 'category'): 626 for k in ('doc', 'help', 'category'):
562 v = ui.config('alias', '%s:%s' % (name, k), None) 627 v = ui.config('alias', '%s:%s' % (name, k), None)
563 if v is None: 628 if v is None:
564 continue 629 continue
565 if not encoding.isasciistr(v): 630 if not encoding.isasciistr(v):
566 self.badalias = (_("non-ASCII character in alias definition " 631 self.badalias = _(
567 "'%s:%s'") % (name, k)) 632 "non-ASCII character in alias definition " "'%s:%s'"
633 ) % (name, k)
568 return 634 return
569 cfg[k] = v 635 cfg[k] = v
570 636
571 self.help = cfg.get('help', defaulthelp or '') 637 self.help = cfg.get('help', defaulthelp or '')
572 if self.help and self.help.startswith("hg " + cmd): 638 if self.help and self.help.startswith("hg " + cmd):
573 # drop prefix in old-style help lines so hg shows the alias 639 # drop prefix in old-style help lines so hg shows the alias
574 self.help = self.help[4 + len(cmd):] 640 self.help = self.help[4 + len(cmd) :]
575 641
576 self.owndoc = 'doc' in cfg 642 self.owndoc = 'doc' in cfg
577 doc = cfg.get('doc', pycompat.getdoc(fn)) 643 doc = cfg.get('doc', pycompat.getdoc(fn))
578 if doc is not None: 644 if doc is not None:
579 doc = pycompat.sysstr(doc) 645 doc = pycompat.sysstr(doc)
585 def args(self): 651 def args(self):
586 args = pycompat.maplist(util.expandpath, self.givenargs) 652 args = pycompat.maplist(util.expandpath, self.givenargs)
587 return aliasargs(self.fn, args) 653 return aliasargs(self.fn, args)
588 654
589 def __getattr__(self, name): 655 def __getattr__(self, name):
590 adefaults = {r'norepo': True, r'intents': set(), 656 adefaults = {
591 r'optionalrepo': False, r'inferrepo': False} 657 r'norepo': True,
658 r'intents': set(),
659 r'optionalrepo': False,
660 r'inferrepo': False,
661 }
592 if name not in adefaults: 662 if name not in adefaults:
593 raise AttributeError(name) 663 raise AttributeError(name)
594 if self.badalias or util.safehasattr(self, 'shell'): 664 if self.badalias or util.safehasattr(self, 'shell'):
595 return adefaults[name] 665 return adefaults[name]
596 return getattr(self.fn, name) 666 return getattr(self.fn, name)
605 hint = _("'%s' is provided by '%s' extension") % (cmd, ext) 675 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
606 except error.UnknownCommand: 676 except error.UnknownCommand:
607 pass 677 pass
608 raise error.Abort(self.badalias, hint=hint) 678 raise error.Abort(self.badalias, hint=hint)
609 if self.shadows: 679 if self.shadows:
610 ui.debug("alias '%s' shadows command '%s'\n" % 680 ui.debug(
611 (self.name, self.cmdname)) 681 "alias '%s' shadows command '%s'\n" % (self.name, self.cmdname)
612 682 )
613 ui.log('commandalias', "alias '%s' expands to '%s'\n", 683
614 self.name, self.definition) 684 ui.log(
685 'commandalias',
686 "alias '%s' expands to '%s'\n",
687 self.name,
688 self.definition,
689 )
615 if util.safehasattr(self, 'shell'): 690 if util.safehasattr(self, 'shell'):
616 return self.fn(ui, *args, **opts) 691 return self.fn(ui, *args, **opts)
617 else: 692 else:
618 try: 693 try:
619 return util.checksignature(self.fn)(ui, *args, **opts) 694 return util.checksignature(self.fn)(ui, *args, **opts)
620 except error.SignatureError: 695 except error.SignatureError:
621 args = ' '.join([self.cmdname] + self.args) 696 args = ' '.join([self.cmdname] + self.args)
622 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args)) 697 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
623 raise 698 raise
624 699
700
625 class lazyaliasentry(object): 701 class lazyaliasentry(object):
626 """like a typical command entry (func, opts, help), but is lazy""" 702 """like a typical command entry (func, opts, help), but is lazy"""
627 703
628 def __init__(self, ui, name, definition, cmdtable, source): 704 def __init__(self, ui, name, definition, cmdtable, source):
629 self.ui = ui 705 self.ui = ui
633 self.source = source 709 self.source = source
634 self.alias = True 710 self.alias = True
635 711
636 @util.propertycache 712 @util.propertycache
637 def _aliasdef(self): 713 def _aliasdef(self):
638 return cmdalias(self.ui, self.name, self.definition, self.cmdtable, 714 return cmdalias(
639 self.source) 715 self.ui, self.name, self.definition, self.cmdtable, self.source
716 )
640 717
641 def __getitem__(self, n): 718 def __getitem__(self, n):
642 aliasdef = self._aliasdef 719 aliasdef = self._aliasdef
643 if n == 0: 720 if n == 0:
644 return aliasdef 721 return aliasdef
654 yield self[i] 731 yield self[i]
655 732
656 def __len__(self): 733 def __len__(self):
657 return 3 734 return 3
658 735
736
659 def addaliases(ui, cmdtable): 737 def addaliases(ui, cmdtable):
660 # aliases are processed after extensions have been loaded, so they 738 # aliases are processed after extensions have been loaded, so they
661 # may use extension commands. Aliases can also use other alias definitions, 739 # may use extension commands. Aliases can also use other alias definitions,
662 # but only if they have been defined prior to the current definition. 740 # but only if they have been defined prior to the current definition.
663 for alias, definition in ui.configitems('alias', ignoresub=True): 741 for alias, definition in ui.configitems('alias', ignoresub=True):
670 748
671 source = ui.configsource('alias', alias) 749 source = ui.configsource('alias', alias)
672 entry = lazyaliasentry(ui, alias, definition, cmdtable, source) 750 entry = lazyaliasentry(ui, alias, definition, cmdtable, source)
673 cmdtable[alias] = entry 751 cmdtable[alias] = entry
674 752
753
675 def _parse(ui, args): 754 def _parse(ui, args):
676 options = {} 755 options = {}
677 cmdoptions = {} 756 cmdoptions = {}
678 757
679 try: 758 try:
681 except getopt.GetoptError as inst: 760 except getopt.GetoptError as inst:
682 raise error.CommandError(None, stringutil.forcebytestr(inst)) 761 raise error.CommandError(None, stringutil.forcebytestr(inst))
683 762
684 if args: 763 if args:
685 cmd, args = args[0], args[1:] 764 cmd, args = args[0], args[1:]
686 aliases, entry = cmdutil.findcmd(cmd, commands.table, 765 aliases, entry = cmdutil.findcmd(
687 ui.configbool("ui", "strict")) 766 cmd, commands.table, ui.configbool("ui", "strict")
767 )
688 cmd = aliases[0] 768 cmd = aliases[0]
689 args = aliasargs(entry[0], args) 769 args = aliasargs(entry[0], args)
690 defaults = ui.config("defaults", cmd) 770 defaults = ui.config("defaults", cmd)
691 if defaults: 771 if defaults:
692 args = pycompat.maplist( 772 args = (
693 util.expandpath, pycompat.shlexsplit(defaults)) + args 773 pycompat.maplist(util.expandpath, pycompat.shlexsplit(defaults))
774 + args
775 )
694 c = list(entry[1]) 776 c = list(entry[1])
695 else: 777 else:
696 cmd = None 778 cmd = None
697 c = [] 779 c = []
698 780
711 options[n] = cmdoptions[n] 793 options[n] = cmdoptions[n]
712 del cmdoptions[n] 794 del cmdoptions[n]
713 795
714 return (cmd, cmd and entry[0] or None, args, options, cmdoptions) 796 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
715 797
798
716 def _parseconfig(ui, config): 799 def _parseconfig(ui, config):
717 """parse the --config options from the command line""" 800 """parse the --config options from the command line"""
718 configs = [] 801 configs = []
719 802
720 for cfg in config: 803 for cfg in config:
721 try: 804 try:
722 name, value = [cfgelem.strip() 805 name, value = [cfgelem.strip() for cfgelem in cfg.split('=', 1)]
723 for cfgelem in cfg.split('=', 1)]
724 section, name = name.split('.', 1) 806 section, name = name.split('.', 1)
725 if not section or not name: 807 if not section or not name:
726 raise IndexError 808 raise IndexError
727 ui.setconfig(section, name, value, '--config') 809 ui.setconfig(section, name, value, '--config')
728 configs.append((section, name, value)) 810 configs.append((section, name, value))
729 except (IndexError, ValueError): 811 except (IndexError, ValueError):
730 raise error.Abort(_('malformed --config option: %r ' 812 raise error.Abort(
731 '(use --config section.name=value)') 813 _(
732 % pycompat.bytestr(cfg)) 814 'malformed --config option: %r '
815 '(use --config section.name=value)'
816 )
817 % pycompat.bytestr(cfg)
818 )
733 819
734 return configs 820 return configs
821
735 822
736 def _earlyparseopts(ui, args): 823 def _earlyparseopts(ui, args):
737 options = {} 824 options = {}
738 fancyopts.fancyopts(args, commands.globalopts, options, 825 fancyopts.fancyopts(
739 gnu=not ui.plain('strictflags'), early=True, 826 args,
740 optaliases={'repository': ['repo']}) 827 commands.globalopts,
828 options,
829 gnu=not ui.plain('strictflags'),
830 early=True,
831 optaliases={'repository': ['repo']},
832 )
741 return options 833 return options
834
742 835
743 def _earlysplitopts(args): 836 def _earlysplitopts(args):
744 """Split args into a list of possible early options and remainder args""" 837 """Split args into a list of possible early options and remainder args"""
745 shortoptions = 'R:' 838 shortoptions = 'R:'
746 # TODO: perhaps 'debugger' should be included 839 # TODO: perhaps 'debugger' should be included
747 longoptions = ['cwd=', 'repository=', 'repo=', 'config='] 840 longoptions = ['cwd=', 'repository=', 'repo=', 'config=']
748 return fancyopts.earlygetopt(args, shortoptions, longoptions, 841 return fancyopts.earlygetopt(
749 gnu=True, keepsep=True) 842 args, shortoptions, longoptions, gnu=True, keepsep=True
843 )
844
750 845
751 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions): 846 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
752 # run pre-hook, and abort if it fails 847 # run pre-hook, and abort if it fails
753 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs), 848 hook.hook(
754 pats=cmdpats, opts=cmdoptions) 849 lui,
850 repo,
851 "pre-%s" % cmd,
852 True,
853 args=" ".join(fullargs),
854 pats=cmdpats,
855 opts=cmdoptions,
856 )
755 try: 857 try:
756 ret = _runcommand(ui, options, cmd, d) 858 ret = _runcommand(ui, options, cmd, d)
757 # run post-hook, passing command result 859 # run post-hook, passing command result
758 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs), 860 hook.hook(
759 result=ret, pats=cmdpats, opts=cmdoptions) 861 lui,
862 repo,
863 "post-%s" % cmd,
864 False,
865 args=" ".join(fullargs),
866 result=ret,
867 pats=cmdpats,
868 opts=cmdoptions,
869 )
760 except Exception: 870 except Exception:
761 # run failure hook and re-raise 871 # run failure hook and re-raise
762 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs), 872 hook.hook(
763 pats=cmdpats, opts=cmdoptions) 873 lui,
874 repo,
875 "fail-%s" % cmd,
876 False,
877 args=" ".join(fullargs),
878 pats=cmdpats,
879 opts=cmdoptions,
880 )
764 raise 881 raise
765 return ret 882 return ret
883
766 884
767 def _getlocal(ui, rpath, wd=None): 885 def _getlocal(ui, rpath, wd=None):
768 """Return (path, local ui object) for the given target path. 886 """Return (path, local ui object) for the given target path.
769 887
770 Takes paths in [cwd]/.hg/hgrc into account." 888 Takes paths in [cwd]/.hg/hgrc into account."
771 """ 889 """
772 if wd is None: 890 if wd is None:
773 try: 891 try:
774 wd = encoding.getcwd() 892 wd = encoding.getcwd()
775 except OSError as e: 893 except OSError as e:
776 raise error.Abort(_("error getting current working directory: %s") % 894 raise error.Abort(
777 encoding.strtolocal(e.strerror)) 895 _("error getting current working directory: %s")
896 % encoding.strtolocal(e.strerror)
897 )
778 path = cmdutil.findrepo(wd) or "" 898 path = cmdutil.findrepo(wd) or ""
779 if not path: 899 if not path:
780 lui = ui 900 lui = ui
781 else: 901 else:
782 lui = ui.copy() 902 lui = ui.copy()
787 lui = ui.copy() 907 lui = ui.copy()
788 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path) 908 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
789 909
790 return path, lui 910 return path, lui
791 911
912
792 def _checkshellalias(lui, ui, args): 913 def _checkshellalias(lui, ui, args):
793 """Return the function to run the shell alias, if it is required""" 914 """Return the function to run the shell alias, if it is required"""
794 options = {} 915 options = {}
795 916
796 try: 917 try:
815 936
816 if cmd and util.safehasattr(fn, 'shell'): 937 if cmd and util.safehasattr(fn, 'shell'):
817 # shell alias shouldn't receive early options which are consumed by hg 938 # shell alias shouldn't receive early options which are consumed by hg
818 _earlyopts, args = _earlysplitopts(args) 939 _earlyopts, args = _earlysplitopts(args)
819 d = lambda: fn(ui, *args[1:]) 940 d = lambda: fn(ui, *args[1:])
820 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, 941 return lambda: runcommand(
821 [], {}) 942 lui, None, cmd, args[:1], ui, options, d, [], {}
943 )
944
822 945
823 def _dispatch(req): 946 def _dispatch(req):
824 args = req.args 947 args = req.args
825 ui = req.ui 948 ui = req.ui
826 949
835 uis = {ui, lui} 958 uis = {ui, lui}
836 959
837 if req.repo: 960 if req.repo:
838 uis.add(req.repo.ui) 961 uis.add(req.repo.ui)
839 962
840 if (req.earlyoptions['verbose'] or req.earlyoptions['debug'] 963 if (
841 or req.earlyoptions['quiet']): 964 req.earlyoptions['verbose']
965 or req.earlyoptions['debug']
966 or req.earlyoptions['quiet']
967 ):
842 for opt in ('verbose', 'debug', 'quiet'): 968 for opt in ('verbose', 'debug', 'quiet'):
843 val = pycompat.bytestr(bool(req.earlyoptions[opt])) 969 val = pycompat.bytestr(bool(req.earlyoptions[opt]))
844 for ui_ in uis: 970 for ui_ in uis:
845 ui_.setconfig('ui', opt, val, '--' + opt) 971 ui_.setconfig('ui', opt, val, '--' + opt)
846 972
885 if options["config"] != req.earlyoptions["config"]: 1011 if options["config"] != req.earlyoptions["config"]:
886 raise error.Abort(_("option --config may not be abbreviated!")) 1012 raise error.Abort(_("option --config may not be abbreviated!"))
887 if options["cwd"] != req.earlyoptions["cwd"]: 1013 if options["cwd"] != req.earlyoptions["cwd"]:
888 raise error.Abort(_("option --cwd may not be abbreviated!")) 1014 raise error.Abort(_("option --cwd may not be abbreviated!"))
889 if options["repository"] != req.earlyoptions["repository"]: 1015 if options["repository"] != req.earlyoptions["repository"]:
890 raise error.Abort(_( 1016 raise error.Abort(
891 "option -R has to be separated from other options (e.g. not " 1017 _(
892 "-qR) and --repository may only be abbreviated as --repo!")) 1018 "option -R has to be separated from other options (e.g. not "
1019 "-qR) and --repository may only be abbreviated as --repo!"
1020 )
1021 )
893 if options["debugger"] != req.earlyoptions["debugger"]: 1022 if options["debugger"] != req.earlyoptions["debugger"]:
894 raise error.Abort(_("option --debugger may not be abbreviated!")) 1023 raise error.Abort(_("option --debugger may not be abbreviated!"))
895 # don't validate --profile/--traceback, which can be enabled from now 1024 # don't validate --profile/--traceback, which can be enabled from now
896 1025
897 if options["encoding"]: 1026 if options["encoding"]:
898 encoding.encoding = options["encoding"] 1027 encoding.encoding = options["encoding"]
899 if options["encodingmode"]: 1028 if options["encodingmode"]:
900 encoding.encodingmode = options["encodingmode"] 1029 encoding.encodingmode = options["encodingmode"]
901 if options["time"]: 1030 if options["time"]:
1031
902 def get_times(): 1032 def get_times():
903 t = os.times() 1033 t = os.times()
904 if t[4] == 0.0: 1034 if t[4] == 0.0:
905 # Windows leaves this as zero, so use time.clock() 1035 # Windows leaves this as zero, so use time.clock()
906 t = (t[0], t[1], t[2], t[3], time.clock()) 1036 t = (t[0], t[1], t[2], t[3], time.clock())
907 return t 1037 return t
1038
908 s = get_times() 1039 s = get_times()
1040
909 def print_time(): 1041 def print_time():
910 t = get_times() 1042 t = get_times()
911 ui.warn( 1043 ui.warn(
912 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") % 1044 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n")
913 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3])) 1045 % (
1046 t[4] - s[4],
1047 t[0] - s[0],
1048 t[2] - s[2],
1049 t[1] - s[1],
1050 t[3] - s[3],
1051 )
1052 )
1053
914 ui.atexit(print_time) 1054 ui.atexit(print_time)
915 if options["profile"]: 1055 if options["profile"]:
916 profiler.start() 1056 profiler.start()
917 1057
918 # if abbreviated version of this were used, take them in account, now 1058 # if abbreviated version of this were used, take them in account, now
975 repo.ui.fout = ui.fout 1115 repo.ui.fout = ui.fout
976 repo.ui.ferr = ui.ferr 1116 repo.ui.ferr = ui.ferr
977 repo.ui.fmsg = ui.fmsg 1117 repo.ui.fmsg = ui.fmsg
978 else: 1118 else:
979 try: 1119 try:
980 repo = hg.repository(ui, path=path, 1120 repo = hg.repository(
981 presetupfuncs=req.prereposetups, 1121 ui,
982 intents=func.intents) 1122 path=path,
1123 presetupfuncs=req.prereposetups,
1124 intents=func.intents,
1125 )
983 if not repo.local(): 1126 if not repo.local():
984 raise error.Abort(_("repository '%s' is not local") 1127 raise error.Abort(
985 % path) 1128 _("repository '%s' is not local") % path
986 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 1129 )
987 'repo') 1130 repo.ui.setconfig(
1131 "bundle", "mainreporoot", repo.root, 'repo'
1132 )
988 except error.RequirementError: 1133 except error.RequirementError:
989 raise 1134 raise
990 except error.RepoError: 1135 except error.RepoError:
991 if rpath: # invalid -R path 1136 if rpath: # invalid -R path
992 raise 1137 raise
993 if not func.optionalrepo: 1138 if not func.optionalrepo:
994 if func.inferrepo and args and not path: 1139 if func.inferrepo and args and not path:
995 # try to infer -R from command args 1140 # try to infer -R from command args
996 repos = pycompat.maplist(cmdutil.findrepo, args) 1141 repos = pycompat.maplist(cmdutil.findrepo, args)
998 if guess and repos.count(guess) == len(repos): 1143 if guess and repos.count(guess) == len(repos):
999 req.args = ['--repository', guess] + fullargs 1144 req.args = ['--repository', guess] + fullargs
1000 req.earlyoptions['repository'] = guess 1145 req.earlyoptions['repository'] = guess
1001 return _dispatch(req) 1146 return _dispatch(req)
1002 if not path: 1147 if not path:
1003 raise error.RepoError(_("no repository found in" 1148 raise error.RepoError(
1004 " '%s' (.hg not found)") 1149 _(
1005 % encoding.getcwd()) 1150 "no repository found in"
1151 " '%s' (.hg not found)"
1152 )
1153 % encoding.getcwd()
1154 )
1006 raise 1155 raise
1007 if repo: 1156 if repo:
1008 ui = repo.ui 1157 ui = repo.ui
1009 if options['hidden']: 1158 if options['hidden']:
1010 repo = repo.unfiltered() 1159 repo = repo.unfiltered()
1015 msg = _formatargs(fullargs) 1164 msg = _formatargs(fullargs)
1016 ui.log("command", '%s\n', msg) 1165 ui.log("command", '%s\n', msg)
1017 strcmdopt = pycompat.strkwargs(cmdoptions) 1166 strcmdopt = pycompat.strkwargs(cmdoptions)
1018 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt) 1167 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
1019 try: 1168 try:
1020 return runcommand(lui, repo, cmd, fullargs, ui, options, d, 1169 return runcommand(
1021 cmdpats, cmdoptions) 1170 lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions
1171 )
1022 finally: 1172 finally:
1023 if repo and repo != req.repo: 1173 if repo and repo != req.repo:
1024 repo.close() 1174 repo.close()
1175
1025 1176
1026 def _runcommand(ui, options, cmd, cmdfunc): 1177 def _runcommand(ui, options, cmd, cmdfunc):
1027 """Run a command function, possibly with profiling enabled.""" 1178 """Run a command function, possibly with profiling enabled."""
1028 try: 1179 try:
1029 with tracing.log("Running %s command" % cmd): 1180 with tracing.log("Running %s command" % cmd):
1030 return cmdfunc() 1181 return cmdfunc()
1031 except error.SignatureError: 1182 except error.SignatureError:
1032 raise error.CommandError(cmd, _('invalid arguments')) 1183 raise error.CommandError(cmd, _('invalid arguments'))
1184
1033 1185
1034 def _exceptionwarning(ui): 1186 def _exceptionwarning(ui):
1035 """Produce a warning message for the current active exception""" 1187 """Produce a warning message for the current active exception"""
1036 1188
1037 # For compatibility checking, we discard the portion of the hg 1189 # For compatibility checking, we discard the portion of the hg
1067 if worst[0] is None or nearest < worst[1]: 1219 if worst[0] is None or nearest < worst[1]:
1068 worst = name, nearest, report 1220 worst = name, nearest, report
1069 if worst[0] is not None: 1221 if worst[0] is not None:
1070 name, testedwith, report = worst 1222 name, testedwith, report = worst
1071 if not isinstance(testedwith, (bytes, str)): 1223 if not isinstance(testedwith, (bytes, str)):
1072 testedwith = '.'.join([stringutil.forcebytestr(c) 1224 testedwith = '.'.join(
1073 for c in testedwith]) 1225 [stringutil.forcebytestr(c) for c in testedwith]
1074 warning = (_('** Unknown exception encountered with ' 1226 )
1075 'possibly-broken third-party extension %s\n' 1227 warning = _(
1076 '** which supports versions %s of Mercurial.\n' 1228 '** Unknown exception encountered with '
1077 '** Please disable %s and try your action again.\n' 1229 'possibly-broken third-party extension %s\n'
1078 '** If that fixes the bug please report it to %s\n') 1230 '** which supports versions %s of Mercurial.\n'
1079 % (name, testedwith, name, stringutil.forcebytestr(report))) 1231 '** Please disable %s and try your action again.\n'
1232 '** If that fixes the bug please report it to %s\n'
1233 ) % (name, testedwith, name, stringutil.forcebytestr(report))
1080 else: 1234 else:
1081 bugtracker = ui.config('ui', 'supportcontact') 1235 bugtracker = ui.config('ui', 'supportcontact')
1082 if bugtracker is None: 1236 if bugtracker is None:
1083 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker") 1237 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
1084 warning = (_("** unknown exception encountered, " 1238 warning = (
1085 "please report by visiting\n** ") + bugtracker + '\n') 1239 _(
1240 "** unknown exception encountered, "
1241 "please report by visiting\n** "
1242 )
1243 + bugtracker
1244 + '\n'
1245 )
1086 sysversion = pycompat.sysbytes(sys.version).replace('\n', '') 1246 sysversion = pycompat.sysbytes(sys.version).replace('\n', '')
1087 warning += ((_("** Python %s\n") % sysversion) + 1247 warning += (
1088 (_("** Mercurial Distributed SCM (version %s)\n") % 1248 (_("** Python %s\n") % sysversion)
1089 util.version()) + 1249 + (_("** Mercurial Distributed SCM (version %s)\n") % util.version())
1090 (_("** Extensions loaded: %s\n") % 1250 + (
1091 ", ".join([x[0] for x in extensions.extensions()]))) 1251 _("** Extensions loaded: %s\n")
1252 % ", ".join([x[0] for x in extensions.extensions()])
1253 )
1254 )
1092 return warning 1255 return warning
1256
1093 1257
1094 def handlecommandexception(ui): 1258 def handlecommandexception(ui):
1095 """Produce a warning message for broken commands 1259 """Produce a warning message for broken commands
1096 1260
1097 Called when handling an exception; the exception is reraised if 1261 Called when handling an exception; the exception is reraised if
1098 this function returns False, ignored otherwise. 1262 this function returns False, ignored otherwise.
1099 """ 1263 """
1100 warning = _exceptionwarning(ui) 1264 warning = _exceptionwarning(ui)
1101 ui.log("commandexception", "%s\n%s\n", warning, 1265 ui.log(
1102 pycompat.sysbytes(traceback.format_exc())) 1266 "commandexception",
1267 "%s\n%s\n",
1268 warning,
1269 pycompat.sysbytes(traceback.format_exc()),
1270 )
1103 ui.warn(warning) 1271 ui.warn(warning)
1104 return False # re-raise the exception 1272 return False # re-raise the exception