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