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