branch | stable |
changeset 32054 | 616e788321cc |
parent 32050 | 77eaf9539499 |
parent 32044 | cde72a195f32 |
child 32111 | 1208b74841ff |
child 32383 | f928d53b687c |
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 |