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