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