templater: factor out function to create mapping dict for nested evaluation
authorYuya Nishihara <yuya@tcha.org>
Thu, 15 Mar 2018 21:22:52 +0900
changeset 37074 2891079fb0c0
parent 37073 44757e6dad93
child 37075 46859b437697
templater: factor out function to create mapping dict for nested evaluation overlaymap() is the hook point to drop mapping items conflicting with the default keywords which have to be re-evaluated with new 'ctx' resource.
mercurial/templatekw.py
mercurial/templater.py
mercurial/templateutil.py
--- a/mercurial/templatekw.py	Thu Mar 15 20:43:39 2018 +0900
+++ b/mercurial/templatekw.py	Thu Mar 15 21:22:52 2018 +0900
@@ -449,8 +449,8 @@
 @templatekeyword('changessincelatesttag', requires={'repo', 'ctx', 'cache'})
 def showchangessincelatesttag(context, mapping):
     """Integer. All ancestors not in the latest tag."""
-    mapping = mapping.copy()
-    mapping['tag'] = getlatesttags(context, mapping)[2][0]
+    tag = getlatesttags(context, mapping)[2][0]
+    mapping = context.overlaymap(mapping, {'tag': tag})
     return _showchangessincetag(context, mapping)
 
 def _showchangessincetag(context, mapping):
@@ -480,8 +480,7 @@
         return
     mrev = repo.manifestlog._revlog.rev(mnode)
     mhex = hex(mnode)
-    mapping = mapping.copy()
-    mapping.update({'rev': mrev, 'node': mhex})
+    mapping = context.overlaymap(mapping, {'rev': mrev, 'node': mhex})
     f = context.process('manifest', mapping)
     # TODO: perhaps 'ctx' should be dropped from mapping because manifest
     # rev and node are completely different from changeset's.
--- a/mercurial/templater.py	Thu Mar 15 20:43:39 2018 +0900
+++ b/mercurial/templater.py	Thu Mar 15 21:22:52 2018 +0900
@@ -613,6 +613,13 @@
         self._aliasmap = _aliasrules.buildmap(aliases)
         self._cache = {}  # key: (func, data)
 
+    def overlaymap(self, origmapping, newmapping):
+        """Create combined mapping from the original mapping and partial
+        mapping to override the original"""
+        mapping = origmapping.copy()
+        mapping.update(newmapping)
+        return mapping
+
     def symbol(self, mapping, key):
         """Resolve symbol to value or function; None if nothing found"""
         v = None
--- a/mercurial/templateutil.py	Thu Mar 15 20:43:39 2018 +0900
+++ b/mercurial/templateutil.py	Thu Mar 15 21:22:52 2018 +0900
@@ -202,8 +202,8 @@
     startname = 'start_' + plural
     if context.preload(startname):
         yield context.process(startname, mapping)
-    vmapping = mapping.copy()
     def one(v, tag=name):
+        vmapping = {}
         try:
             vmapping.update(v)
         # Python 2 raises ValueError if the type of v is wrong. Python
@@ -216,6 +216,7 @@
                     vmapping[a] = b
             except (TypeError, ValueError):
                 vmapping[name] = v
+        vmapping = context.overlaymap(mapping, vmapping)
         return context.process(tag, vmapping)
     lastname = 'last_' + name
     if context.preload(lastname):
@@ -399,10 +400,9 @@
                 raise error.ParseError(_("%r is not iterable") % d)
 
     for i, v in enumerate(diter):
-        lm = mapping.copy()
-        lm['index'] = i
         if isinstance(v, dict):
-            lm.update(v)
+            lm = context.overlaymap(mapping, v)
+            lm['index'] = i
             lm['originalnode'] = mapping.get('node')
             yield evalrawexp(context, lm, targ)
         else:
@@ -415,8 +415,7 @@
     darg, memb = data
     d = evalrawexp(context, mapping, darg)
     if util.safehasattr(d, 'tomap'):
-        lm = mapping.copy()
-        lm.update(d.tomap())
+        lm = context.overlaymap(mapping, d.tomap())
         return runsymbol(context, lm, memb)
     if util.safehasattr(d, 'get'):
         return getdictitem(d, memb)