eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/extensions.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # extensions.py - extension handling for mercurial
       
     2 #
       
     3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
       
     4 #
       
     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.
       
     7 
       
     8 import imp, os
       
     9 import util, cmdutil, help, error
       
    10 from i18n import _, gettext
       
    11 
       
    12 _extensions = {}
       
    13 _order = []
       
    14 
       
    15 def extensions():
       
    16     for name in _order:
       
    17         module = _extensions[name]
       
    18         if module:
       
    19             yield name, module
       
    20 
       
    21 def find(name):
       
    22     '''return module with given extension name'''
       
    23     try:
       
    24         return _extensions[name]
       
    25     except KeyError:
       
    26         for k, v in _extensions.iteritems():
       
    27             if k.endswith('.' + name) or k.endswith('/' + name):
       
    28                 return v
       
    29         raise KeyError(name)
       
    30 
       
    31 def loadpath(path, module_name):
       
    32     module_name = module_name.replace('.', '_')
       
    33     path = util.expandpath(path)
       
    34     if os.path.isdir(path):
       
    35         # module/__init__.py style
       
    36         d, f = os.path.split(path.rstrip('/'))
       
    37         fd, fpath, desc = imp.find_module(f, [d])
       
    38         return imp.load_module(module_name, fd, fpath, desc)
       
    39     else:
       
    40         return imp.load_source(module_name, path)
       
    41 
       
    42 def load(ui, name, path):
       
    43     # unused ui argument kept for backwards compatibility
       
    44     if name.startswith('hgext.') or name.startswith('hgext/'):
       
    45         shortname = name[6:]
       
    46     else:
       
    47         shortname = name
       
    48     if shortname in _extensions:
       
    49         return _extensions[shortname]
       
    50     _extensions[shortname] = None
       
    51     if path:
       
    52         # the module will be loaded in sys.modules
       
    53         # choose an unique name so that it doesn't
       
    54         # conflicts with other modules
       
    55         mod = loadpath(path, 'hgext.%s' % name)
       
    56     else:
       
    57         def importh(name):
       
    58             mod = __import__(name)
       
    59             components = name.split('.')
       
    60             for comp in components[1:]:
       
    61                 mod = getattr(mod, comp)
       
    62             return mod
       
    63         try:
       
    64             mod = importh("hgext.%s" % name)
       
    65         except ImportError:
       
    66             mod = importh(name)
       
    67     _extensions[shortname] = mod
       
    68     _order.append(shortname)
       
    69     return mod
       
    70 
       
    71 def loadall(ui):
       
    72     result = ui.configitems("extensions")
       
    73     newindex = len(_order)
       
    74     for (name, path) in result:
       
    75         if path:
       
    76             if path[0] == '!':
       
    77                 continue
       
    78         try:
       
    79             load(ui, name, path)
       
    80         except KeyboardInterrupt:
       
    81             raise
       
    82         except Exception, inst:
       
    83             if path:
       
    84                 ui.warn(_("*** failed to import extension %s from %s: %s\n")
       
    85                         % (name, path, inst))
       
    86             else:
       
    87                 ui.warn(_("*** failed to import extension %s: %s\n")
       
    88                         % (name, inst))
       
    89             if ui.traceback():
       
    90                 return 1
       
    91 
       
    92     for name in _order[newindex:]:
       
    93         uisetup = getattr(_extensions[name], 'uisetup', None)
       
    94         if uisetup:
       
    95             uisetup(ui)
       
    96 
       
    97     for name in _order[newindex:]:
       
    98         extsetup = getattr(_extensions[name], 'extsetup', None)
       
    99         if extsetup:
       
   100             try:
       
   101                 extsetup(ui)
       
   102             except TypeError:
       
   103                 if extsetup.func_code.co_argcount != 0:
       
   104                     raise
       
   105                 extsetup() # old extsetup with no ui argument
       
   106 
       
   107 def wrapcommand(table, command, wrapper):
       
   108     '''Wrap the command named `command' in table
       
   109 
       
   110     Replace command in the command table with wrapper. The wrapped command will
       
   111     be inserted into the command table specified by the table argument.
       
   112 
       
   113     The wrapper will be called like
       
   114 
       
   115       wrapper(orig, *args, **kwargs)
       
   116 
       
   117     where orig is the original (wrapped) function, and *args, **kwargs
       
   118     are the arguments passed to it.
       
   119     '''
       
   120     assert hasattr(wrapper, '__call__')
       
   121     aliases, entry = cmdutil.findcmd(command, table)
       
   122     for alias, e in table.iteritems():
       
   123         if e is entry:
       
   124             key = alias
       
   125             break
       
   126 
       
   127     origfn = entry[0]
       
   128     def wrap(*args, **kwargs):
       
   129         return util.checksignature(wrapper)(
       
   130             util.checksignature(origfn), *args, **kwargs)
       
   131 
       
   132     wrap.__doc__ = getattr(origfn, '__doc__')
       
   133     wrap.__module__ = getattr(origfn, '__module__')
       
   134 
       
   135     newentry = list(entry)
       
   136     newentry[0] = wrap
       
   137     table[key] = tuple(newentry)
       
   138     return entry
       
   139 
       
   140 def wrapfunction(container, funcname, wrapper):
       
   141     '''Wrap the function named funcname in container
       
   142 
       
   143     Replace the funcname member in the given container with the specified
       
   144     wrapper. The container is typically a module, class, or instance.
       
   145 
       
   146     The wrapper will be called like
       
   147 
       
   148       wrapper(orig, *args, **kwargs)
       
   149 
       
   150     where orig is the original (wrapped) function, and *args, **kwargs
       
   151     are the arguments passed to it.
       
   152 
       
   153     Wrapping methods of the repository object is not recommended since
       
   154     it conflicts with extensions that extend the repository by
       
   155     subclassing. All extensions that need to extend methods of
       
   156     localrepository should use this subclassing trick: namely,
       
   157     reposetup() should look like
       
   158 
       
   159       def reposetup(ui, repo):
       
   160           class myrepo(repo.__class__):
       
   161               def whatever(self, *args, **kwargs):
       
   162                   [...extension stuff...]
       
   163                   super(myrepo, self).whatever(*args, **kwargs)
       
   164                   [...extension stuff...]
       
   165 
       
   166           repo.__class__ = myrepo
       
   167 
       
   168     In general, combining wrapfunction() with subclassing does not
       
   169     work. Since you cannot control what other extensions are loaded by
       
   170     your end users, you should play nicely with others by using the
       
   171     subclass trick.
       
   172     '''
       
   173     assert hasattr(wrapper, '__call__')
       
   174     def wrap(*args, **kwargs):
       
   175         return wrapper(origfn, *args, **kwargs)
       
   176 
       
   177     origfn = getattr(container, funcname)
       
   178     assert hasattr(origfn, '__call__')
       
   179     setattr(container, funcname, wrap)
       
   180     return origfn
       
   181 
       
   182 def _disabledpaths(strip_init=False):
       
   183     '''find paths of disabled extensions. returns a dict of {name: path}
       
   184     removes /__init__.py from packages if strip_init is True'''
       
   185     import hgext
       
   186     extpath = os.path.dirname(os.path.abspath(hgext.__file__))
       
   187     try: # might not be a filesystem path
       
   188         files = os.listdir(extpath)
       
   189     except OSError:
       
   190         return {}
       
   191 
       
   192     exts = {}
       
   193     for e in files:
       
   194         if e.endswith('.py'):
       
   195             name = e.rsplit('.', 1)[0]
       
   196             path = os.path.join(extpath, e)
       
   197         else:
       
   198             name = e
       
   199             path = os.path.join(extpath, e, '__init__.py')
       
   200             if not os.path.exists(path):
       
   201                 continue
       
   202             if strip_init:
       
   203                 path = os.path.dirname(path)
       
   204         if name in exts or name in _order or name == '__init__':
       
   205             continue
       
   206         exts[name] = path
       
   207     return exts
       
   208 
       
   209 def _disabledhelp(path):
       
   210     '''retrieve help synopsis of a disabled extension (without importing)'''
       
   211     try:
       
   212         file = open(path)
       
   213     except IOError:
       
   214         return
       
   215     else:
       
   216         doc = help.moduledoc(file)
       
   217         file.close()
       
   218 
       
   219     if doc: # extracting localized synopsis
       
   220         return gettext(doc).splitlines()[0]
       
   221     else:
       
   222         return _('(no help text available)')
       
   223 
       
   224 def disabled():
       
   225     '''find disabled extensions from hgext
       
   226     returns a dict of {name: desc}, and the max name length'''
       
   227 
       
   228     paths = _disabledpaths()
       
   229     if not paths:
       
   230         return None, 0
       
   231 
       
   232     exts = {}
       
   233     maxlength = 0
       
   234     for name, path in paths.iteritems():
       
   235         doc = _disabledhelp(path)
       
   236         if not doc:
       
   237             continue
       
   238 
       
   239         exts[name] = doc
       
   240         if len(name) > maxlength:
       
   241             maxlength = len(name)
       
   242 
       
   243     return exts, maxlength
       
   244 
       
   245 def disabledext(name):
       
   246     '''find a specific disabled extension from hgext. returns desc'''
       
   247     paths = _disabledpaths()
       
   248     if name in paths:
       
   249         return _disabledhelp(paths[name])
       
   250 
       
   251 def disabledcmd(cmd, strict=False):
       
   252     '''import disabled extensions until cmd is found.
       
   253     returns (cmdname, extname, doc)'''
       
   254 
       
   255     paths = _disabledpaths(strip_init=True)
       
   256     if not paths:
       
   257         raise error.UnknownCommand(cmd)
       
   258 
       
   259     def findcmd(cmd, name, path):
       
   260         try:
       
   261             mod = loadpath(path, 'hgext.%s' % name)
       
   262         except Exception:
       
   263             return
       
   264         try:
       
   265             aliases, entry = cmdutil.findcmd(cmd,
       
   266                 getattr(mod, 'cmdtable', {}), strict)
       
   267         except (error.AmbiguousCommand, error.UnknownCommand):
       
   268             return
       
   269         for c in aliases:
       
   270             if c.startswith(cmd):
       
   271                 cmd = c
       
   272                 break
       
   273         else:
       
   274             cmd = aliases[0]
       
   275         return (cmd, name, mod)
       
   276 
       
   277     # first, search for an extension with the same name as the command
       
   278     path = paths.pop(cmd, None)
       
   279     if path:
       
   280         ext = findcmd(cmd, cmd, path)
       
   281         if ext:
       
   282             return ext
       
   283 
       
   284     # otherwise, interrogate each extension until there's a match
       
   285     for name, path in paths.iteritems():
       
   286         ext = findcmd(cmd, name, path)
       
   287         if ext:
       
   288             return ext
       
   289 
       
   290     raise error.UnknownCommand(cmd)
       
   291 
       
   292 def enabled():
       
   293     '''return a dict of {name: desc} of extensions, and the max name length'''
       
   294     exts = {}
       
   295     maxlength = 0
       
   296     for ename, ext in extensions():
       
   297         doc = (gettext(ext.__doc__) or _('(no help text available)'))
       
   298         ename = ename.split('.')[-1]
       
   299         maxlength = max(len(ename), maxlength)
       
   300         exts[ename] = doc.splitlines()[0].strip()
       
   301 
       
   302     return exts, maxlength