##// END OF EJS Templates
localrepo: create new function for instantiating a local repo object...
localrepo: create new function for instantiating a local repo object Today, there is a single local repository class - localrepository. Its __init__ is responsible for loading the .hg/requires file and taking different actions depending on what is present. In addition, extensions may define a "reposetup" function that monkeypatches constructed repository instances, often by implementing a derived type and changing the __class__ of the repo instance. Work around alternate storage backends and partial clone has made it clear to me that shoehorning all this logic into __init__ and operating on an existing instance is too convoluted. For example, localrepository assumes revlog storage and swapping in non-revlog storage requires overriding e.g. file() to return something that isn't a revlog. I've authored various patches that either: a) teach various methods (like file()) about different states and taking the appropriate code path at run-time b) create methods/attributes/callables used for instantiating things and populating these in __init__ "a" incurs run-time performance penalties and makes code more complicated since various functions have a bunch of "if storage is X" branches. "b" makes localrepository quickly explode in complexity. My plan for tackling this problem is to make the local repository type more dynamic. Instead of a static localrepository class/type that supports all of the local repository configurations (revlogs vs other, revlogs with ellipsis, revlog v1 versus revlog v2, etc), we'll dynamically construct a type providing the implementations that are needed for the repository on disk, derived from the .hg/requires file and configuration options. The constructed repository type will be specialized and methods won't need to be taught about different implementations nor overloaded. We may also leverage this functionality for building types that don't implement all attributes. For example, the "intents" feature allows commands to declare that they are read only. By dynamically constructing a repository type, we could return a repository instance with no attributes related to mutating the repository. This could include things like a "changelog" property implementation that doesn't check whether it needs to invalidate the hidden revisions set on every access. This commit establishes a function for building a local repository instance. Future commits will start moving functionality from localrepository.__init__ to this function. Then we'll start dynamically changing the returned type depending on options that are present. This change may seem radical. But it should be fully compatible with the reposetup() model - at least for now. Differential Revision: https://phab.mercurial-scm.org/D4563

File last commit:

