Patchwork [1,of,4,V2] templater: convert resources to a table of callables for future extension

login
register
mail settings
Submitter Yuya Nishihara
Date March 16, 2018, 4:03 p.m.
Message ID <5def08d8870b1a9b4f0d.1521216198@mimosa>
Download mbox | patch
Permalink /patch/29566/
State New
Headers show

Comments

Yuya Nishihara - March 16, 2018, 4:03 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1520769929 -32400
#      Sun Mar 11 21:05:29 2018 +0900
# Node ID 5def08d8870b1a9b4f0d88a80ac8f4696a199ea6
# Parent  c1f88589ddc9885b39025f3d4b19a5cf3cfe3251
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.)

Patch

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):