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