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