r38279:15a1e37f default
r39723:bfeab472 default
Show More
profiling.py
251 lines | 7.9 KiB | text/x-python | PythonLexer
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 # profiling.py - profiling functions
#
# Copyright 2016 Gregory Szorc <gregory.szorc@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import, print_function
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 import contextlib
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
from .i18n import _
from . import (
Pulkit Goyal
py3: replace pycompat.getenv with encoding.environ.get...
r30820 encoding,
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 error,
Jun Wu
profiling: allow loading profiling extension before everything else...
r32417 extensions,
Matt Harbison
profile: colorize output on Windows...
r36701 pycompat,
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 util,
)
Jun Wu
profiling: allow loading profiling extension before everything else...
r32417 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)
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 @contextlib.contextmanager
def lsprofile(ui, fp):
Jun Wu
codemod: register core configitems using a script...
r33499 format = ui.config('profiling', 'format')
field = ui.config('profiling', 'sort')
limit = ui.configint('profiling', 'limit')
climit = ui.configint('profiling', 'nested')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
if format not in ['text', 'kcachegrind']:
ui.warn(_("unrecognized profiling format '%s'"
" - Ignored\n") % format)
format = 'text'
try:
from . import lsprof
except ImportError:
raise error.Abort(_(
'lsprof not available - install from '
'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
p = lsprof.Profiler()
p.enable(subcalls=True)
try:
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 yield
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 finally:
p.disable()
if format == 'kcachegrind':
from . import lsprofcalltree
calltree = lsprofcalltree.KCacheGrind(p)
calltree.output(fp)
else:
# format == 'text'
stats = lsprof.Stats(p.getstats())
stats.sort(field)
stats.pprint(limit=limit, file=fp, climit=climit)
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 @contextlib.contextmanager
def flameprofile(ui, fp):
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 try:
from flamegraph import flamegraph
except ImportError:
raise error.Abort(_(
'flamegraph not available - install from '
'https://github.com/evanhempel/python-flamegraph'))
# developer config: profiling.freq
Jun Wu
codemod: register core configitems using a script...
r33499 freq = ui.configint('profiling', 'freq')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 filter_ = None
collapse_recursion = True
thread = flamegraph.ProfileThread(fp, 1.0 / freq,
filter_, collapse_recursion)
Simon Farnsworth
mercurial: switch to util.timer for all interval timings...
r30975 start_time = util.timer()
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 try:
thread.start()
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 yield
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 finally:
thread.stop()
thread.join()
print('Collected %d stack frames (%d unique) in %2.2f seconds.' % (
Simon Farnsworth
mercurial: switch to util.timer for all interval timings...
r30975 util.timer() - start_time, thread.num_frames(),
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 thread.num_frames(unique=True)))
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 @contextlib.contextmanager
def statprofile(ui, fp):
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316 from . import statprof
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
Jun Wu
codemod: register core configitems using a script...
r33499 freq = ui.configint('profiling', 'freq')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 if freq > 0:
Gregory Szorc
profiling: don't error with statprof when profiling has already started...
r29785 # Cannot reset when profiler is already active. So silently no-op.
if statprof.state.profile_level == 0:
statprof.reset(freq)
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 else:
ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
Boris Feld
profiling: introduce a "profiling.time-track" option...
r38279 track = ui.config('profiling', 'time-track')
statprof.start(mechanism='thread', track=track)
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 try:
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 yield
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781 finally:
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316 data = statprof.stop()
Jun Wu
codemod: register core configitems using a script...
r33499 profformat = ui.config('profiling', 'statformat')
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316
formats = {
'byline': statprof.DisplayFormats.ByLine,
'bymethod': statprof.DisplayFormats.ByMethod,
'hotpath': statprof.DisplayFormats.Hotpath,
'json': statprof.DisplayFormats.Json,
Bryan O'Sullivan
profiling: add statprof support for Chrome trace viewer rendering...
r30930 'chrome': statprof.DisplayFormats.Chrome,
Gregory Szorc
profiling: use vendored statprof and upstream enhancements (BC)...
r30316 }
if profformat in formats:
displayformat = formats[profformat]
else:
ui.warn(_('unknown profiler output format: %s\n') % profformat)
displayformat = statprof.DisplayFormats.Hotpath
Bryan O'Sullivan
profiling: add statprof support for Chrome trace viewer rendering...
r30930 kwargs = {}
def fraction(s):
profiling: cope with configwith default value handling changes...
r32978 if isinstance(s, (float, int)):
return float(s)
Bryan O'Sullivan
profiling: add statprof support for Chrome trace viewer rendering...
r30930 if s.endswith('%'):
v = float(s[:-1]) / 100
else:
v = float(s)
if 0 <= v <= 1:
return v
raise ValueError(s)
if profformat == 'chrome':
showmin = ui.configwith(fraction, 'profiling', 'showmin', 0.005)
Boris Feld
configitems: register the 'profiling.showmax' config
r34411 showmax = ui.configwith(fraction, 'profiling', 'showmax')
Bryan O'Sullivan
profiling: add statprof support for Chrome trace viewer rendering...
r30930 kwargs.update(minthreshold=showmin, maxthreshold=showmax)
Gregory Szorc
profiling: allow configuring minimum display threshold for hotpath...
r32851 elif profformat == 'hotpath':
Gregory Szorc
check-config: syntax to allow inconsistent config values...
r33192 # inconsistent config: profiling.showmin
Gregory Szorc
profiling: allow configuring minimum display threshold for hotpath...
r32851 limit = ui.configwith(fraction, 'profiling', 'showmin', 0.05)
Pulkit Goyal
py3: fix handling of keyword arguments at more places...
r36418 kwargs[r'limit'] = limit
Bryan O'Sullivan
profiling: add statprof support for Chrome trace viewer rendering...
r30930
statprof.display(fp, data=data, format=displayformat, **kwargs)
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
profile: upgrade the "profile" context manager to a full class...
r32783 class profile(object):
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783 """Start profiling.
Profiling is active when the context manager is active. When the context
manager exits, profiling results will be written to the configured output.
"""
profile: introduce a knob to control if the context is actually profiling...
r32785 def __init__(self, ui, enabled=True):
profile: upgrade the "profile" context manager to a full class...
r32783 self._ui = ui
self._output = None
self._fp = None
profile: use explicit logic to control file closing...
r32805 self._fpdoclose = True
profile: upgrade the "profile" context manager to a full class...
r32783 self._profiler = None
profile: introduce a knob to control if the context is actually profiling...
r32785 self._enabled = enabled
profile: introduce a "start" method to the profile context...
r32784 self._entered = False
self._started = False
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
profile: upgrade the "profile" context manager to a full class...
r32783 def __enter__(self):
profile: introduce a "start" method to the profile context...
r32784 self._entered = True
profile: introduce a knob to control if the context is actually profiling...
r32785 if self._enabled:
self.start()
profile: make the contextmanager object available to the callers...
r32786 return self
profile: introduce a "start" method to the profile context...
r32784
def start(self):
"""Start profiling.
The profiling will stop at the context exit.
If the profiler was already started, this has no effect."""
if not self._entered:
raise error.ProgrammingError()
if self._started:
return
self._started = True
profile: upgrade the "profile" context manager to a full class...
r32783 profiler = encoding.environ.get('HGPROF')
proffn = None
if profiler is None:
Boris Feld
configitems: register the 'profiling.type' config
r34413 profiler = self._ui.config('profiling', 'type')
profile: upgrade the "profile" context manager to a full class...
r32783 if profiler not in ('ls', 'stat', 'flame'):
# try load profiler from extension with the same name
proffn = _loadprofiler(self._ui, profiler)
if proffn is None:
self._ui.warn(_("unrecognized profiler '%s' - ignored\n")
% profiler)
profiler = 'stat'
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
profile: upgrade the "profile" context manager to a full class...
r32783 self._output = self._ui.config('profiling', 'output')
Gregory Szorc
profiling: move profiling code from dispatch.py (API)...
r29781
profile: close 'fp' on error within '__enter__'...
r32808 try:
profile: indent part of '__enter__'...
r32807 if self._output == 'blackbox':
self._fp = util.stringio()
elif self._output:
path = self._ui.expandpath(self._output)
self._fp = open(path, 'wb')
Matt Harbison
profile: colorize output on Windows...
r36701 elif pycompat.iswindows:
# parse escape sequence by win32print()
class uifp(object):
def __init__(self, ui):
self._ui = ui
def write(self, data):
self._ui.write_err(data)
def flush(self):
self._ui.flush()
self._fpdoclose = False
self._fp = uifp(self._ui)
profile: indent part of '__enter__'...
r32807 else:
self._fpdoclose = False
self._fp = self._ui.ferr
profile: upgrade the "profile" context manager to a full class...
r32783
profile: indent part of '__enter__'...
r32807 if proffn is not None:
pass
elif profiler == 'ls':
proffn = lsprofile
elif profiler == 'flame':
proffn = flameprofile
else:
proffn = statprofile
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783
profile: indent part of '__enter__'...
r32807 self._profiler = proffn(self._ui, self._fp)
self._profiler.__enter__()
profile: close 'fp' on error within '__enter__'...
r32808 except: # re-raises
self._closefp()
raise
Gregory Szorc
profiling: make profiling functions context managers (API)...
r29783
profile: upgrade the "profile" context manager to a full class...
r32783 def __exit__(self, exception_type, exception_value, traceback):
profile: properly propagate exception from the sub-context manager...
r32810 propagate = None
profile: close 'fp' in all cases...
r32809 if self._profiler is not None:
profile: properly propagate exception from the sub-context manager...
r32810 propagate = self._profiler.__exit__(exception_type, exception_value,
traceback)
profile: close 'fp' in all cases...
r32809 if self._output == 'blackbox':
val = 'Profile:\n%s' % self._fp.getvalue()
# ui.log treats the input as a format string,
# so we need to escape any % signs.
val = val.replace('%', '%%')
self._ui.log('profile', val)
profile: remove now useless indent...
r32806 self._closefp()
profile: properly propagate exception from the sub-context manager...
r32810 return propagate
profiling: move 'fp' closing logic into its own function...
r32804
def _closefp(self):
profile: use explicit logic to control file closing...
r32805 if self._fpdoclose and self._fp is not None:
self._fp.close()