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