templater: introduce resourcemapper class
A couple more functions will be added later to work around nested mapping
bugs such as the issue 5612.
--- a/mercurial/formatter.py Thu Mar 15 20:27:38 2018 +0900
+++ b/mercurial/formatter.py Thu Mar 15 20:43:39 2018 +0900
@@ -494,22 +494,32 @@
t.cache[''] = tmpl
return t
-def templateresources(ui, repo=None):
- """Create a dict of template resources designed for the default templatekw
- and function"""
- resmap = {
- 'cache': {}, # for templatekw/funcs to store reusable data
- 'repo': repo,
- 'ui': ui,
- }
+class templateresources(templater.resourcemapper):
+ """Resource mapper designed for the default templatekw and function"""
+
+ def __init__(self, ui, repo=None):
+ self._resmap = {
+ 'cache': {}, # for templatekw/funcs to store reusable data
+ 'repo': repo,
+ 'ui': ui,
+ }
- def getsome(context, mapping, key):
+ def knownkeys(self):
+ return self._knownkeys
+
+ def lookup(self, context, mapping, key):
+ get = self._gettermap.get(key)
+ if not get:
+ return None
+ return get(self, context, mapping, key)
+
+ def _getsome(self, context, mapping, key):
v = mapping.get(key)
if v is not None:
return v
- return resmap.get(key)
+ return self._resmap.get(key)
- def getctx(context, mapping, key):
+ def _getctx(self, context, mapping, key):
ctx = mapping.get('ctx')
if ctx is not None:
return ctx
@@ -517,20 +527,21 @@
if fctx is not None:
return fctx.changectx()
- def getrepo(context, mapping, key):
- ctx = getctx(context, mapping, 'ctx')
+ def _getrepo(self, context, mapping, key):
+ ctx = self._getctx(context, mapping, 'ctx')
if ctx is not None:
return ctx.repo()
- return getsome(context, mapping, key)
+ return self._getsome(context, mapping, key)
- return {
- 'cache': getsome,
- 'ctx': getctx,
- 'fctx': getsome,
- 'repo': getrepo,
- 'revcache': getsome, # per-ctx cache; set later
- 'ui': getsome,
+ _gettermap = {
+ 'cache': _getsome,
+ 'ctx': _getctx,
+ 'fctx': _getsome,
+ 'repo': _getrepo,
+ 'revcache': _getsome, # per-ctx cache; set later
+ 'ui': _getsome,
}
+ _knownkeys = set(_gettermap.keys())
def formatter(ui, out, topic, opts):
template = opts.get("template", "")
--- a/mercurial/templater.py Thu Mar 15 20:27:38 2018 +0900
+++ b/mercurial/templater.py Thu Mar 15 20:43:39 2018 +0900
@@ -48,6 +48,7 @@
from __future__ import absolute_import, print_function
+import abc
import os
from .i18n import _
@@ -556,6 +557,26 @@
return s
return s[1:-1]
+class resourcemapper(object):
+ """Mapper of internal template resources"""
+
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def knownkeys(self):
+ """Return a set of supported resource keys"""
+
+ @abc.abstractmethod
+ def lookup(self, context, mapping, key):
+ """Return a resource for the key if available; otherwise None"""
+
+class nullresourcemapper(resourcemapper):
+ def knownkeys(self):
+ return set()
+
+ def lookup(self, context, mapping, key):
+ return None
+
class engine(object):
'''template expansion engine.
@@ -586,7 +607,7 @@
if defaults is None:
defaults = {}
if resources is None:
- resources = {}
+ resources = nullresourcemapper()
self._defaults = defaults
self._resources = resources
self._aliasmap = _aliasrules.buildmap(aliases)
@@ -595,7 +616,7 @@
def symbol(self, mapping, key):
"""Resolve symbol to value or function; None if nothing found"""
v = None
- if key not in self._resources:
+ if key not in self._resources.knownkeys():
v = mapping.get(key)
if v is None:
v = self._defaults.get(key)
@@ -604,9 +625,7 @@
def resource(self, mapping, key):
"""Return internal data (e.g. cache) used for keyword/function
evaluation"""
- v = None
- if key in self._resources:
- v = self._resources[key](self, mapping, key)
+ v = self._resources.lookup(self, mapping, key)
if v is None:
raise templateutil.ResourceUnavailable(
_('template resource not available: %s') % key)
@@ -717,7 +736,7 @@
- ``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 functions returning internal data
+ - ``resources``: a resourcemapper object to look up internal data
(e.g. cache), inaccessible from user template.
- ``cache``: a dict of preloaded template fragments.
- ``aliases``: a list of alias (name, replacement) pairs.
@@ -729,8 +748,6 @@
filters = {}
if defaults is None:
defaults = {}
- if resources is None:
- resources = {}
if cache is None:
cache = {}
self.cache = cache.copy()
--- a/mercurial/templateutil.py Thu Mar 15 20:27:38 2018 +0900
+++ b/mercurial/templateutil.py Thu Mar 15 20:43:39 2018 +0900
@@ -349,8 +349,8 @@
if callable(v) and getattr(v, '_requires', None) is None:
# old templatekw: expand all keywords and resources
# (TODO: deprecate this after porting web template keywords to new API)
- props = {k: f(context, mapping, k)
- for k, f in context._resources.items()}
+ props = {k: context._resources.lookup(context, mapping, k)
+ for k in context._resources.knownkeys()}
# pass context to _showcompatlist() through templatekw._showlist()
props['templ'] = context
props.update(mapping)