mercurial/extensions.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43085 eef9a2d67051
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    32 _extensions = {}
    32 _extensions = {}
    33 _disabledextensions = {}
    33 _disabledextensions = {}
    34 _aftercallbacks = {}
    34 _aftercallbacks = {}
    35 _order = []
    35 _order = []
    36 _builtin = {
    36 _builtin = {
    37     'hbisect',
    37     b'hbisect',
    38     'bookmarks',
    38     b'bookmarks',
    39     'color',
    39     b'color',
    40     'parentrevspec',
    40     b'parentrevspec',
    41     'progress',
    41     b'progress',
    42     'interhg',
    42     b'interhg',
    43     'inotify',
    43     b'inotify',
    44     'hgcia',
    44     b'hgcia',
    45     'shelve',
    45     b'shelve',
    46 }
    46 }
    47 
    47 
    48 
    48 
    49 def extensions(ui=None):
    49 def extensions(ui=None):
    50     if ui:
    50     if ui:
    51 
    51 
    52         def enabled(name):
    52         def enabled(name):
    53             for format in ['%s', 'hgext.%s']:
    53             for format in [b'%s', b'hgext.%s']:
    54                 conf = ui.config('extensions', format % name)
    54                 conf = ui.config(b'extensions', format % name)
    55                 if conf is not None and not conf.startswith('!'):
    55                 if conf is not None and not conf.startswith(b'!'):
    56                     return True
    56                     return True
    57 
    57 
    58     else:
    58     else:
    59         enabled = lambda name: True
    59         enabled = lambda name: True
    60     for name in _order:
    60     for name in _order:
    68     mod = None
    68     mod = None
    69     try:
    69     try:
    70         mod = _extensions[name]
    70         mod = _extensions[name]
    71     except KeyError:
    71     except KeyError:
    72         for k, v in _extensions.iteritems():
    72         for k, v in _extensions.iteritems():
    73             if k.endswith('.' + name) or k.endswith('/' + name):
    73             if k.endswith(b'.' + name) or k.endswith(b'/' + name):
    74                 mod = v
    74                 mod = v
    75                 break
    75                 break
    76     if not mod:
    76     if not mod:
    77         raise KeyError(name)
    77         raise KeyError(name)
    78     return mod
    78     return mod
    79 
    79 
    80 
    80 
    81 def loadpath(path, module_name):
    81 def loadpath(path, module_name):
    82     module_name = module_name.replace('.', '_')
    82     module_name = module_name.replace(b'.', b'_')
    83     path = util.normpath(util.expandpath(path))
    83     path = util.normpath(util.expandpath(path))
    84     module_name = pycompat.fsdecode(module_name)
    84     module_name = pycompat.fsdecode(module_name)
    85     path = pycompat.fsdecode(path)
    85     path = pycompat.fsdecode(path)
    86     if os.path.isdir(path):
    86     if os.path.isdir(path):
    87         # module/__init__.py style
    87         # module/__init__.py style
    98 
    98 
    99 
    99 
   100 def _importh(name):
   100 def _importh(name):
   101     """import and return the <name> module"""
   101     """import and return the <name> module"""
   102     mod = __import__(pycompat.sysstr(name))
   102     mod = __import__(pycompat.sysstr(name))
   103     components = name.split('.')
   103     components = name.split(b'.')
   104     for comp in components[1:]:
   104     for comp in components[1:]:
   105         mod = getattr(mod, comp)
   105         mod = getattr(mod, comp)
   106     return mod
   106     return mod
   107 
   107 
   108 
   108 
   109 def _importext(name, path=None, reportfunc=None):
   109 def _importext(name, path=None, reportfunc=None):
   110     if path:
   110     if path:
   111         # the module will be loaded in sys.modules
   111         # the module will be loaded in sys.modules
   112         # choose an unique name so that it doesn't
   112         # choose an unique name so that it doesn't
   113         # conflicts with other modules
   113         # conflicts with other modules
   114         mod = loadpath(path, 'hgext.%s' % name)
   114         mod = loadpath(path, b'hgext.%s' % name)
   115     else:
   115     else:
   116         try:
   116         try:
   117             mod = _importh("hgext.%s" % name)
   117             mod = _importh(b"hgext.%s" % name)
   118         except ImportError as err:
   118         except ImportError as err:
   119             if reportfunc:
   119             if reportfunc:
   120                 reportfunc(err, "hgext.%s" % name, "hgext3rd.%s" % name)
   120                 reportfunc(err, b"hgext.%s" % name, b"hgext3rd.%s" % name)
   121             try:
   121             try:
   122                 mod = _importh("hgext3rd.%s" % name)
   122                 mod = _importh(b"hgext3rd.%s" % name)
   123             except ImportError as err:
   123             except ImportError as err:
   124                 if reportfunc:
   124                 if reportfunc:
   125                     reportfunc(err, "hgext3rd.%s" % name, name)
   125                     reportfunc(err, b"hgext3rd.%s" % name, name)
   126                 mod = _importh(name)
   126                 mod = _importh(name)
   127     return mod
   127     return mod
   128 
   128 
   129 
   129 
   130 def _reportimporterror(ui, err, failed, next):
   130 def _reportimporterror(ui, err, failed, next):
   135         b'    - could not import %s (%s): trying %s\n',
   135         b'    - could not import %s (%s): trying %s\n',
   136         failed,
   136         failed,
   137         stringutil.forcebytestr(err),
   137         stringutil.forcebytestr(err),
   138         next,
   138         next,
   139     )
   139     )
   140     if ui.debugflag and ui.configbool('devel', 'debug.extensions'):
   140     if ui.debugflag and ui.configbool(b'devel', b'debug.extensions'):
   141         ui.traceback()
   141         ui.traceback()
   142 
   142 
   143 
   143 
   144 def _rejectunicode(name, xs):
   144 def _rejectunicode(name, xs):
   145     if isinstance(xs, (list, set, tuple)):
   145     if isinstance(xs, (list, set, tuple)):
   150             _rejectunicode(name, k)
   150             _rejectunicode(name, k)
   151             _rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v)
   151             _rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v)
   152     elif isinstance(xs, type(u'')):
   152     elif isinstance(xs, type(u'')):
   153         raise error.ProgrammingError(
   153         raise error.ProgrammingError(
   154             b"unicode %r found in %s" % (xs, name),
   154             b"unicode %r found in %s" % (xs, name),
   155             hint="use b'' to make it byte string",
   155             hint=b"use b'' to make it byte string",
   156         )
   156         )
   157 
   157 
   158 
   158 
   159 # attributes set by registrar.command
   159 # attributes set by registrar.command
   160 _cmdfuncattrs = ('norepo', 'optionalrepo', 'inferrepo')
   160 _cmdfuncattrs = (b'norepo', b'optionalrepo', b'inferrepo')
   161 
   161 
   162 
   162 
   163 def _validatecmdtable(ui, cmdtable):
   163 def _validatecmdtable(ui, cmdtable):
   164     """Check if extension commands have required attributes"""
   164     """Check if extension commands have required attributes"""
   165     for c, e in cmdtable.iteritems():
   165     for c, e in cmdtable.iteritems():
   166         f = e[0]
   166         f = e[0]
   167         missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)]
   167         missing = [a for a in _cmdfuncattrs if not util.safehasattr(f, a)]
   168         if not missing:
   168         if not missing:
   169             continue
   169             continue
   170         raise error.ProgrammingError(
   170         raise error.ProgrammingError(
   171             'missing attributes: %s' % ', '.join(missing),
   171             b'missing attributes: %s' % b', '.join(missing),
   172             hint="use @command decorator to register '%s'" % c,
   172             hint=b"use @command decorator to register '%s'" % c,
   173         )
   173         )
   174 
   174 
   175 
   175 
   176 def _validatetables(ui, mod):
   176 def _validatetables(ui, mod):
   177     """Sanity check for loadable tables provided by extension module"""
   177     """Sanity check for loadable tables provided by extension module"""
   178     for t in ['cmdtable', 'colortable', 'configtable']:
   178     for t in [b'cmdtable', b'colortable', b'configtable']:
   179         _rejectunicode(t, getattr(mod, t, {}))
   179         _rejectunicode(t, getattr(mod, t, {}))
   180     for t in [
   180     for t in [
   181         'filesetpredicate',
   181         b'filesetpredicate',
   182         'internalmerge',
   182         b'internalmerge',
   183         'revsetpredicate',
   183         b'revsetpredicate',
   184         'templatefilter',
   184         b'templatefilter',
   185         'templatefunc',
   185         b'templatefunc',
   186         'templatekeyword',
   186         b'templatekeyword',
   187     ]:
   187     ]:
   188         o = getattr(mod, t, None)
   188         o = getattr(mod, t, None)
   189         if o:
   189         if o:
   190             _rejectunicode(t, o._table)
   190             _rejectunicode(t, o._table)
   191     _validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
   191     _validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
   192 
   192 
   193 
   193 
   194 def load(ui, name, path, loadingtime=None):
   194 def load(ui, name, path, loadingtime=None):
   195     if name.startswith('hgext.') or name.startswith('hgext/'):
   195     if name.startswith(b'hgext.') or name.startswith(b'hgext/'):
   196         shortname = name[6:]
   196         shortname = name[6:]
   197     else:
   197     else:
   198         shortname = name
   198         shortname = name
   199     if shortname in _builtin:
   199     if shortname in _builtin:
   200         return None
   200         return None
   201     if shortname in _extensions:
   201     if shortname in _extensions:
   202         return _extensions[shortname]
   202         return _extensions[shortname]
   203     ui.log(b'extension', b'  - loading extension: %s\n', shortname)
   203     ui.log(b'extension', b'  - loading extension: %s\n', shortname)
   204     _extensions[shortname] = None
   204     _extensions[shortname] = None
   205     with util.timedcm('load extension %s', shortname) as stats:
   205     with util.timedcm(b'load extension %s', shortname) as stats:
   206         mod = _importext(name, path, bind(_reportimporterror, ui))
   206         mod = _importext(name, path, bind(_reportimporterror, ui))
   207     ui.log(b'extension', b'  > %s extension loaded in %s\n', shortname, stats)
   207     ui.log(b'extension', b'  > %s extension loaded in %s\n', shortname, stats)
   208     if loadingtime is not None:
   208     if loadingtime is not None:
   209         loadingtime[shortname] += stats.elapsed
   209         loadingtime[shortname] += stats.elapsed
   210 
   210 
   213     # extensions short circuit when loaded with a known incompatible version
   213     # extensions short circuit when loaded with a known incompatible version
   214     # of Mercurial.
   214     # of Mercurial.
   215     minver = getattr(mod, 'minimumhgversion', None)
   215     minver = getattr(mod, 'minimumhgversion', None)
   216     if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2):
   216     if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2):
   217         msg = _(
   217         msg = _(
   218             '(third party extension %s requires version %s or newer '
   218             b'(third party extension %s requires version %s or newer '
   219             'of Mercurial (current: %s); disabling)\n'
   219             b'of Mercurial (current: %s); disabling)\n'
   220         )
   220         )
   221         ui.warn(msg % (shortname, minver, util.version()))
   221         ui.warn(msg % (shortname, minver, util.version()))
   222         return
   222         return
   223     ui.log(b'extension', b'    - validating extension tables: %s\n', shortname)
   223     ui.log(b'extension', b'    - validating extension tables: %s\n', shortname)
   224     _validatetables(ui, mod)
   224     _validatetables(ui, mod)
   226     _extensions[shortname] = mod
   226     _extensions[shortname] = mod
   227     _order.append(shortname)
   227     _order.append(shortname)
   228     ui.log(
   228     ui.log(
   229         b'extension', b'    - invoking registered callbacks: %s\n', shortname
   229         b'extension', b'    - invoking registered callbacks: %s\n', shortname
   230     )
   230     )
   231     with util.timedcm('callbacks extension %s', shortname) as stats:
   231     with util.timedcm(b'callbacks extension %s', shortname) as stats:
   232         for fn in _aftercallbacks.get(shortname, []):
   232         for fn in _aftercallbacks.get(shortname, []):
   233             fn(loaded=True)
   233             fn(loaded=True)
   234     ui.log(b'extension', b'    > callbacks completed in %s\n', stats)
   234     ui.log(b'extension', b'    > callbacks completed in %s\n', stats)
   235     return mod
   235     return mod
   236 
   236 
   241         try:
   241         try:
   242             uisetup(ui)
   242             uisetup(ui)
   243         except Exception as inst:
   243         except Exception as inst:
   244             ui.traceback(force=True)
   244             ui.traceback(force=True)
   245             msg = stringutil.forcebytestr(inst)
   245             msg = stringutil.forcebytestr(inst)
   246             ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
   246             ui.warn(_(b"*** failed to set up extension %s: %s\n") % (name, msg))
   247             return False
   247             return False
   248     return True
   248     return True
   249 
   249 
   250 
   250 
   251 def _runextsetup(name, ui):
   251 def _runextsetup(name, ui):
   254         try:
   254         try:
   255             extsetup(ui)
   255             extsetup(ui)
   256         except Exception as inst:
   256         except Exception as inst:
   257             ui.traceback(force=True)
   257             ui.traceback(force=True)
   258             msg = stringutil.forcebytestr(inst)
   258             msg = stringutil.forcebytestr(inst)
   259             ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
   259             ui.warn(_(b"*** failed to set up extension %s: %s\n") % (name, msg))
   260             return False
   260             return False
   261     return True
   261     return True
   262 
   262 
   263 
   263 
   264 def loadall(ui, whitelist=None):
   264 def loadall(ui, whitelist=None):
   265     loadingtime = collections.defaultdict(int)
   265     loadingtime = collections.defaultdict(int)
   266     result = ui.configitems("extensions")
   266     result = ui.configitems(b"extensions")
   267     if whitelist is not None:
   267     if whitelist is not None:
   268         result = [(k, v) for (k, v) in result if k in whitelist]
   268         result = [(k, v) for (k, v) in result if k in whitelist]
   269     newindex = len(_order)
   269     newindex = len(_order)
   270     ui.log(
   270     ui.log(
   271         b'extension',
   271         b'extension',
   272         b'loading %sextensions\n',
   272         b'loading %sextensions\n',
   273         'additional ' if newindex else '',
   273         b'additional ' if newindex else b'',
   274     )
   274     )
   275     ui.log(b'extension', b'- processing %d entries\n', len(result))
   275     ui.log(b'extension', b'- processing %d entries\n', len(result))
   276     with util.timedcm('load all extensions') as stats:
   276     with util.timedcm(b'load all extensions') as stats:
   277         for (name, path) in result:
   277         for (name, path) in result:
   278             if path:
   278             if path:
   279                 if path[0:1] == '!':
   279                 if path[0:1] == b'!':
   280                     if name not in _disabledextensions:
   280                     if name not in _disabledextensions:
   281                         ui.log(
   281                         ui.log(
   282                             b'extension',
   282                             b'extension',
   283                             b'  - skipping disabled extension: %s\n',
   283                             b'  - skipping disabled extension: %s\n',
   284                             name,
   284                             name,
   289                 load(ui, name, path, loadingtime)
   289                 load(ui, name, path, loadingtime)
   290             except Exception as inst:
   290             except Exception as inst:
   291                 msg = stringutil.forcebytestr(inst)
   291                 msg = stringutil.forcebytestr(inst)
   292                 if path:
   292                 if path:
   293                     ui.warn(
   293                     ui.warn(
   294                         _("*** failed to import extension %s from %s: %s\n")
   294                         _(b"*** failed to import extension %s from %s: %s\n")
   295                         % (name, path, msg)
   295                         % (name, path, msg)
   296                     )
   296                     )
   297                 else:
   297                 else:
   298                     ui.warn(
   298                     ui.warn(
   299                         _("*** failed to import extension %s: %s\n")
   299                         _(b"*** failed to import extension %s: %s\n")
   300                         % (name, msg)
   300                         % (name, msg)
   301                     )
   301                     )
   302                 if isinstance(inst, error.Hint) and inst.hint:
   302                 if isinstance(inst, error.Hint) and inst.hint:
   303                     ui.warn(_("*** (%s)\n") % inst.hint)
   303                     ui.warn(_(b"*** (%s)\n") % inst.hint)
   304                 ui.traceback()
   304                 ui.traceback()
   305 
   305 
   306     ui.log(
   306     ui.log(
   307         b'extension',
   307         b'extension',
   308         b'> loaded %d extensions, total time %s\n',
   308         b'> loaded %d extensions, total time %s\n',
   316     # - loadername is the name of the function,
   316     # - loadername is the name of the function,
   317     #   which takes (ui, extensionname, extraobj) arguments
   317     #   which takes (ui, extensionname, extraobj) arguments
   318     #
   318     #
   319     # This one is for the list of item that must be run before running any setup
   319     # This one is for the list of item that must be run before running any setup
   320     earlyextraloaders = [
   320     earlyextraloaders = [
   321         ('configtable', configitems, 'loadconfigtable'),
   321         (b'configtable', configitems, b'loadconfigtable'),
   322     ]
   322     ]
   323 
   323 
   324     ui.log(b'extension', b'- loading configtable attributes\n')
   324     ui.log(b'extension', b'- loading configtable attributes\n')
   325     _loadextra(ui, newindex, earlyextraloaders)
   325     _loadextra(ui, newindex, earlyextraloaders)
   326 
   326 
   327     broken = set()
   327     broken = set()
   328     ui.log(b'extension', b'- executing uisetup hooks\n')
   328     ui.log(b'extension', b'- executing uisetup hooks\n')
   329     with util.timedcm('all uisetup') as alluisetupstats:
   329     with util.timedcm(b'all uisetup') as alluisetupstats:
   330         for name in _order[newindex:]:
   330         for name in _order[newindex:]:
   331             ui.log(b'extension', b'  - running uisetup for %s\n', name)
   331             ui.log(b'extension', b'  - running uisetup for %s\n', name)
   332             with util.timedcm('uisetup %s', name) as stats:
   332             with util.timedcm(b'uisetup %s', name) as stats:
   333                 if not _runuisetup(name, ui):
   333                 if not _runuisetup(name, ui):
   334                     ui.log(
   334                     ui.log(
   335                         b'extension',
   335                         b'extension',
   336                         b'    - the %s extension uisetup failed\n',
   336                         b'    - the %s extension uisetup failed\n',
   337                         name,
   337                         name,
   340             ui.log(b'extension', b'  > uisetup for %s took %s\n', name, stats)
   340             ui.log(b'extension', b'  > uisetup for %s took %s\n', name, stats)
   341             loadingtime[name] += stats.elapsed
   341             loadingtime[name] += stats.elapsed
   342     ui.log(b'extension', b'> all uisetup took %s\n', alluisetupstats)
   342     ui.log(b'extension', b'> all uisetup took %s\n', alluisetupstats)
   343 
   343 
   344     ui.log(b'extension', b'- executing extsetup hooks\n')
   344     ui.log(b'extension', b'- executing extsetup hooks\n')
   345     with util.timedcm('all extsetup') as allextetupstats:
   345     with util.timedcm(b'all extsetup') as allextetupstats:
   346         for name in _order[newindex:]:
   346         for name in _order[newindex:]:
   347             if name in broken:
   347             if name in broken:
   348                 continue
   348                 continue
   349             ui.log(b'extension', b'  - running extsetup for %s\n', name)
   349             ui.log(b'extension', b'  - running extsetup for %s\n', name)
   350             with util.timedcm('extsetup %s', name) as stats:
   350             with util.timedcm(b'extsetup %s', name) as stats:
   351                 if not _runextsetup(name, ui):
   351                 if not _runextsetup(name, ui):
   352                     ui.log(
   352                     ui.log(
   353                         b'extension',
   353                         b'extension',
   354                         b'    - the %s extension extsetup failed\n',
   354                         b'    - the %s extension extsetup failed\n',
   355                         name,
   355                         name,
   363         ui.log(b'extension', b'    - disabling broken %s extension\n', name)
   363         ui.log(b'extension', b'    - disabling broken %s extension\n', name)
   364         _extensions[name] = None
   364         _extensions[name] = None
   365 
   365 
   366     # Call aftercallbacks that were never met.
   366     # Call aftercallbacks that were never met.
   367     ui.log(b'extension', b'- executing remaining aftercallbacks\n')
   367     ui.log(b'extension', b'- executing remaining aftercallbacks\n')
   368     with util.timedcm('aftercallbacks') as stats:
   368     with util.timedcm(b'aftercallbacks') as stats:
   369         for shortname in _aftercallbacks:
   369         for shortname in _aftercallbacks:
   370             if shortname in _extensions:
   370             if shortname in _extensions:
   371                 continue
   371                 continue
   372 
   372 
   373             for fn in _aftercallbacks[shortname]:
   373             for fn in _aftercallbacks[shortname]:
   401     # - loadermod is the module where loader is placed
   401     # - loadermod is the module where loader is placed
   402     # - loadername is the name of the function,
   402     # - loadername is the name of the function,
   403     #   which takes (ui, extensionname, extraobj) arguments
   403     #   which takes (ui, extensionname, extraobj) arguments
   404     ui.log(b'extension', b'- loading extension registration objects\n')
   404     ui.log(b'extension', b'- loading extension registration objects\n')
   405     extraloaders = [
   405     extraloaders = [
   406         ('cmdtable', commands, 'loadcmdtable'),
   406         (b'cmdtable', commands, b'loadcmdtable'),
   407         ('colortable', color, 'loadcolortable'),
   407         (b'colortable', color, b'loadcolortable'),
   408         ('filesetpredicate', fileset, 'loadpredicate'),
   408         (b'filesetpredicate', fileset, b'loadpredicate'),
   409         ('internalmerge', filemerge, 'loadinternalmerge'),
   409         (b'internalmerge', filemerge, b'loadinternalmerge'),
   410         ('revsetpredicate', revset, 'loadpredicate'),
   410         (b'revsetpredicate', revset, b'loadpredicate'),
   411         ('templatefilter', templatefilters, 'loadfilter'),
   411         (b'templatefilter', templatefilters, b'loadfilter'),
   412         ('templatefunc', templatefuncs, 'loadfunction'),
   412         (b'templatefunc', templatefuncs, b'loadfunction'),
   413         ('templatekeyword', templatekw, 'loadkeyword'),
   413         (b'templatekeyword', templatekw, b'loadkeyword'),
   414     ]
   414     ]
   415     with util.timedcm('load registration objects') as stats:
   415     with util.timedcm(b'load registration objects') as stats:
   416         _loadextra(ui, newindex, extraloaders)
   416         _loadextra(ui, newindex, extraloaders)
   417     ui.log(
   417     ui.log(
   418         b'extension',
   418         b'extension',
   419         b'> extension registration object loading took %s\n',
   419         b'> extension registration object loading took %s\n',
   420         stats,
   420         stats,
   480         try:
   480         try:
   481             hook(ui)
   481             hook(ui)
   482         except Exception as inst:
   482         except Exception as inst:
   483             ui.traceback(force=True)
   483             ui.traceback(force=True)
   484             ui.warn(
   484             ui.warn(
   485                 _('*** failed to populate ui by extension %s: %s\n')
   485                 _(b'*** failed to populate ui by extension %s: %s\n')
   486                 % (name, stringutil.forcebytestr(inst))
   486                 % (name, stringutil.forcebytestr(inst))
   487             )
   487             )
   488 
   488 
   489 
   489 
   490 def bind(func, *args):
   490 def bind(func, *args):
   707     except OSError:
   707     except OSError:
   708         return {}
   708         return {}
   709 
   709 
   710     exts = {}
   710     exts = {}
   711     for e in files:
   711     for e in files:
   712         if e.endswith('.py'):
   712         if e.endswith(b'.py'):
   713             name = e.rsplit('.', 1)[0]
   713             name = e.rsplit(b'.', 1)[0]
   714             path = os.path.join(extpath, e)
   714             path = os.path.join(extpath, e)
   715         else:
   715         else:
   716             name = e
   716             name = e
   717             path = os.path.join(extpath, e, '__init__.py')
   717             path = os.path.join(extpath, e, b'__init__.py')
   718             if not os.path.exists(path):
   718             if not os.path.exists(path):
   719                 continue
   719                 continue
   720         if name in exts or name in _order or name == '__init__':
   720         if name in exts or name in _order or name == b'__init__':
   721             continue
   721             continue
   722         exts[name] = path
   722         exts[name] = path
   723     for name, path in _disabledextensions.iteritems():
   723     for name, path in _disabledextensions.iteritems():
   724         # If no path was provided for a disabled extension (e.g. "color=!"),
   724         # If no path was provided for a disabled extension (e.g. "color=!"),
   725         # don't replace the path we already found by the scan above.
   725         # don't replace the path we already found by the scan above.
   735     handle triple quotes and to return the whole text instead of just
   735     handle triple quotes and to return the whole text instead of just
   736     the synopsis'''
   736     the synopsis'''
   737     result = []
   737     result = []
   738 
   738 
   739     line = file.readline()
   739     line = file.readline()
   740     while line[:1] == '#' or not line.strip():
   740     while line[:1] == b'#' or not line.strip():
   741         line = file.readline()
   741         line = file.readline()
   742         if not line:
   742         if not line:
   743             break
   743             break
   744 
   744 
   745     start = line[:3]
   745     start = line[:3]
   746     if start == '"""' or start == "'''":
   746     if start == b'"""' or start == b"'''":
   747         line = line[3:]
   747         line = line[3:]
   748         while line:
   748         while line:
   749             if line.rstrip().endswith(start):
   749             if line.rstrip().endswith(start):
   750                 line = line.split(start)[0]
   750                 line = line.split(start)[0]
   751                 if line:
   751                 if line:
   756             result.append(line)
   756             result.append(line)
   757             line = file.readline()
   757             line = file.readline()
   758     else:
   758     else:
   759         return None
   759         return None
   760 
   760 
   761     return ''.join(result)
   761     return b''.join(result)
   762 
   762 
   763 
   763 
   764 def _disabledhelp(path):
   764 def _disabledhelp(path):
   765     '''retrieve help synopsis of a disabled extension (without importing)'''
   765     '''retrieve help synopsis of a disabled extension (without importing)'''
   766     try:
   766     try:
   767         with open(path, 'rb') as src:
   767         with open(path, b'rb') as src:
   768             doc = _moduledoc(src)
   768             doc = _moduledoc(src)
   769     except IOError:
   769     except IOError:
   770         return
   770         return
   771 
   771 
   772     if doc:  # extracting localized synopsis
   772     if doc:  # extracting localized synopsis
   773         return gettext(doc)
   773         return gettext(doc)
   774     else:
   774     else:
   775         return _('(no help text available)')
   775         return _(b'(no help text available)')
   776 
   776 
   777 
   777 
   778 def disabled():
   778 def disabled():
   779     '''find disabled extensions from hgext. returns a dict of {name: desc}'''
   779     '''find disabled extensions from hgext. returns a dict of {name: desc}'''
   780     try:
   780     try:
   839 def _disabledcmdtable(path):
   839 def _disabledcmdtable(path):
   840     """Construct a dummy command table without loading the extension module
   840     """Construct a dummy command table without loading the extension module
   841 
   841 
   842     This may raise IOError or SyntaxError.
   842     This may raise IOError or SyntaxError.
   843     """
   843     """
   844     with open(path, 'rb') as src:
   844     with open(path, b'rb') as src:
   845         root = ast.parse(src.read(), path)
   845         root = ast.parse(src.read(), path)
   846     cmdtable = {}
   846     cmdtable = {}
   847     for node in _walkcommand(root):
   847     for node in _walkcommand(root):
   848         if not node.args:
   848         if not node.args:
   849             continue
   849             continue
   904 
   904 
   905 def enabled(shortname=True):
   905 def enabled(shortname=True):
   906     '''return a dict of {name: desc} of extensions'''
   906     '''return a dict of {name: desc} of extensions'''
   907     exts = {}
   907     exts = {}
   908     for ename, ext in extensions():
   908     for ename, ext in extensions():
   909         doc = gettext(ext.__doc__) or _('(no help text available)')
   909         doc = gettext(ext.__doc__) or _(b'(no help text available)')
   910         if shortname:
   910         if shortname:
   911             ename = ename.split('.')[-1]
   911             ename = ename.split(b'.')[-1]
   912         exts[ename] = doc.splitlines()[0].strip()
   912         exts[ename] = doc.splitlines()[0].strip()
   913 
   913 
   914     return exts
   914     return exts
   915 
   915 
   916 
   916 
   919     return [name for name, mod in _extensions.iteritems() if mod is None]
   919     return [name for name, mod in _extensions.iteritems() if mod is None]
   920 
   920 
   921 
   921 
   922 def moduleversion(module):
   922 def moduleversion(module):
   923     '''return version information from given module as a string'''
   923     '''return version information from given module as a string'''
   924     if util.safehasattr(module, 'getversion') and callable(module.getversion):
   924     if util.safehasattr(module, b'getversion') and callable(module.getversion):
   925         version = module.getversion()
   925         version = module.getversion()
   926     elif util.safehasattr(module, '__version__'):
   926     elif util.safehasattr(module, b'__version__'):
   927         version = module.__version__
   927         version = module.__version__
   928     else:
   928     else:
   929         version = ''
   929         version = b''
   930     if isinstance(version, (list, tuple)):
   930     if isinstance(version, (list, tuple)):
   931         version = '.'.join(pycompat.bytestr(o) for o in version)
   931         version = b'.'.join(pycompat.bytestr(o) for o in version)
   932     return version
   932     return version
   933 
   933 
   934 
   934 
   935 def ismoduleinternal(module):
   935 def ismoduleinternal(module):
   936     exttestedwith = getattr(module, 'testedwith', None)
   936     exttestedwith = getattr(module, 'testedwith', None)
   937     return exttestedwith == "ships-with-hg-core"
   937     return exttestedwith == b"ships-with-hg-core"