##// END OF EJS Templates
exthelper: simplify configitem registration
Matt Harbison -
r41075:c1476d09 default
parent child Browse files
Show More
@@ -1,354 +1,348 b''
1 1 # Copyright 2012 Logilab SA <contact@logilab.fr>
2 2 # Pierre-Yves David <pierre-yves.david@ens-lyon.org>
3 3 # Octobus <contact@octobus.net>
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 #####################################################################
9 9 ### Extension helper ###
10 10 #####################################################################
11 11
12 12 from __future__ import absolute_import
13 13
14 14 from . import (
15 15 commands,
16 configitems,
17 16 extensions,
18 17 fileset as filesetmod,
19 18 registrar,
20 19 revset as revsetmod,
21 20 templatekw as templatekwmod,
22 21 )
23 22
24 23 class exthelper(object):
25 24 """Helper for modular extension setup
26 25
27 26 A single helper should be instantiated for each extension. Helper
28 27 methods are then used as decorators for various purpose.
29 28
30 29 All decorators return the original function and may be chained.
31 30 """
32 31
33 32 def __init__(self):
34 33 self._uipopulatecallables = []
35 34 self._uicallables = []
36 35 self._extcallables = []
37 36 self._repocallables = []
38 37 self._revsetsymbols = []
39 38 self._filesetsymbols = []
40 39 self._templatekws = []
41 40 self._commandwrappers = []
42 41 self._extcommandwrappers = []
43 42 self._functionwrappers = []
44 43 self._duckpunchers = []
45 44 self.cmdtable = {}
46 45 self.command = registrar.command(self.cmdtable)
47 46 if '^init' in commands.table:
48 47 olddoregister = self.command._doregister
49 48
50 49 def _newdoregister(self, name, *args, **kwargs):
51 50 if kwargs.pop('helpbasic', False):
52 51 name = '^' + name
53 52 return olddoregister(self, name, *args, **kwargs)
54 53 self.command._doregister = _newdoregister
55 54
56 55 self.configtable = {}
57 self._configitem = registrar.configitem(self.configtable)
58
59 def configitem(self, section, config, default=configitems.dynamicdefault):
60 """Register a config item.
61 """
62 self._configitem(section, config, default=default)
56 self.configitem = registrar.configitem(self.configtable)
63 57
64 58 def merge(self, other):
65 59 self._uicallables.extend(other._uicallables)
66 60 self._uipopulatecallables.extend(other._uipopulatecallables)
67 61 self._extcallables.extend(other._extcallables)
68 62 self._repocallables.extend(other._repocallables)
69 63 self._revsetsymbols.extend(other._revsetsymbols)
70 64 self._filesetsymbols.extend(other._filesetsymbols)
71 65 self._templatekws.extend(other._templatekws)
72 66 self._commandwrappers.extend(other._commandwrappers)
73 67 self._extcommandwrappers.extend(other._extcommandwrappers)
74 68 self._functionwrappers.extend(other._functionwrappers)
75 69 self._duckpunchers.extend(other._duckpunchers)
76 70 self.cmdtable.update(other.cmdtable)
77 71 for section, items in other.configtable.iteritems():
78 72 if section in self.configtable:
79 73 self.configtable[section].update(items)
80 74 else:
81 75 self.configtable[section] = items
82 76
83 77 def finaluisetup(self, ui):
84 78 """Method to be used as the extension uisetup
85 79
86 80 The following operations belong here:
87 81
88 82 - Changes to ui.__class__ . The ui object that will be used to run the
89 83 command has not yet been created. Changes made here will affect ui
90 84 objects created after this, and in particular the ui that will be
91 85 passed to runcommand
92 86 - Command wraps (extensions.wrapcommand)
93 87 - Changes that need to be visible to other extensions: because
94 88 initialization occurs in phases (all extensions run uisetup, then all
95 89 run extsetup), a change made here will be visible to other extensions
96 90 during extsetup
97 91 - Monkeypatch or wrap function (extensions.wrapfunction) of dispatch
98 92 module members
99 93 - Setup of pre-* and post-* hooks
100 94 - pushkey setup
101 95 """
102 96 for cont, funcname, func in self._duckpunchers:
103 97 setattr(cont, funcname, func)
104 98 for command, wrapper, opts in self._commandwrappers:
105 99 entry = extensions.wrapcommand(commands.table, command, wrapper)
106 100 if opts:
107 101 for short, long, val, msg in opts:
108 102 entry[1].append((short, long, val, msg))
109 103 for cont, funcname, wrapper in self._functionwrappers:
110 104 extensions.wrapfunction(cont, funcname, wrapper)
111 105 for c in self._uicallables:
112 106 c(ui)
113 107
114 108 def finaluipopulate(self, ui):
115 109 """Method to be used as the extension uipopulate
116 110
117 111 This is called once per ui instance to:
118 112
119 113 - Set up additional ui members
120 114 - Update configuration by ``ui.setconfig()``
121 115 - Extend the class dynamically
122 116 """
123 117 for c in self._uipopulatecallables:
124 118 c(ui)
125 119
126 120 def finalextsetup(self, ui):
127 121 """Method to be used as a the extension extsetup
128 122
129 123 The following operations belong here:
130 124
131 125 - Changes depending on the status of other extensions. (if
132 126 extensions.find('mq'))
133 127 - Add a global option to all commands
134 128 - Register revset functions
135 129 """
136 130 knownexts = {}
137 131
138 132 revsetpredicate = registrar.revsetpredicate()
139 133 for name, symbol in self._revsetsymbols:
140 134 revsetpredicate(name)(symbol)
141 135 revsetmod.loadpredicate(ui, 'evolve', revsetpredicate)
142 136
143 137 filesetpredicate = registrar.filesetpredicate()
144 138 for name, symbol in self._filesetsymbols:
145 139 filesetpredicate(name)(symbol)
146 140 # TODO: Figure out the calling extension name
147 141 filesetmod.loadpredicate(ui, 'exthelper', filesetpredicate)
148 142
149 143 templatekeyword = registrar.templatekeyword()
150 144 for name, kw, requires in self._templatekws:
151 145 if requires is not None:
152 146 templatekeyword(name, requires=requires)(kw)
153 147 else:
154 148 templatekeyword(name)(kw)
155 149 templatekwmod.loadkeyword(ui, 'evolve', templatekeyword)
156 150
157 151 for ext, command, wrapper, opts in self._extcommandwrappers:
158 152 if ext not in knownexts:
159 153 try:
160 154 e = extensions.find(ext)
161 155 except KeyError:
162 156 # Extension isn't enabled, so don't bother trying to wrap
163 157 # it.
164 158 continue
165 159 knownexts[ext] = e.cmdtable
166 160 entry = extensions.wrapcommand(knownexts[ext], command, wrapper)
167 161 if opts:
168 162 for short, long, val, msg in opts:
169 163 entry[1].append((short, long, val, msg))
170 164
171 165 for c in self._extcallables:
172 166 c(ui)
173 167
174 168 def finalreposetup(self, ui, repo):
175 169 """Method to be used as the extension reposetup
176 170
177 171 The following operations belong here:
178 172
179 173 - All hooks but pre-* and post-*
180 174 - Modify configuration variables
181 175 - Changes to repo.__class__, repo.dirstate.__class__
182 176 """
183 177 for c in self._repocallables:
184 178 c(ui, repo)
185 179
186 180 def uisetup(self, call):
187 181 """Decorated function will be executed during uisetup
188 182
189 183 example::
190 184
191 185 @eh.uisetup
192 186 def setupbabar(ui):
193 187 print 'this is uisetup!'
194 188 """
195 189 self._uicallables.append(call)
196 190 return call
197 191
198 192 def uipopulate(self, call):
199 193 """Decorated function will be executed during uipopulate
200 194
201 195 example::
202 196
203 197 @eh.uipopulate
204 198 def setupfoo(ui):
205 199 print 'this is uipopulate!'
206 200 """
207 201 self._uipopulatecallables.append(call)
208 202 return call
209 203
210 204 def extsetup(self, call):
211 205 """Decorated function will be executed during extsetup
212 206
213 207 example::
214 208
215 209 @eh.extsetup
216 210 def setupcelestine(ui):
217 211 print 'this is extsetup!'
218 212 """
219 213 self._extcallables.append(call)
220 214 return call
221 215
222 216 def reposetup(self, call):
223 217 """Decorated function will be executed during reposetup
224 218
225 219 example::
226 220
227 221 @eh.reposetup
228 222 def setupzephir(ui, repo):
229 223 print 'this is reposetup!'
230 224 """
231 225 self._repocallables.append(call)
232 226 return call
233 227
234 228 def revset(self, symbolname):
235 229 """Decorated function is a revset symbol
236 230
237 231 The name of the symbol must be given as the decorator argument.
238 232 The symbol is added during `extsetup`.
239 233
240 234 example::
241 235
242 236 @eh.revset('hidden')
243 237 def revsetbabar(repo, subset, x):
244 238 args = revset.getargs(x, 0, 0, 'babar accept no argument')
245 239 return [r for r in subset if 'babar' in repo[r].description()]
246 240 """
247 241 def dec(symbol):
248 242 self._revsetsymbols.append((symbolname, symbol))
249 243 return symbol
250 244 return dec
251 245
252 246 def fileset(self, symbolname):
253 247 """Decorated function is a fileset symbol
254 248
255 249 The name of the symbol must be given as the decorator argument.
256 250 The symbol is added during `extsetup`.
257 251
258 252 example::
259 253
260 254 @eh.fileset('lfs()')
261 255 def filesetbabar(mctx, x):
262 256 return mctx.predicate(...)
263 257 """
264 258 def dec(symbol):
265 259 self._filesetsymbols.append((symbolname, symbol))
266 260 return symbol
267 261 return dec
268 262
269 263 def templatekw(self, keywordname, requires=None):
270 264 """Decorated function is a template keyword
271 265
272 266 The name of the keyword must be given as the decorator argument.
273 267 The symbol is added during `extsetup`.
274 268
275 269 example::
276 270
277 271 @eh.templatekw('babar')
278 272 def kwbabar(ctx):
279 273 return 'babar'
280 274 """
281 275 def dec(keyword):
282 276 self._templatekws.append((keywordname, keyword, requires))
283 277 return keyword
284 278 return dec
285 279
286 280 def wrapcommand(self, command, extension=None, opts=None):
287 281 """Decorated function is a command wrapper
288 282
289 283 The name of the command must be given as the decorator argument.
290 284 The wrapping is installed during `uisetup`.
291 285
292 286 If the second option `extension` argument is provided, the wrapping
293 287 will be applied in the extension commandtable. This argument must be a
294 288 string that will be searched using `extension.find` if not found and
295 289 Abort error is raised. If the wrapping applies to an extension, it is
296 290 installed during `extsetup`.
297 291
298 292 example::
299 293
300 294 @eh.wrapcommand('summary')
301 295 def wrapsummary(orig, ui, repo, *args, **kwargs):
302 296 ui.note('Barry!')
303 297 return orig(ui, repo, *args, **kwargs)
304 298
305 299 The `opts` argument allows specifying additional arguments for the
306 300 command.
307 301
308 302 """
309 303 if opts is None:
310 304 opts = []
311 305 def dec(wrapper):
312 306 if extension is None:
313 307 self._commandwrappers.append((command, wrapper, opts))
314 308 else:
315 309 self._extcommandwrappers.append((extension, command, wrapper,
316 310 opts))
317 311 return wrapper
318 312 return dec
319 313
320 314 def wrapfunction(self, container, funcname):
321 315 """Decorated function is a function wrapper
322 316
323 317 This function takes two arguments, the container and the name of the
324 318 function to wrap. The wrapping is performed during `uisetup`.
325 319 (there is no extension support)
326 320
327 321 example::
328 322
329 323 @eh.function(discovery, 'checkheads')
330 324 def wrapfunction(orig, *args, **kwargs):
331 325 ui.note('His head smashed in and his heart cut out')
332 326 return orig(*args, **kwargs)
333 327 """
334 328 def dec(wrapper):
335 329 self._functionwrappers.append((container, funcname, wrapper))
336 330 return wrapper
337 331 return dec
338 332
339 333 def addattr(self, container, funcname):
340 334 """Decorated function is to be added to the container
341 335
342 336 This function takes two arguments, the container and the name of the
343 337 function to wrap. The wrapping is performed during `uisetup`.
344 338
345 339 example::
346 340
347 341 @eh.function(context.changectx, 'babar')
348 342 def babar(ctx):
349 343 return 'babar' in ctx.description
350 344 """
351 345 def dec(func):
352 346 self._duckpunchers.append((container, funcname, func))
353 347 return func
354 348 return dec
General Comments 0
You need to be logged in to leave comments. Login now