Mercurial > public > mercurial-scm > hg
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 |