comparison mercurial/dispatch.py @ 32054:616e788321cc stable 4.2-rc

freeze: merge default into stable for 4.2 code freeze
author Augie Fackler <augie@google.com>
date Tue, 18 Apr 2017 12:24:34 -0400
parents 77eaf9539499 cde72a195f32
children 1208b74841ff f928d53b687c
comparison
equal deleted inserted replaced
32053:52902059edc7 32054:616e788321cc
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 from __future__ import absolute_import, print_function 8 from __future__ import absolute_import, print_function
9 9
10 import atexit
11 import difflib 10 import difflib
12 import errno 11 import errno
13 import getopt 12 import getopt
14 import os 13 import os
15 import pdb 14 import pdb
31 encoding, 30 encoding,
32 error, 31 error,
33 extensions, 32 extensions,
34 fancyopts, 33 fancyopts,
35 fileset, 34 fileset,
35 help,
36 hg, 36 hg,
37 hook, 37 hook,
38 profiling, 38 profiling,
39 pycompat, 39 pycompat,
40 revset, 40 revset,
56 # input/output/error streams 56 # input/output/error streams
57 self.fin = fin 57 self.fin = fin
58 self.fout = fout 58 self.fout = fout
59 self.ferr = ferr 59 self.ferr = ferr
60 60
61 def _runexithandlers(self):
62 exc = None
63 handlers = self.ui._exithandlers
64 try:
65 while handlers:
66 func, args, kwargs = handlers.pop()
67 try:
68 func(*args, **kwargs)
69 except: # re-raises below
70 if exc is None:
71 exc = sys.exc_info()[1]
72 self.ui.warn(('error in exit handlers:\n'))
73 self.ui.traceback(force=True)
74 finally:
75 if exc is not None:
76 raise exc
77
61 def run(): 78 def run():
62 "run the command in sys.argv" 79 "run the command in sys.argv"
63 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255) 80 req = request(pycompat.sysargv[1:])
81 err = None
82 try:
83 status = (dispatch(req) or 0) & 255
84 except error.StdioError as err:
85 status = -1
86 if util.safehasattr(req.ui, 'fout'):
87 try:
88 req.ui.fout.close()
89 except IOError as err:
90 status = -1
91 if util.safehasattr(req.ui, 'ferr'):
92 if err is not None and err.errno != errno.EPIPE:
93 req.ui.ferr.write('abort: %s\n' % err.strerror)
94 req.ui.ferr.close()
95 sys.exit(status & 255)
64 96
65 def _getsimilar(symbols, value): 97 def _getsimilar(symbols, value):
66 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio() 98 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
67 # The cutoff for similarity here is pretty arbitrary. It should 99 # The cutoff for similarity here is pretty arbitrary. It should
68 # probably be investigated and tweaked. 100 # probably be investigated and tweaked.
89 write(_("hg: parse error: %s\n") % inst.args[0]) 121 write(_("hg: parse error: %s\n") % inst.args[0])
90 _reportsimilar(write, similar) 122 _reportsimilar(write, similar)
91 if inst.hint: 123 if inst.hint:
92 write(_("(%s)\n") % inst.hint) 124 write(_("(%s)\n") % inst.hint)
93 125
126 def _formatargs(args):
127 return ' '.join(util.shellquote(a) for a in args)
128
94 def dispatch(req): 129 def dispatch(req):
95 "run the command specified in req.args" 130 "run the command specified in req.args"
96 if req.ferr: 131 if req.ferr:
97 ferr = req.ferr 132 ferr = req.ferr
98 elif req.ui: 133 elif req.ui:
120 return -1 155 return -1
121 except error.ParseError as inst: 156 except error.ParseError as inst:
122 _formatparse(ferr.write, inst) 157 _formatparse(ferr.write, inst)
123 return -1 158 return -1
124 159
125 msg = ' '.join(' ' in a and repr(a) or a for a in req.args) 160 msg = _formatargs(req.args)
126 starttime = time.time() 161 starttime = util.timer()
127 ret = None 162 ret = None
128 try: 163 try:
129 ret = _runcatch(req) 164 ret = _runcatch(req)
130 except KeyboardInterrupt: 165 except KeyboardInterrupt:
131 try: 166 try:
132 req.ui.warn(_("interrupted!\n")) 167 req.ui.warn(_("interrupted!\n"))
168 except error.SignalInterrupt:
169 # maybe pager would quit without consuming all the output, and
170 # SIGPIPE was raised. we cannot print anything in this case.
171 pass
133 except IOError as inst: 172 except IOError as inst:
134 if inst.errno != errno.EPIPE: 173 if inst.errno != errno.EPIPE:
135 raise 174 raise
136 ret = -1 175 ret = -1
137 finally: 176 finally:
138 duration = time.time() - starttime 177 duration = util.timer() - starttime
139 req.ui.flush() 178 req.ui.flush()
179 if req.ui.logblockedtimes:
180 req.ui._blockedtimes['command_duration'] = duration * 1000
181 req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes)
140 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n", 182 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
141 msg, ret or 0, duration) 183 msg, ret or 0, duration)
184 try:
185 req._runexithandlers()
186 except: # exiting, so no re-raises
187 ret = ret or -1
142 return ret 188 return ret
143 189
144 def _runcatch(req): 190 def _runcatch(req):
145 def catchterm(*args): 191 def catchterm(*args):
146 raise error.SignalInterrupt 192 raise error.SignalInterrupt
242 except: # re-raises 288 except: # re-raises
243 # enter the debugger when we hit an exception 289 # enter the debugger when we hit an exception
244 if '--debugger' in req.args: 290 if '--debugger' in req.args:
245 traceback.print_exc() 291 traceback.print_exc()
246 debugmortem[debugger](sys.exc_info()[2]) 292 debugmortem[debugger](sys.exc_info()[2])
247 ui.traceback()
248 raise 293 raise
249 294
250 return callcatch(ui, _runcatchfunc) 295 return _callcatch(ui, _runcatchfunc)
251 296
252 def callcatch(ui, func): 297 def _callcatch(ui, func):
253 """like scmutil.callcatch but handles more high-level exceptions about 298 """like scmutil.callcatch but handles more high-level exceptions about
254 config parsing and commands. besides, use handlecommandexception to handle 299 config parsing and commands. besides, use handlecommandexception to handle
255 uncaught exceptions. 300 uncaught exceptions.
256 """ 301 """
257 try: 302 try:
259 except error.AmbiguousCommand as inst: 304 except error.AmbiguousCommand as inst:
260 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") % 305 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
261 (inst.args[0], " ".join(inst.args[1]))) 306 (inst.args[0], " ".join(inst.args[1])))
262 except error.CommandError as inst: 307 except error.CommandError as inst:
263 if inst.args[0]: 308 if inst.args[0]:
309 ui.pager('help')
264 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1])) 310 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
265 commands.help_(ui, inst.args[0], full=False, command=True) 311 commands.help_(ui, inst.args[0], full=False, command=True)
266 else: 312 else:
313 ui.pager('help')
267 ui.warn(_("hg: %s\n") % inst.args[1]) 314 ui.warn(_("hg: %s\n") % inst.args[1])
268 commands.help_(ui, 'shortlist') 315 commands.help_(ui, 'shortlist')
269 except error.ParseError as inst: 316 except error.ParseError as inst:
270 _formatparse(ui.warn, inst) 317 _formatparse(ui.warn, inst)
271 return -1 318 return -1
272 except error.UnknownCommand as inst: 319 except error.UnknownCommand as inst:
273 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0]) 320 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
274 try: 321 try:
275 # check if the command is in a disabled extension 322 # check if the command is in a disabled extension
276 # (but don't check for extensions themselves) 323 # (but don't check for extensions themselves)
277 commands.help_(ui, inst.args[0], unknowncmd=True) 324 formatted = help.formattedhelp(ui, inst.args[0], unknowncmd=True)
325 ui.warn(nocmdmsg)
326 ui.write(formatted)
278 except (error.UnknownCommand, error.Abort): 327 except (error.UnknownCommand, error.Abort):
279 suggested = False 328 suggested = False
280 if len(inst.args) == 2: 329 if len(inst.args) == 2:
281 sim = _getsimilar(inst.args[1], inst.args[0]) 330 sim = _getsimilar(inst.args[1], inst.args[0])
282 if sim: 331 if sim:
332 ui.warn(nocmdmsg)
283 _reportsimilar(ui.warn, sim) 333 _reportsimilar(ui.warn, sim)
284 suggested = True 334 suggested = True
285 if not suggested: 335 if not suggested:
336 ui.pager('help')
337 ui.warn(nocmdmsg)
286 commands.help_(ui, 'shortlist') 338 commands.help_(ui, 'shortlist')
287 except IOError: 339 except IOError:
288 raise 340 raise
289 except KeyboardInterrupt: 341 except KeyboardInterrupt:
290 raise 342 raise
304 num = int(m.group(1)) - 1 356 num = int(m.group(1)) - 1
305 nums.append(num) 357 nums.append(num)
306 if num < len(givenargs): 358 if num < len(givenargs):
307 return givenargs[num] 359 return givenargs[num]
308 raise error.Abort(_('too few arguments for command alias')) 360 raise error.Abort(_('too few arguments for command alias'))
309 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd) 361 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
310 givenargs = [x for i, x in enumerate(givenargs) 362 givenargs = [x for i, x in enumerate(givenargs)
311 if i not in nums] 363 if i not in nums]
312 args = pycompat.shlexsplit(cmd) 364 args = pycompat.shlexsplit(cmd)
313 return args + givenargs 365 return args + givenargs
314 366
374 "of %i variable in alias '%s' definition." 426 "of %i variable in alias '%s' definition."
375 % (int(m.groups()[0]), self.name)) 427 % (int(m.groups()[0]), self.name))
376 return '' 428 return ''
377 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:]) 429 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
378 cmd = aliasinterpolate(self.name, args, cmd) 430 cmd = aliasinterpolate(self.name, args, cmd)
379 return ui.system(cmd, environ=env) 431 return ui.system(cmd, environ=env,
432 blockedtag='alias_%s' % self.name)
380 self.fn = fn 433 self.fn = fn
381 return 434 return
382 435
383 try: 436 try:
384 args = pycompat.shlexsplit(self.definition) 437 args = pycompat.shlexsplit(self.definition)
416 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'") 469 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
417 % (self.name, cmd)) 470 % (self.name, cmd))
418 471
419 @property 472 @property
420 def args(self): 473 def args(self):
421 args = map(util.expandpath, self.givenargs) 474 args = pycompat.maplist(util.expandpath, self.givenargs)
422 return aliasargs(self.fn, args) 475 return aliasargs(self.fn, args)
423 476
424 def __getattr__(self, name): 477 def __getattr__(self, name):
425 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False} 478 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
426 if name not in adefaults: 479 if name not in adefaults:
489 ui.configbool("ui", "strict")) 542 ui.configbool("ui", "strict"))
490 cmd = aliases[0] 543 cmd = aliases[0]
491 args = aliasargs(entry[0], args) 544 args = aliasargs(entry[0], args)
492 defaults = ui.config("defaults", cmd) 545 defaults = ui.config("defaults", cmd)
493 if defaults: 546 if defaults:
494 args = map(util.expandpath, pycompat.shlexsplit(defaults)) + args 547 args = pycompat.maplist(
548 util.expandpath, pycompat.shlexsplit(defaults)) + args
495 c = list(entry[1]) 549 c = list(entry[1])
496 else: 550 else:
497 cmd = None 551 cmd = None
498 c = [] 552 c = []
499 553
684 os.chdir(cwd[-1]) 738 os.chdir(cwd[-1])
685 739
686 rpath = _earlygetopt(["-R", "--repository", "--repo"], args) 740 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
687 path, lui = _getlocal(ui, rpath) 741 path, lui = _getlocal(ui, rpath)
688 742
689 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
690 # reposetup. Programs like TortoiseHg will call _dispatch several
691 # times so we keep track of configured extensions in _loaded.
692 extensions.loadall(lui)
693 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
694 # Propagate any changes to lui.__class__ by extensions
695 ui.__class__ = lui.__class__
696
697 # (uisetup and extsetup are handled in extensions.loadall)
698
699 for name, module in exts:
700 for objname, loadermod, loadername in extraloaders:
701 extraobj = getattr(module, objname, None)
702 if extraobj is not None:
703 getattr(loadermod, loadername)(ui, name, extraobj)
704 _loaded.add(name)
705
706 # (reposetup is handled in hg.repository)
707
708 # Side-effect of accessing is debugcommands module is guaranteed to be 743 # Side-effect of accessing is debugcommands module is guaranteed to be
709 # imported and commands.table is populated. 744 # imported and commands.table is populated.
710 debugcommands.command 745 debugcommands.command
711 746
712 addaliases(lui, commands.table)
713
714 # All aliases and commands are completely defined, now.
715 # Check abbreviation/ambiguity of shell alias.
716 shellaliasfn = _checkshellalias(lui, ui, args)
717 if shellaliasfn:
718 with profiling.maybeprofile(lui):
719 return shellaliasfn()
720
721 # check for fallback encoding
722 fallback = lui.config('ui', 'fallbackencoding')
723 if fallback:
724 encoding.fallbackencoding = fallback
725
726 fullargs = args
727 cmd, func, args, options, cmdoptions = _parse(lui, args)
728
729 if options["config"]:
730 raise error.Abort(_("option --config may not be abbreviated!"))
731 if options["cwd"]:
732 raise error.Abort(_("option --cwd may not be abbreviated!"))
733 if options["repository"]:
734 raise error.Abort(_(
735 "option -R has to be separated from other options (e.g. not -qR) "
736 "and --repository may only be abbreviated as --repo!"))
737
738 if options["encoding"]:
739 encoding.encoding = options["encoding"]
740 if options["encodingmode"]:
741 encoding.encodingmode = options["encodingmode"]
742 if options["time"]:
743 def get_times():
744 t = os.times()
745 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
746 t = (t[0], t[1], t[2], t[3], time.clock())
747 return t
748 s = get_times()
749 def print_time():
750 t = get_times()
751 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
752 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
753 atexit.register(print_time)
754
755 uis = set([ui, lui]) 747 uis = set([ui, lui])
756 748
757 if req.repo: 749 if req.repo:
758 uis.add(req.repo.ui) 750 uis.add(req.repo.ui)
759 751
760 if options['verbose'] or options['debug'] or options['quiet']: 752 if '--profile' in args:
761 for opt in ('verbose', 'debug', 'quiet'):
762 val = str(bool(options[opt]))
763 for ui_ in uis:
764 ui_.setconfig('ui', opt, val, '--' + opt)
765
766 if options['profile']:
767 for ui_ in uis: 753 for ui_ in uis:
768 ui_.setconfig('profiling', 'enabled', 'true', '--profile') 754 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
769 755
770 if options['traceback']: 756 with profiling.maybeprofile(lui):
757 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
758 # reposetup. Programs like TortoiseHg will call _dispatch several
759 # times so we keep track of configured extensions in _loaded.
760 extensions.loadall(lui)
761 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
762 # Propagate any changes to lui.__class__ by extensions
763 ui.__class__ = lui.__class__
764
765 # (uisetup and extsetup are handled in extensions.loadall)
766
767 for name, module in exts:
768 for objname, loadermod, loadername in extraloaders:
769 extraobj = getattr(module, objname, None)
770 if extraobj is not None:
771 getattr(loadermod, loadername)(ui, name, extraobj)
772 _loaded.add(name)
773
774 # (reposetup is handled in hg.repository)
775
776 addaliases(lui, commands.table)
777
778 # All aliases and commands are completely defined, now.
779 # Check abbreviation/ambiguity of shell alias.
780 shellaliasfn = _checkshellalias(lui, ui, args)
781 if shellaliasfn:
782 return shellaliasfn()
783
784 # check for fallback encoding
785 fallback = lui.config('ui', 'fallbackencoding')
786 if fallback:
787 encoding.fallbackencoding = fallback
788
789 fullargs = args
790 cmd, func, args, options, cmdoptions = _parse(lui, args)
791
792 if options["config"]:
793 raise error.Abort(_("option --config may not be abbreviated!"))
794 if options["cwd"]:
795 raise error.Abort(_("option --cwd may not be abbreviated!"))
796 if options["repository"]:
797 raise error.Abort(_(
798 "option -R has to be separated from other options (e.g. not "
799 "-qR) and --repository may only be abbreviated as --repo!"))
800
801 if options["encoding"]:
802 encoding.encoding = options["encoding"]
803 if options["encodingmode"]:
804 encoding.encodingmode = options["encodingmode"]
805 if options["time"]:
806 def get_times():
807 t = os.times()
808 if t[4] == 0.0:
809 # Windows leaves this as zero, so use time.clock()
810 t = (t[0], t[1], t[2], t[3], time.clock())
811 return t
812 s = get_times()
813 def print_time():
814 t = get_times()
815 ui.warn(
816 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
817 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
818 ui.atexit(print_time)
819
820 if options['verbose'] or options['debug'] or options['quiet']:
821 for opt in ('verbose', 'debug', 'quiet'):
822 val = str(bool(options[opt]))
823 if pycompat.ispy3:
824 val = val.encode('ascii')
825 for ui_ in uis:
826 ui_.setconfig('ui', opt, val, '--' + opt)
827
828 if options['traceback']:
829 for ui_ in uis:
830 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
831
832 if options['noninteractive']:
833 for ui_ in uis:
834 ui_.setconfig('ui', 'interactive', 'off', '-y')
835
836 if util.parsebool(options['pager']):
837 ui.pager('internal-always-' + cmd)
838 elif options['pager'] != 'auto':
839 ui.disablepager()
840
841 if cmdoptions.get('insecure', False):
842 for ui_ in uis:
843 ui_.insecureconnections = True
844
845 # setup color handling
846 coloropt = options['color']
771 for ui_ in uis: 847 for ui_ in uis:
772 ui_.setconfig('ui', 'traceback', 'on', '--traceback') 848 if coloropt:
773 849 ui_.setconfig('ui', 'color', coloropt, '--color')
774 if options['noninteractive']: 850 color.setup(ui_)
775 for ui_ in uis: 851
776 ui_.setconfig('ui', 'interactive', 'off', '-y') 852 if options['version']:
777 853 return commands.version_(ui)
778 if cmdoptions.get('insecure', False): 854 if options['help']:
779 for ui_ in uis: 855 return commands.help_(ui, cmd, command=cmd is not None)
780 ui_.insecureconnections = True 856 elif not cmd:
781 857 return commands.help_(ui, 'shortlist')
782 if options['version']: 858
783 return commands.version_(ui)
784 if options['help']:
785 return commands.help_(ui, cmd, command=cmd is not None)
786 elif not cmd:
787 return commands.help_(ui, 'shortlist')
788
789 with profiling.maybeprofile(lui):
790 repo = None 859 repo = None
791 cmdpats = args[:] 860 cmdpats = args[:]
792 if not func.norepo: 861 if not func.norepo:
793 # use the repo from the request only if we don't have -R 862 # use the repo from the request only if we don't have -R
794 if not rpath and not cwd: 863 if not rpath and not cwd:
831 repo = repo.unfiltered() 900 repo = repo.unfiltered()
832 args.insert(0, repo) 901 args.insert(0, repo)
833 elif rpath: 902 elif rpath:
834 ui.warn(_("warning: --repository ignored\n")) 903 ui.warn(_("warning: --repository ignored\n"))
835 904
836 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs) 905 msg = _formatargs(fullargs)
837 ui.log("command", '%s\n', msg) 906 ui.log("command", '%s\n', msg)
838 strcmdopt = pycompat.strkwargs(cmdoptions) 907 strcmdopt = pycompat.strkwargs(cmdoptions)
839 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt) 908 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
840 try: 909 try:
841 return runcommand(lui, repo, cmd, fullargs, ui, options, d, 910 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
864 ct = util.versiontuple(n=2) 933 ct = util.versiontuple(n=2)
865 worst = None, ct, '' 934 worst = None, ct, ''
866 if ui.config('ui', 'supportcontact', None) is None: 935 if ui.config('ui', 'supportcontact', None) is None:
867 for name, mod in extensions.extensions(): 936 for name, mod in extensions.extensions():
868 testedwith = getattr(mod, 'testedwith', '') 937 testedwith = getattr(mod, 'testedwith', '')
938 if pycompat.ispy3 and isinstance(testedwith, str):
939 testedwith = testedwith.encode(u'utf-8')
869 report = getattr(mod, 'buglink', _('the extension author.')) 940 report = getattr(mod, 'buglink', _('the extension author.'))
870 if not testedwith.strip(): 941 if not testedwith.strip():
871 # We found an untested extension. It's likely the culprit. 942 # We found an untested extension. It's likely the culprit.
872 worst = name, 'unknown', report 943 worst = name, 'unknown', report
873 break 944 break
884 nearest = max(lower or tested) 955 nearest = max(lower or tested)
885 if worst[0] is None or nearest < worst[1]: 956 if worst[0] is None or nearest < worst[1]:
886 worst = name, nearest, report 957 worst = name, nearest, report
887 if worst[0] is not None: 958 if worst[0] is not None:
888 name, testedwith, report = worst 959 name, testedwith, report = worst
889 if not isinstance(testedwith, str): 960 if not isinstance(testedwith, (bytes, str)):
890 testedwith = '.'.join([str(c) for c in testedwith]) 961 testedwith = '.'.join([str(c) for c in testedwith])
891 warning = (_('** Unknown exception encountered with ' 962 warning = (_('** Unknown exception encountered with '
892 'possibly-broken third-party extension %s\n' 963 'possibly-broken third-party extension %s\n'
893 '** which supports versions %s of Mercurial.\n' 964 '** which supports versions %s of Mercurial.\n'
894 '** Please disable %s and try your action again.\n' 965 '** Please disable %s and try your action again.\n'
898 bugtracker = ui.config('ui', 'supportcontact', None) 969 bugtracker = ui.config('ui', 'supportcontact', None)
899 if bugtracker is None: 970 if bugtracker is None:
900 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker") 971 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
901 warning = (_("** unknown exception encountered, " 972 warning = (_("** unknown exception encountered, "
902 "please report by visiting\n** ") + bugtracker + '\n') 973 "please report by visiting\n** ") + bugtracker + '\n')
903 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) + 974 if pycompat.ispy3:
975 sysversion = sys.version.encode(u'utf-8')
976 else:
977 sysversion = sys.version
978 sysversion = sysversion.replace('\n', '')
979 warning += ((_("** Python %s\n") % sysversion) +
904 (_("** Mercurial Distributed SCM (version %s)\n") % 980 (_("** Mercurial Distributed SCM (version %s)\n") %
905 util.version()) + 981 util.version()) +
906 (_("** Extensions loaded: %s\n") % 982 (_("** Extensions loaded: %s\n") %
907 ", ".join([x[0] for x in extensions.extensions()]))) 983 ", ".join([x[0] for x in extensions.extensions()])))
908 return warning 984 return warning