comparison mercurial/extensions.py @ 40996:6f2510b581a0

extensions: use ui.log() interface to provide detailed loading information The output format changes and the messages will be sent to stderr instead of stdout, but I don't think that matters.
author Yuya Nishihara <yuya@tcha.org>
date Sat, 15 Dec 2018 16:28:29 +0900
parents c93d046d4300
children 28a4fb793ba1
comparison
equal deleted inserted replaced
40995:19178aeb9b43 40996:6f2510b581a0
119 reportfunc(err, "hgext3rd.%s" % name, name) 119 reportfunc(err, "hgext3rd.%s" % name, name)
120 mod = _importh(name) 120 mod = _importh(name)
121 return mod 121 return mod
122 122
123 def _reportimporterror(ui, err, failed, next): 123 def _reportimporterror(ui, err, failed, next):
124 # note: this ui.debug happens before --debug is processed, 124 # note: this ui.log happens before --debug is processed,
125 # Use --config ui.debug=1 to see them. 125 # Use --config ui.debug=1 to see them.
126 if ui.configbool('devel', 'debug.extensions'): 126 ui.log(b'extension', b' - could not import %s (%s): trying %s\n',
127 ui.debug('debug.extensions: - could not import %s (%s): trying %s\n' 127 failed, stringutil.forcebytestr(err), next)
128 % (failed, stringutil.forcebytestr(err), next)) 128 if ui.debugflag and ui.configbool('devel', 'debug.extensions'):
129 if ui.debugflag: 129 ui.traceback()
130 ui.traceback()
131 130
132 def _rejectunicode(name, xs): 131 def _rejectunicode(name, xs):
133 if isinstance(xs, (list, set, tuple)): 132 if isinstance(xs, (list, set, tuple)):
134 for x in xs: 133 for x in xs:
135 _rejectunicode(name, x) 134 _rejectunicode(name, x)
164 o = getattr(mod, t, None) 163 o = getattr(mod, t, None)
165 if o: 164 if o:
166 _rejectunicode(t, o._table) 165 _rejectunicode(t, o._table)
167 _validatecmdtable(ui, getattr(mod, 'cmdtable', {})) 166 _validatecmdtable(ui, getattr(mod, 'cmdtable', {}))
168 167
169 def load(ui, name, path, log=lambda *a: None, loadingtime=None): 168 def load(ui, name, path, loadingtime=None):
170 if name.startswith('hgext.') or name.startswith('hgext/'): 169 if name.startswith('hgext.') or name.startswith('hgext/'):
171 shortname = name[6:] 170 shortname = name[6:]
172 else: 171 else:
173 shortname = name 172 shortname = name
174 if shortname in _builtin: 173 if shortname in _builtin:
175 return None 174 return None
176 if shortname in _extensions: 175 if shortname in _extensions:
177 return _extensions[shortname] 176 return _extensions[shortname]
178 log(' - loading extension: %s\n', shortname) 177 ui.log(b'extension', b' - loading extension: %s\n', shortname)
179 _extensions[shortname] = None 178 _extensions[shortname] = None
180 with util.timedcm('load extension %s', shortname) as stats: 179 with util.timedcm('load extension %s', shortname) as stats:
181 mod = _importext(name, path, bind(_reportimporterror, ui)) 180 mod = _importext(name, path, bind(_reportimporterror, ui))
182 log(' > %s extension loaded in %s\n', shortname, stats) 181 ui.log(b'extension', b' > %s extension loaded in %s\n', shortname, stats)
183 if loadingtime is not None: 182 if loadingtime is not None:
184 loadingtime[shortname] += stats.elapsed 183 loadingtime[shortname] += stats.elapsed
185 184
186 # Before we do anything with the extension, check against minimum stated 185 # Before we do anything with the extension, check against minimum stated
187 # compatibility. This gives extension authors a mechanism to have their 186 # compatibility. This gives extension authors a mechanism to have their
191 if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2): 190 if minver and util.versiontuple(minver, 2) > util.versiontuple(n=2):
192 msg = _('(third party extension %s requires version %s or newer ' 191 msg = _('(third party extension %s requires version %s or newer '
193 'of Mercurial (current: %s); disabling)\n') 192 'of Mercurial (current: %s); disabling)\n')
194 ui.warn(msg % (shortname, minver, util.version())) 193 ui.warn(msg % (shortname, minver, util.version()))
195 return 194 return
196 log(' - validating extension tables: %s\n', shortname) 195 ui.log(b'extension', b' - validating extension tables: %s\n', shortname)
197 _validatetables(ui, mod) 196 _validatetables(ui, mod)
198 197
199 _extensions[shortname] = mod 198 _extensions[shortname] = mod
200 _order.append(shortname) 199 _order.append(shortname)
201 log(' - invoking registered callbacks: %s\n', shortname) 200 ui.log(b'extension', b' - invoking registered callbacks: %s\n',
201 shortname)
202 with util.timedcm('callbacks extension %s', shortname) as stats: 202 with util.timedcm('callbacks extension %s', shortname) as stats:
203 for fn in _aftercallbacks.get(shortname, []): 203 for fn in _aftercallbacks.get(shortname, []):
204 fn(loaded=True) 204 fn(loaded=True)
205 log(' > callbacks completed in %s\n', stats) 205 ui.log(b'extension', b' > callbacks completed in %s\n', stats)
206 return mod 206 return mod
207 207
208 def _runuisetup(name, ui): 208 def _runuisetup(name, ui):
209 uisetup = getattr(_extensions[name], 'uisetup', None) 209 uisetup = getattr(_extensions[name], 'uisetup', None)
210 if uisetup: 210 if uisetup:
233 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg)) 233 ui.warn(_("*** failed to set up extension %s: %s\n") % (name, msg))
234 return False 234 return False
235 return True 235 return True
236 236
237 def loadall(ui, whitelist=None): 237 def loadall(ui, whitelist=None):
238 if ui.configbool('devel', 'debug.extensions'):
239 log = lambda msg, *values: ui.debug('debug.extensions: ',
240 msg % values, label='debug.extensions')
241 else:
242 log = lambda *a, **kw: None
243 loadingtime = collections.defaultdict(int) 238 loadingtime = collections.defaultdict(int)
244 result = ui.configitems("extensions") 239 result = ui.configitems("extensions")
245 if whitelist is not None: 240 if whitelist is not None:
246 result = [(k, v) for (k, v) in result if k in whitelist] 241 result = [(k, v) for (k, v) in result if k in whitelist]
247 newindex = len(_order) 242 newindex = len(_order)
248 log('loading %sextensions\n', 'additional ' if newindex else '') 243 ui.log(b'extension', b'loading %sextensions\n',
249 log('- processing %d entries\n', len(result)) 244 'additional ' if newindex else '')
245 ui.log(b'extension', b'- processing %d entries\n', len(result))
250 with util.timedcm('load all extensions') as stats: 246 with util.timedcm('load all extensions') as stats:
251 for (name, path) in result: 247 for (name, path) in result:
252 if path: 248 if path:
253 if path[0:1] == '!': 249 if path[0:1] == '!':
254 if name not in _disabledextensions: 250 if name not in _disabledextensions:
255 log(' - skipping disabled extension: %s\n', name) 251 ui.log(b'extension',
252 b' - skipping disabled extension: %s\n', name)
256 _disabledextensions[name] = path[1:] 253 _disabledextensions[name] = path[1:]
257 continue 254 continue
258 try: 255 try:
259 load(ui, name, path, log, loadingtime) 256 load(ui, name, path, loadingtime)
260 except Exception as inst: 257 except Exception as inst:
261 msg = stringutil.forcebytestr(inst) 258 msg = stringutil.forcebytestr(inst)
262 if path: 259 if path:
263 ui.warn(_("*** failed to import extension %s from %s: %s\n") 260 ui.warn(_("*** failed to import extension %s from %s: %s\n")
264 % (name, path, msg)) 261 % (name, path, msg))
267 % (name, msg)) 264 % (name, msg))
268 if isinstance(inst, error.Hint) and inst.hint: 265 if isinstance(inst, error.Hint) and inst.hint:
269 ui.warn(_("*** (%s)\n") % inst.hint) 266 ui.warn(_("*** (%s)\n") % inst.hint)
270 ui.traceback() 267 ui.traceback()
271 268
272 log('> loaded %d extensions, total time %s\n', 269 ui.log(b'extension', b'> loaded %d extensions, total time %s\n',
273 len(_order) - newindex, stats) 270 len(_order) - newindex, stats)
274 # list of (objname, loadermod, loadername) tuple: 271 # list of (objname, loadermod, loadername) tuple:
275 # - objname is the name of an object in extension module, 272 # - objname is the name of an object in extension module,
276 # from which extra information is loaded 273 # from which extra information is loaded
277 # - loadermod is the module where loader is placed 274 # - loadermod is the module where loader is placed
278 # - loadername is the name of the function, 275 # - loadername is the name of the function,
281 # This one is for the list of item that must be run before running any setup 278 # This one is for the list of item that must be run before running any setup
282 earlyextraloaders = [ 279 earlyextraloaders = [
283 ('configtable', configitems, 'loadconfigtable'), 280 ('configtable', configitems, 'loadconfigtable'),
284 ] 281 ]
285 282
286 log('- loading configtable attributes\n') 283 ui.log(b'extension', b'- loading configtable attributes\n')
287 _loadextra(ui, newindex, earlyextraloaders) 284 _loadextra(ui, newindex, earlyextraloaders)
288 285
289 broken = set() 286 broken = set()
290 log('- executing uisetup hooks\n') 287 ui.log(b'extension', b'- executing uisetup hooks\n')
291 with util.timedcm('all uisetup') as alluisetupstats: 288 with util.timedcm('all uisetup') as alluisetupstats:
292 for name in _order[newindex:]: 289 for name in _order[newindex:]:
293 log(' - running uisetup for %s\n', name) 290 ui.log(b'extension', b' - running uisetup for %s\n', name)
294 with util.timedcm('uisetup %s', name) as stats: 291 with util.timedcm('uisetup %s', name) as stats:
295 if not _runuisetup(name, ui): 292 if not _runuisetup(name, ui):
296 log(' - the %s extension uisetup failed\n', name) 293 ui.log(b'extension',
294 b' - the %s extension uisetup failed\n', name)
297 broken.add(name) 295 broken.add(name)
298 log(' > uisetup for %s took %s\n', name, stats) 296 ui.log(b'extension', b' > uisetup for %s took %s\n', name, stats)
299 loadingtime[name] += stats.elapsed 297 loadingtime[name] += stats.elapsed
300 log('> all uisetup took %s\n', alluisetupstats) 298 ui.log(b'extension', b'> all uisetup took %s\n', alluisetupstats)
301 299
302 log('- executing extsetup hooks\n') 300 ui.log(b'extension', b'- executing extsetup hooks\n')
303 with util.timedcm('all extsetup') as allextetupstats: 301 with util.timedcm('all extsetup') as allextetupstats:
304 for name in _order[newindex:]: 302 for name in _order[newindex:]:
305 if name in broken: 303 if name in broken:
306 continue 304 continue
307 log(' - running extsetup for %s\n', name) 305 ui.log(b'extension', b' - running extsetup for %s\n', name)
308 with util.timedcm('extsetup %s', name) as stats: 306 with util.timedcm('extsetup %s', name) as stats:
309 if not _runextsetup(name, ui): 307 if not _runextsetup(name, ui):
310 log(' - the %s extension extsetup failed\n', name) 308 ui.log(b'extension',
309 b' - the %s extension extsetup failed\n', name)
311 broken.add(name) 310 broken.add(name)
312 log(' > extsetup for %s took %s\n', name, stats) 311 ui.log(b'extension', b' > extsetup for %s took %s\n', name, stats)
313 loadingtime[name] += stats.elapsed 312 loadingtime[name] += stats.elapsed
314 log('> all extsetup took %s\n', allextetupstats) 313 ui.log(b'extension', b'> all extsetup took %s\n', allextetupstats)
315 314
316 for name in broken: 315 for name in broken:
317 log(' - disabling broken %s extension\n', name) 316 ui.log(b'extension', b' - disabling broken %s extension\n', name)
318 _extensions[name] = None 317 _extensions[name] = None
319 318
320 # Call aftercallbacks that were never met. 319 # Call aftercallbacks that were never met.
321 log('- executing remaining aftercallbacks\n') 320 ui.log(b'extension', b'- executing remaining aftercallbacks\n')
322 with util.timedcm('aftercallbacks') as stats: 321 with util.timedcm('aftercallbacks') as stats:
323 for shortname in _aftercallbacks: 322 for shortname in _aftercallbacks:
324 if shortname in _extensions: 323 if shortname in _extensions:
325 continue 324 continue
326 325
327 for fn in _aftercallbacks[shortname]: 326 for fn in _aftercallbacks[shortname]:
328 log(' - extension %s not loaded, notify callbacks\n', 327 ui.log(b'extension',
329 shortname) 328 b' - extension %s not loaded, notify callbacks\n',
329 shortname)
330 fn(loaded=False) 330 fn(loaded=False)
331 log('> remaining aftercallbacks completed in %s\n', stats) 331 ui.log(b'extension', b'> remaining aftercallbacks completed in %s\n', stats)
332 332
333 # loadall() is called multiple times and lingering _aftercallbacks 333 # loadall() is called multiple times and lingering _aftercallbacks
334 # entries could result in double execution. See issue4646. 334 # entries could result in double execution. See issue4646.
335 _aftercallbacks.clear() 335 _aftercallbacks.clear()
336 336
350 # - objname is the name of an object in extension module, 350 # - objname is the name of an object in extension module,
351 # from which extra information is loaded 351 # from which extra information is loaded
352 # - loadermod is the module where loader is placed 352 # - loadermod is the module where loader is placed
353 # - loadername is the name of the function, 353 # - loadername is the name of the function,
354 # which takes (ui, extensionname, extraobj) arguments 354 # which takes (ui, extensionname, extraobj) arguments
355 log('- loading extension registration objects\n') 355 ui.log(b'extension', b'- loading extension registration objects\n')
356 extraloaders = [ 356 extraloaders = [
357 ('cmdtable', commands, 'loadcmdtable'), 357 ('cmdtable', commands, 'loadcmdtable'),
358 ('colortable', color, 'loadcolortable'), 358 ('colortable', color, 'loadcolortable'),
359 ('filesetpredicate', fileset, 'loadpredicate'), 359 ('filesetpredicate', fileset, 'loadpredicate'),
360 ('internalmerge', filemerge, 'loadinternalmerge'), 360 ('internalmerge', filemerge, 'loadinternalmerge'),
363 ('templatefunc', templatefuncs, 'loadfunction'), 363 ('templatefunc', templatefuncs, 'loadfunction'),
364 ('templatekeyword', templatekw, 'loadkeyword'), 364 ('templatekeyword', templatekw, 'loadkeyword'),
365 ] 365 ]
366 with util.timedcm('load registration objects') as stats: 366 with util.timedcm('load registration objects') as stats:
367 _loadextra(ui, newindex, extraloaders) 367 _loadextra(ui, newindex, extraloaders)
368 log('> extension registration object loading took %s\n', stats) 368 ui.log(b'extension', b'> extension registration object loading took %s\n',
369 stats)
369 370
370 # Report per extension loading time (except reposetup) 371 # Report per extension loading time (except reposetup)
371 for name in sorted(loadingtime): 372 for name in sorted(loadingtime):
372 extension_msg = '> extension %s take a total of %s to load\n' 373 ui.log(b'extension', b'> extension %s take a total of %s to load\n',
373 log(extension_msg, name, util.timecount(loadingtime[name])) 374 name, util.timecount(loadingtime[name]))
374 375
375 log('extension loading complete\n') 376 ui.log(b'extension', b'extension loading complete\n')
376 377
377 def _loadextra(ui, newindex, extraloaders): 378 def _loadextra(ui, newindex, extraloaders):
378 for name in _order[newindex:]: 379 for name in _order[newindex:]:
379 module = _extensions[name] 380 module = _extensions[name]
380 if not module: 381 if not module: