--- a/mercurial/extensions.py Sat Oct 05 10:29:34 2019 -0400
+++ b/mercurial/extensions.py Sun Oct 06 09:45:02 2019 -0400
@@ -27,9 +27,7 @@
util,
)
-from .utils import (
- stringutil,
-)
+from .utils import stringutil
_extensions = {}
_disabledextensions = {}
@@ -47,13 +45,16 @@
'shelve',
}
+
def extensions(ui=None):
if ui:
+
def enabled(name):
for format in ['%s', 'hgext.%s']:
conf = ui.config('extensions', format % name)
if conf is not None and not conf.startswith('!'):
return True
+
else:
enabled = lambda name: True
for name in _order:
@@ -61,6 +62,7 @@
if module and enabled(name):
yield name, module
+
def find(name):
'''return module with given extension name'''
mod = None
@@ -75,6 +77,7 @@
raise KeyError(name)
return mod
+
def loadpath(path, module_name):
module_name = module_name.replace('.', '_')
path = util.normpath(util.expandpath(path))
@@ -90,9 +93,10 @@
return imp.load_source(module_name, path)
except IOError as exc:
if not exc.filename:
- exc.filename = path # python does not fill this
+ exc.filename = path # python does not fill this
raise
+
def _importh(name):
"""import and return the <name> module"""
mod = __import__(pycompat.sysstr(name))
@@ -101,6 +105,7 @@
mod = getattr(mod, comp)
return mod
+
def _importext(name, path=None, reportfunc=None):
if path:
# the module will be loaded in sys.modules
@@ -121,14 +126,21 @@
mod = _importh(name)
return mod
+
def _reportimporterror(ui, err, failed, next):
# note: this ui.log happens before --debug is processed,
# Use --config ui.debug=1 to see them.
- ui.log(b'extension', b' - could not import %s (%s): trying %s\n',
- failed, stringutil.forcebytestr(err), next)
+ ui.log(
+ b'extension',
+ b' - could not import %s (%s): trying %s\n',
+ failed,
+ stringutil.forcebytestr(err),
+ next,
+ )
if ui.debugflag and ui.configbool('devel', 'debug.extensions'):
ui.traceback()
+
def _rejectunicode(name, xs):
if isinstance(xs, (list, set, tuple)):
for x in xs:
@@ -138,12 +150,16 @@
_rejectunicode(name, k)
_rejectunicode(b'%s.%s' % (name, stringutil.forcebytestr(k)), v)
elif isinstance(xs, type(u'')):
- raise error.ProgrammingError(b"unicode %r found in %s" % (xs, name),
- hint="use b'' to make it byte string")
+ raise error.ProgrammingError(
+ b"unicode %r found in %s" % (xs, name),
+ hint="use b'' to make it byte string",
+ )
+
# attributes set by registrar.command
_cmdfuncattrs = ('norepo', 'optionalrepo', 'inferrepo')
+
def _validatecmdtable(ui, cmdtable):
"""Check if extension commands have required attributes"""
for c, e in cmdtable.iteritems():
@@ -153,19 +169,28 @@
continue
raise error.ProgrammingError(
'missing attributes: %s' % ', '.join(missing),
- hint="use @command decorator to register '%s'" % c)
+ hint="use @command decorator to register '%s'" % c,
+ )
+
def _validatetables(ui, mod):
"""Sanity check for loadable tables provided by extension module"""
for t in ['cmdtable', 'colortable', 'configtable']:
_rejectunicode(t, getattr(mod, t, {}))
- for t in ['filesetpredicate', 'internalmerge', 'revsetpredicate',
- 'templatefilter', 'templatefunc', 'templatekeyword']:
+ for t in [
+ 'filesetpredicate',
+ 'internalmerge',
+ 'revsetpredicate',
+ 'templatefilter',
+ 'templatefunc',
+ 'templatekeyword',
+ ]:
o = getattr(mod, t, None)
if o:
_rejectunicode(t, o._table)
_validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
+
def load(ui, name, path, loadingtime=None):
if name.startswith('hgext.') or name.startswith('hgext/'):
shortname = name[6:]
@@ -189,8 +214,10 @@
# of Mercurial.
minver = getattr(mod, 'minimumhgversion', None)
if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2):
- msg = _('(third party extension %s requires version %s or newer '
- 'of Mercurial (current: %s); disabling)\n')
+ msg = _(
+ '(third party extension %s requires version %s or newer '
+ 'of Mercurial (current: %s); disabling)\n'
+ )
ui.warn(msg % (shortname, minver, util.version()))
return
ui.log(b'extension', b' - validating extension tables: %s\n', shortname)
@@ -198,14 +225,16 @@
_extensions[shortname] = mod
_order.append(shortname)
- ui.log(b'extension', b' - invoking registered callbacks: %s\n',
- shortname)
+ ui.log(
+ b'extension', b' - invoking registered callbacks: %s\n', shortname
+ )
with util.timedcm('callbacks extension %s', shortname) as stats:
for fn in _aftercallbacks.get(shortname, []):
fn(loaded=True)
ui.log(b'extension', b' > callbacks completed in %s\n', stats)
return mod
+
def _runuisetup(name, ui):
uisetup = getattr(_extensions[name], 'uisetup', None)
if uisetup:
@@ -218,6 +247,7 @@
return False
return True
+
def _runextsetup(name, ui):
extsetup = getattr(_extensions[name], 'extsetup', None)
if extsetup:
@@ -230,22 +260,29 @@
return False
return True
+
def loadall(ui, whitelist=None):
loadingtime = collections.defaultdict(int)
result = ui.configitems("extensions")
if whitelist is not None:
result = [(k, v) for (k, v) in result if k in whitelist]
newindex = len(_order)
- ui.log(b'extension', b'loading %sextensions\n',
- 'additional ' if newindex else '')
+ ui.log(
+ b'extension',
+ b'loading %sextensions\n',
+ 'additional ' if newindex else '',
+ )
ui.log(b'extension', b'- processing %d entries\n', len(result))
with util.timedcm('load all extensions') as stats:
for (name, path) in result:
if path:
if path[0:1] == '!':
if name not in _disabledextensions:
- ui.log(b'extension',
- b' - skipping disabled extension: %s\n', name)
+ ui.log(
+ b'extension',
+ b' - skipping disabled extension: %s\n',
+ name,
+ )
_disabledextensions[name] = path[1:]
continue
try:
@@ -253,17 +290,25 @@
except Exception as inst:
msg = stringutil.forcebytestr(inst)
if path:
- ui.warn(_("*** failed to import extension %s from %s: %s\n")
- % (name, path, msg))
+ ui.warn(
+ _("*** failed to import extension %s from %s: %s\n")
+ % (name, path, msg)
+ )
else:
- ui.warn(_("*** failed to import extension %s: %s\n")
- % (name, msg))
+ ui.warn(
+ _("*** failed to import extension %s: %s\n")
+ % (name, msg)
+ )
if isinstance(inst, error.Hint) and inst.hint:
ui.warn(_("*** (%s)\n") % inst.hint)
ui.traceback()
- ui.log(b'extension', b'> loaded %d extensions, total time %s\n',
- len(_order) - newindex, stats)
+ ui.log(
+ b'extension',
+ b'> loaded %d extensions, total time %s\n',
+ len(_order) - newindex,
+ stats,
+ )
# list of (objname, loadermod, loadername) tuple:
# - objname is the name of an object in extension module,
# from which extra information is loaded
@@ -286,8 +331,11 @@
ui.log(b'extension', b' - running uisetup for %s\n', name)
with util.timedcm('uisetup %s', name) as stats:
if not _runuisetup(name, ui):
- ui.log(b'extension',
- b' - the %s extension uisetup failed\n', name)
+ ui.log(
+ b'extension',
+ b' - the %s extension uisetup failed\n',
+ name,
+ )
broken.add(name)
ui.log(b'extension', b' > uisetup for %s took %s\n', name, stats)
loadingtime[name] += stats.elapsed
@@ -301,8 +349,11 @@
ui.log(b'extension', b' - running extsetup for %s\n', name)
with util.timedcm('extsetup %s', name) as stats:
if not _runextsetup(name, ui):
- ui.log(b'extension',
- b' - the %s extension extsetup failed\n', name)
+ ui.log(
+ b'extension',
+ b' - the %s extension extsetup failed\n',
+ name,
+ )
broken.add(name)
ui.log(b'extension', b' > extsetup for %s took %s\n', name, stats)
loadingtime[name] += stats.elapsed
@@ -320,9 +371,11 @@
continue
for fn in _aftercallbacks[shortname]:
- ui.log(b'extension',
- b' - extension %s not loaded, notify callbacks\n',
- shortname)
+ ui.log(
+ b'extension',
+ b' - extension %s not loaded, notify callbacks\n',
+ shortname,
+ )
fn(loaded=False)
ui.log(b'extension', b'> remaining aftercallbacks completed in %s\n', stats)
@@ -361,27 +414,36 @@
]
with util.timedcm('load registration objects') as stats:
_loadextra(ui, newindex, extraloaders)
- ui.log(b'extension', b'> extension registration object loading took %s\n',
- stats)
+ ui.log(
+ b'extension',
+ b'> extension registration object loading took %s\n',
+ stats,
+ )
# Report per extension loading time (except reposetup)
for name in sorted(loadingtime):
- ui.log(b'extension', b'> extension %s take a total of %s to load\n',
- name, util.timecount(loadingtime[name]))
+ ui.log(
+ b'extension',
+ b'> extension %s take a total of %s to load\n',
+ name,
+ util.timecount(loadingtime[name]),
+ )
ui.log(b'extension', b'extension loading complete\n')
+
def _loadextra(ui, newindex, extraloaders):
for name in _order[newindex:]:
module = _extensions[name]
if not module:
- continue # loading this module failed
+ continue # loading this module failed
for objname, loadermod, loadername in extraloaders:
extraobj = getattr(module, objname, None)
if extraobj is not None:
getattr(loadermod, loadername)(ui, name, extraobj)
+
def afterloaded(extension, callback):
'''Run the specified function after a named extension is loaded.
@@ -397,11 +459,12 @@
if extension in _extensions:
# Report loaded as False if the extension is disabled
- loaded = (_extensions[extension] is not None)
+ loaded = _extensions[extension] is not None
callback(loaded=loaded)
else:
_aftercallbacks.setdefault(extension, []).append(callback)
+
def populateui(ui):
"""Run extension hooks on the given ui to populate additional members,
extend the class dynamically, etc.
@@ -418,8 +481,11 @@
hook(ui)
except Exception as inst:
ui.traceback(force=True)
- ui.warn(_('*** failed to populate ui by extension %s: %s\n')
- % (name, stringutil.forcebytestr(inst)))
+ ui.warn(
+ _('*** failed to populate ui by extension %s: %s\n')
+ % (name, stringutil.forcebytestr(inst))
+ )
+
def bind(func, *args):
'''Partial function application
@@ -429,10 +495,13 @@
f(1, 2, bar=3) === bind(f, 1)(2, bar=3)'''
assert callable(func)
+
def closure(*a, **kw):
return func(*(args + a), **kw)
+
return closure
+
def _updatewrapper(wrap, origfn, unboundwrapper):
'''Copy and add some useful attributes to wrapper'''
try:
@@ -445,6 +514,7 @@
wrap._origfunc = origfn
wrap._unboundwrapper = unboundwrapper
+
def wrapcommand(table, command, wrapper, synopsis=None, docstring=None):
'''Wrap the command named `command' in table
@@ -482,8 +552,9 @@
break
origfn = entry[0]
- wrap = functools.partial(util.checksignature(wrapper),
- util.checksignature(origfn))
+ wrap = functools.partial(
+ util.checksignature(wrapper), util.checksignature(origfn)
+ )
_updatewrapper(wrap, origfn, wrapper)
if docstring is not None:
wrap.__doc__ += docstring
@@ -495,6 +566,7 @@
table[key] = tuple(newentry)
return entry
+
def wrapfilecache(cls, propname, wrapper):
"""Wraps a filecache property.
@@ -506,14 +578,18 @@
if propname in currcls.__dict__:
origfn = currcls.__dict__[propname].func
assert callable(origfn)
+
def wrap(*args, **kwargs):
return wrapper(origfn, *args, **kwargs)
+
currcls.__dict__[propname].func = wrap
break
if currcls is object:
- raise AttributeError(r"type '%s' has no property '%s'" % (
- cls, propname))
+ raise AttributeError(
+ r"type '%s' has no property '%s'" % (cls, propname)
+ )
+
class wrappedfunction(object):
'''context manager for temporarily wrapping a function'''
@@ -530,6 +606,7 @@
def __exit__(self, exctype, excvalue, traceback):
unwrapfunction(self._container, self._funcname, self._wrapper)
+
def wrapfunction(container, funcname, wrapper):
'''Wrap the function named funcname in container
@@ -579,6 +656,7 @@
setattr(container, funcname, wrap)
return origfn
+
def unwrapfunction(container, funcname, wrapper=None):
'''undo wrapfunction
@@ -599,6 +677,7 @@
wrapfunction(container, funcname, w)
return wrapper
+
def getwrapperchain(container, funcname):
'''get a chain of wrappers of a function
@@ -615,12 +694,15 @@
fn = getattr(fn, '_origfunc', None)
return result
+
def _disabledpaths():
'''find paths of disabled extensions. returns a dict of {name: path}'''
import hgext
+
extpath = os.path.dirname(
- os.path.abspath(pycompat.fsencode(hgext.__file__)))
- try: # might not be a filesystem path
+ os.path.abspath(pycompat.fsencode(hgext.__file__))
+ )
+ try: # might not be a filesystem path
files = os.listdir(extpath)
except OSError:
return {}
@@ -645,6 +727,7 @@
exts[name] = path
return exts
+
def _moduledoc(file):
'''return the top-level python documentation for the given file
@@ -669,7 +752,7 @@
result.append(line)
break
elif not line:
- return None # unmatched delimiter
+ return None # unmatched delimiter
result.append(line)
line = file.readline()
else:
@@ -677,6 +760,7 @@
return ''.join(result)
+
def _disabledhelp(path):
'''retrieve help synopsis of a disabled extension (without importing)'''
try:
@@ -685,18 +769,22 @@
except IOError:
return
- if doc: # extracting localized synopsis
+ if doc: # extracting localized synopsis
return gettext(doc)
else:
return _('(no help text available)')
+
def disabled():
'''find disabled extensions from hgext. returns a dict of {name: desc}'''
try:
from hgext import __index__
- return dict((name, gettext(desc))
- for name, desc in __index__.docs.iteritems()
- if name not in _order)
+
+ return dict(
+ (name, gettext(desc))
+ for name, desc in __index__.docs.iteritems()
+ if name not in _order
+ )
except (ImportError, AttributeError):
pass
@@ -712,10 +800,12 @@
return exts
+
def disabledext(name):
'''find a specific disabled extension from hgext. returns desc'''
try:
from hgext import __index__
+
if name in _order: # enabled
return
else:
@@ -727,6 +817,7 @@
if name in paths:
return _disabledhelp(paths[name])
+
def _walkcommand(node):
"""Scan @command() decorators in the tree starting at node"""
todo = collections.deque([node])
@@ -744,6 +835,7 @@
continue
yield d
+
def _disabledcmdtable(path):
"""Construct a dummy command table without loading the extension module
@@ -765,6 +857,7 @@
cmdtable[name] = (None, [], b'')
return cmdtable
+
def _finddisabledcmd(ui, cmd, name, path, strict):
try:
cmdtable = _disabledcmdtable(path)
@@ -783,6 +876,7 @@
doc = _disabledhelp(path)
return (cmd, name, doc)
+
def disabledcmd(ui, cmd, strict=False):
'''find cmd from disabled extensions without importing.
returns (cmdname, extname, doc)'''
@@ -807,25 +901,27 @@
raise error.UnknownCommand(cmd)
+
def enabled(shortname=True):
'''return a dict of {name: desc} of extensions'''
exts = {}
for ename, ext in extensions():
- doc = (gettext(ext.__doc__) or _('(no help text available)'))
+ doc = gettext(ext.__doc__) or _('(no help text available)')
if shortname:
ename = ename.split('.')[-1]
exts[ename] = doc.splitlines()[0].strip()
return exts
+
def notloaded():
'''return short names of extensions that failed to load'''
return [name for name, mod in _extensions.iteritems() if mod is None]
+
def moduleversion(module):
'''return version information from given module as a string'''
- if (util.safehasattr(module, 'getversion')
- and callable(module.getversion)):
+ if util.safehasattr(module, 'getversion') and callable(module.getversion):
version = module.getversion()
elif util.safehasattr(module, '__version__'):
version = module.__version__
@@ -835,6 +931,7 @@
version = '.'.join(pycompat.bytestr(o) for o in version)
return version
+
def ismoduleinternal(module):
exttestedwith = getattr(module, 'testedwith', None)
return exttestedwith == "ships-with-hg-core"