# HG changeset patch # User Yuya Nishihara # Date 2018-03-11 12:05:29 # Node ID 255f635c3204baf09e418655ffe54c88221b170c # Parent c479692690efb14def6681640cde7bd9b4bc3533 templater: convert resources to a table of callables for future extension I'm going to add a full templating support to the annotate command. As the annotate is a filectx-oriented command, we'll need a way to look up a ctx from a bounded fctx only when necessary. This is the minimal change to support that. I'm thinking of defining a proper interface to look up template resources to fix other issues, but that isn't ready yet. (Changes from V1: just updated tests and patch descriptions.) diff --git a/mercurial/formatter.py b/mercurial/formatter.py --- a/mercurial/formatter.py +++ b/mercurial/formatter.py @@ -501,14 +501,23 @@ def maketemplater(ui, tmpl, defaults=Non def templateresources(ui, repo=None): """Create a dict of template resources designed for the default templatekw and function""" - return { + resmap = { 'cache': {}, # for templatekw/funcs to store reusable data - 'ctx': None, 'repo': repo, - 'revcache': None, # per-ctx cache; set later 'ui': ui, } + def getsome(context, mapping, key): + return resmap.get(key) + + return { + 'cache': getsome, + 'ctx': getsome, + 'repo': getsome, + 'revcache': getsome, # per-ctx cache; set later + 'ui': getsome, + } + def formatter(ui, out, topic, opts): template = opts.get("template", "") if template == "json": diff --git a/mercurial/logcmdutil.py b/mercurial/logcmdutil.py --- a/mercurial/logcmdutil.py +++ b/mercurial/logcmdutil.py @@ -423,7 +423,7 @@ class changesettemplater(changesetprinte resources=tres, cache=templatekw.defaulttempl) self._counter = itertools.count() - self.cache = tres['cache'] # shared with _graphnodeformatter() + self._getcache = tres['cache'] # shared with _graphnodeformatter() self._tref = tmplspec.ref self._parts = {'header': '', 'footer': '', @@ -852,7 +852,8 @@ def _graphnodeformatter(ui, displayer): spec = templater.unquotestring(spec) tres = formatter.templateresources(ui) if isinstance(displayer, changesettemplater): - tres['cache'] = displayer.cache # reuse cache of slow templates + # reuse cache of slow templates + tres['cache'] = displayer._getcache templ = formatter.maketemplater(ui, spec, defaults=templatekw.keywords, resources=tres) def formatnode(repo, ctx): diff --git a/mercurial/templater.py b/mercurial/templater.py --- a/mercurial/templater.py +++ b/mercurial/templater.py @@ -566,8 +566,8 @@ class engine(object): v = None if key in self._resources: v = mapping.get(key) - if v is None: - v = self._resources.get(key) + if v is None and key in self._resources: + v = self._resources[key](self, mapping, key) if v is None: raise templateutil.ResourceUnavailable( _('template resource not available: %s') % key) @@ -670,8 +670,9 @@ class templater(object): - ``filters``: a dict of functions to transform a value into another. - ``defaults``: a dict of symbol values/functions; may be overridden by a ``mapping`` dict. - - ``resources``: a dict of internal data (e.g. cache), inaccessible - from user template; may be overridden by a ``mapping`` dict. + - ``resources``: a dict of functions returning internal data + (e.g. cache), inaccessible from user template; may be overridden by + a ``mapping`` dict. - ``cache``: a dict of preloaded template fragments. - ``aliases``: a list of alias (name, replacement) pairs. @@ -691,7 +692,7 @@ class templater(object): self.filters = templatefilters.filters.copy() self.filters.update(filters) self.defaults = defaults - self._resources = {'templ': self} + self._resources = {'templ': lambda context, mapping, key: self} self._resources.update(resources) self._aliases = aliases self.minchunk, self.maxchunk = minchunk, maxchunk diff --git a/mercurial/templateutil.py b/mercurial/templateutil.py --- a/mercurial/templateutil.py +++ b/mercurial/templateutil.py @@ -350,7 +350,8 @@ def runsymbol(context, mapping, key, def v = default if callable(v) and getattr(v, '_requires', None) is None: # old templatekw: expand all keywords and resources - props = context._resources.copy() + props = {k: f(context, mapping, k) + for k, f in context._resources.items()} props.update(mapping) return v(**pycompat.strkwargs(props)) if callable(v):