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