profiling: allow loading profiling extension before everything else
6d642ecf1a89 makes profiler start early without loading extensions. That
makes it impossible for an extension to add customized profilers.
This patch adds a special case: if a profiler is not found but an extension
with the same name could be loaded, load that extension first, and expect it
to have a "profile" contextmanager method. This allows customized profilers
and extension setup time is still profiled.
--- a/mercurial/extensions.py Mon May 22 00:51:56 2017 -0700
+++ b/mercurial/extensions.py Mon May 22 01:17:49 2017 -0700
@@ -181,7 +181,7 @@
def loadall(ui, whitelist=None):
result = ui.configitems("extensions")
- if whitelist:
+ if whitelist is not None:
result = [(k, v) for (k, v) in result if k in whitelist]
newindex = len(_order)
for (name, path) in result:
--- a/mercurial/profiling.py Mon May 22 00:51:56 2017 -0700
+++ b/mercurial/profiling.py Mon May 22 01:17:49 2017 -0700
@@ -13,9 +13,21 @@
from . import (
encoding,
error,
+ extensions,
util,
)
+def _loadprofiler(ui, profiler):
+ """load profiler extension. return profile method, or None on failure"""
+ extname = profiler
+ extensions.loadall(ui, whitelist=[extname])
+ try:
+ mod = extensions.find(extname)
+ except KeyError:
+ return None
+ else:
+ return getattr(mod, 'profile', None)
+
@contextlib.contextmanager
def lsprofile(ui, fp):
format = ui.config('profiling', 'format', default='text')
@@ -137,11 +149,15 @@
manager exits, profiling results will be written to the configured output.
"""
profiler = encoding.environ.get('HGPROF')
+ proffn = None
if profiler is None:
profiler = ui.config('profiling', 'type', default='stat')
if profiler not in ('ls', 'stat', 'flame'):
- ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
- profiler = 'stat'
+ # try load profiler from extension with the same name
+ proffn = _loadprofiler(ui, profiler)
+ if proffn is None:
+ ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
+ profiler = 'stat'
output = ui.config('profiling', 'output')
@@ -154,7 +170,9 @@
fp = ui.ferr
try:
- if profiler == 'ls':
+ if proffn is not None:
+ pass
+ elif profiler == 'ls':
proffn = lsprofile
elif profiler == 'flame':
proffn = flameprofile
--- a/tests/test-profile.t Mon May 22 00:51:56 2017 -0700
+++ b/tests/test-profile.t Mon May 22 01:17:49 2017 -0700
@@ -99,3 +99,51 @@
[1]
$ cd ..
+
+profiler extension could be loaded before other extensions
+
+ $ cat > fooprof.py <<EOF
+ > from __future__ import absolute_import
+ > import contextlib
+ > @contextlib.contextmanager
+ > def profile(ui, fp):
+ > print('fooprof: start profile')
+ > yield
+ > print('fooprof: end profile')
+ > def extsetup(ui):
+ > ui.write('fooprof: loaded\n')
+ > EOF
+
+ $ cat > otherextension.py <<EOF
+ > from __future__ import absolute_import
+ > def extsetup(ui):
+ > ui.write('otherextension: loaded\n')
+ > EOF
+
+ $ hg init b
+ $ cd b
+ $ cat >> .hg/hgrc <<EOF
+ > [extensions]
+ > other = $TESTTMP/otherextension.py
+ > fooprof = $TESTTMP/fooprof.py
+ > EOF
+
+ $ hg root
+ otherextension: loaded
+ fooprof: loaded
+ $TESTTMP/b (glob)
+ $ HGPROF=fooprof hg root --profile
+ fooprof: loaded
+ fooprof: start profile
+ otherextension: loaded
+ $TESTTMP/b (glob)
+ fooprof: end profile
+
+ $ HGPROF=other hg root --profile 2>&1 | head -n 2
+ otherextension: loaded
+ unrecognized profiler 'other' - ignored
+
+ $ HGPROF=unknown hg root --profile 2>&1 | head -n 1
+ unrecognized profiler 'unknown' - ignored
+
+ $ cd ..