##// END OF EJS Templates
wrapfunction: use sysstr instead of bytes as argument in "exthelper"...
marmoute -
r51691:d9e22b39 default
parent child Browse files
Show More
@@ -1,338 +1,338 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
13 13 from . import (
14 14 commands,
15 15 error,
16 16 extensions,
17 17 registrar,
18 18 )
19 19
20 20 from hgdemandimport import tracing
21 21
22 22
23 23 class exthelper:
24 24 """Helper for modular extension setup
25 25
26 26 A single helper should be instantiated for each module of an
27 27 extension, where a command or function needs to be wrapped, or a
28 28 command, extension hook, fileset, revset or template needs to be
29 29 registered. Helper methods are then used as decorators for
30 30 these various purposes. If an extension spans multiple modules,
31 31 all helper instances should be merged in the main module.
32 32
33 33 All decorators return the original function and may be chained.
34 34
35 35 Aside from the helper functions with examples below, several
36 36 registrar method aliases are available for adding commands,
37 37 configitems, filesets, revsets, and templates. Simply decorate
38 38 the appropriate methods, and assign the corresponding exthelper
39 39 variable to a module level variable of the extension. The
40 40 extension loading mechanism will handle the rest.
41 41
42 42 example::
43 43
44 44 # ext.py
45 45 eh = exthelper.exthelper()
46 46
47 47 # As needed (failure to do this will mean your registration will not
48 48 # happen):
49 49 cmdtable = eh.cmdtable
50 50 configtable = eh.configtable
51 51 filesetpredicate = eh.filesetpredicate
52 52 revsetpredicate = eh.revsetpredicate
53 53 templatekeyword = eh.templatekeyword
54 54
55 55 # As needed (failure to do this will mean your eh.wrap*-decorated
56 56 # functions will not wrap, and/or your eh.*setup-decorated functions
57 57 # will not execute):
58 58 uisetup = eh.finaluisetup
59 59 extsetup = eh.finalextsetup
60 60 reposetup = eh.finalreposetup
61 61 uipopulate = eh.finaluipopulate
62 62
63 63 @eh.command(b'mynewcommand',
64 64 [(b'r', b'rev', [], _(b'operate on these revisions'))],
65 65 _(b'-r REV...'),
66 66 helpcategory=command.CATEGORY_XXX)
67 67 def newcommand(ui, repo, *revs, **opts):
68 68 # implementation goes here
69 69
70 70 eh.configitem(b'experimental', b'foo',
71 71 default=False,
72 72 )
73 73
74 74 @eh.filesetpredicate(b'lfs()')
75 75 def filesetbabar(mctx, x):
76 76 return mctx.predicate(...)
77 77
78 78 @eh.revsetpredicate(b'hidden')
79 79 def revsetbabar(repo, subset, x):
80 80 args = revset.getargs(x, 0, 0, b'babar accept no argument')
81 81 return [r for r in subset if b'babar' in repo[r].description()]
82 82
83 83 @eh.templatekeyword(b'babar')
84 84 def kwbabar(ctx):
85 85 return b'babar'
86 86 """
87 87
88 88 def __init__(self):
89 89 self._uipopulatecallables = []
90 90 self._uicallables = []
91 91 self._extcallables = []
92 92 self._repocallables = []
93 93 self._commandwrappers = []
94 94 self._extcommandwrappers = []
95 95 self._functionwrappers = []
96 96 self.cmdtable = {}
97 97 self.command = registrar.command(self.cmdtable)
98 98 self.configtable = {}
99 99 self.configitem = registrar.configitem(self.configtable)
100 100 self.filesetpredicate = registrar.filesetpredicate()
101 101 self.revsetpredicate = registrar.revsetpredicate()
102 102 self.templatekeyword = registrar.templatekeyword()
103 103
104 104 def merge(self, other):
105 105 self._uicallables.extend(other._uicallables)
106 106 self._uipopulatecallables.extend(other._uipopulatecallables)
107 107 self._extcallables.extend(other._extcallables)
108 108 self._repocallables.extend(other._repocallables)
109 109 self.filesetpredicate._merge(other.filesetpredicate)
110 110 self.revsetpredicate._merge(other.revsetpredicate)
111 111 self.templatekeyword._merge(other.templatekeyword)
112 112 self._commandwrappers.extend(other._commandwrappers)
113 113 self._extcommandwrappers.extend(other._extcommandwrappers)
114 114 self._functionwrappers.extend(other._functionwrappers)
115 115 self.cmdtable.update(other.cmdtable)
116 116 for section, items in other.configtable.items():
117 117 if section in self.configtable:
118 118 self.configtable[section].update(items)
119 119 else:
120 120 self.configtable[section] = items
121 121
122 122 def finaluisetup(self, ui):
123 123 """Method to be used as the extension uisetup
124 124
125 125 The following operations belong here:
126 126
127 127 - Changes to ui.__class__ . The ui object that will be used to run the
128 128 command has not yet been created. Changes made here will affect ui
129 129 objects created after this, and in particular the ui that will be
130 130 passed to runcommand
131 131 - Command wraps (extensions.wrapcommand)
132 132 - Changes that need to be visible to other extensions: because
133 133 initialization occurs in phases (all extensions run uisetup, then all
134 134 run extsetup), a change made here will be visible to other extensions
135 135 during extsetup
136 136 - Monkeypatch or wrap function (extensions.wrapfunction) of dispatch
137 137 module members
138 138 - Setup of pre-* and post-* hooks
139 139 - pushkey setup
140 140 """
141 141 for command, wrapper, opts in self._commandwrappers:
142 142 entry = extensions.wrapcommand(commands.table, command, wrapper)
143 143 if opts:
144 144 for opt in opts:
145 145 entry[1].append(opt)
146 146 for cont, funcname, wrapper in self._functionwrappers:
147 147 extensions.wrapfunction(cont, funcname, wrapper)
148 148 for c in self._uicallables:
149 149 with tracing.log('finaluisetup: %s', repr(c)):
150 150 c(ui)
151 151
152 152 def finaluipopulate(self, ui):
153 153 """Method to be used as the extension uipopulate
154 154
155 155 This is called once per ui instance to:
156 156
157 157 - Set up additional ui members
158 158 - Update configuration by ``ui.setconfig()``
159 159 - Extend the class dynamically
160 160 """
161 161 for c in self._uipopulatecallables:
162 162 c(ui)
163 163
164 164 def finalextsetup(self, ui):
165 165 """Method to be used as the extension extsetup
166 166
167 167 The following operations belong here:
168 168
169 169 - Changes depending on the status of other extensions. (if
170 170 extensions.find(b'mq'))
171 171 - Add a global option to all commands
172 172 """
173 173 knownexts = {}
174 174
175 175 for ext, command, wrapper, opts in self._extcommandwrappers:
176 176 if ext not in knownexts:
177 177 try:
178 178 e = extensions.find(ext)
179 179 except KeyError:
180 180 # Extension isn't enabled, so don't bother trying to wrap
181 181 # it.
182 182 continue
183 183 knownexts[ext] = e.cmdtable
184 184 entry = extensions.wrapcommand(knownexts[ext], command, wrapper)
185 185 if opts:
186 186 for opt in opts:
187 187 entry[1].append(opt)
188 188
189 189 for c in self._extcallables:
190 190 with tracing.log('finalextsetup: %s', repr(c)):
191 191 c(ui)
192 192
193 193 def finalreposetup(self, ui, repo):
194 194 """Method to be used as the extension reposetup
195 195
196 196 The following operations belong here:
197 197
198 198 - All hooks but pre-* and post-*
199 199 - Modify configuration variables
200 200 - Changes to repo.__class__, repo.dirstate.__class__
201 201 """
202 202 for c in self._repocallables:
203 203 with tracing.log('finalreposetup: %s', repr(c)):
204 204 c(ui, repo)
205 205
206 206 def uisetup(self, call):
207 207 """Decorated function will be executed during uisetup
208 208
209 209 example::
210 210
211 211 # Required, otherwise your uisetup function(s) will not execute.
212 212 uisetup = eh.finaluisetup
213 213
214 214 @eh.uisetup
215 215 def setupbabar(ui):
216 216 print('this is uisetup!')
217 217 """
218 218 self._uicallables.append(call)
219 219 return call
220 220
221 221 def uipopulate(self, call):
222 222 """Decorated function will be executed during uipopulate
223 223
224 224 example::
225 225
226 226 # Required, otherwise your uipopulate function(s) will not execute.
227 227 uipopulate = eh.finaluipopulate
228 228
229 229 @eh.uipopulate
230 230 def setupfoo(ui):
231 231 print('this is uipopulate!')
232 232 """
233 233 self._uipopulatecallables.append(call)
234 234 return call
235 235
236 236 def extsetup(self, call):
237 237 """Decorated function will be executed during extsetup
238 238
239 239 example::
240 240
241 241 # Required, otherwise your extsetup function(s) will not execute.
242 242 extsetup = eh.finalextsetup
243 243
244 244 @eh.extsetup
245 245 def setupcelestine(ui):
246 246 print('this is extsetup!')
247 247 """
248 248 self._extcallables.append(call)
249 249 return call
250 250
251 251 def reposetup(self, call):
252 252 """Decorated function will be executed during reposetup
253 253
254 254 example::
255 255
256 256 # Required, otherwise your reposetup function(s) will not execute.
257 257 reposetup = eh.finalreposetup
258 258
259 259 @eh.reposetup
260 260 def setupzephir(ui, repo):
261 261 print('this is reposetup!')
262 262 """
263 263 self._repocallables.append(call)
264 264 return call
265 265
266 266 def wrapcommand(self, command, extension=None, opts=None):
267 267 """Decorated function is a command wrapper
268 268
269 269 The name of the command must be given as the decorator argument.
270 270 The wrapping is installed during `uisetup`.
271 271
272 272 If the second option `extension` argument is provided, the wrapping
273 273 will be applied in the extension commandtable. This argument must be a
274 274 string that will be searched using `extension.find` if not found and
275 275 Abort error is raised. If the wrapping applies to an extension, it is
276 276 installed during `extsetup`.
277 277
278 278 example::
279 279
280 280 # Required if `extension` is not provided
281 281 uisetup = eh.finaluisetup
282 282 # Required if `extension` is provided
283 283 extsetup = eh.finalextsetup
284 284
285 285 @eh.wrapcommand(b'summary')
286 286 def wrapsummary(orig, ui, repo, *args, **kwargs):
287 287 ui.note(b'Barry!')
288 288 return orig(ui, repo, *args, **kwargs)
289 289
290 290 The `opts` argument allows specifying a list of tuples for additional
291 291 arguments for the command. See ``mercurial.fancyopts.fancyopts()`` for
292 292 the format of the tuple.
293 293
294 294 """
295 295 if opts is None:
296 296 opts = []
297 297 else:
298 298 for opt in opts:
299 299 if not isinstance(opt, tuple):
300 300 raise error.ProgrammingError(b'opts must be list of tuples')
301 301 if len(opt) not in (4, 5):
302 302 msg = b'each opt tuple must contain 4 or 5 values'
303 303 raise error.ProgrammingError(msg)
304 304
305 305 def dec(wrapper):
306 306 if extension is None:
307 307 self._commandwrappers.append((command, wrapper, opts))
308 308 else:
309 309 self._extcommandwrappers.append(
310 310 (extension, command, wrapper, opts)
311 311 )
312 312 return wrapper
313 313
314 314 return dec
315 315
316 316 def wrapfunction(self, container, funcname):
317 317 """Decorated function is a function wrapper
318 318
319 319 This function takes two arguments, the container and the name of the
320 320 function to wrap. The wrapping is performed during `uisetup`.
321 321 (there is no extension support)
322 322
323 323 example::
324 324
325 325 # Required, otherwise the function will not be wrapped
326 326 uisetup = eh.finaluisetup
327 327
328 @eh.wrapfunction(discovery, b'checkheads')
328 @eh.wrapfunction(discovery, 'checkheads')
329 329 def wrapcheckheads(orig, *args, **kwargs):
330 330 ui.note(b'His head smashed in and his heart cut out')
331 331 return orig(*args, **kwargs)
332 332 """
333 333
334 334 def dec(wrapper):
335 335 self._functionwrappers.append((container, funcname, wrapper))
336 336 return wrapper
337 337
338 338 return dec
General Comments 0
You need to be logged in to leave comments. Login now