Patchwork [2,of,9] templater: use template context to render old-style list template

mail settings
Submitter Yuya Nishihara
Date March 21, 2018, 1:51 p.m.
Message ID <f8432bc67321b317502c.1521640310@mimosa>
Download mbox | patch
Permalink /patch/29688/
State Accepted
Headers show


Yuya Nishihara - March 21, 2018, 1:51 p.m.
# HG changeset patch
# User Yuya Nishihara <>
# Date 1521208035 -32400
#      Fri Mar 16 22:47:15 2018 +0900
# Node ID f8432bc67321b317502c765d4c3412a77552ada6
# Parent  5f2336d71c2403543586388e4ee628e221138870
templater: use template context to render old-style list template

Prepares for dropping the 'templ' resource.

This means old-style list templates are processed by the same engine class
as the one for the list node. I think that's fine since templates for the
same list should be tightly coupled, and I believe the extension point for
the engine classes isn't actually used.

Now templatekw._showlist() is a compatibility wrapper for _showcompatlist(),
and will be deprecated soon. The function is still marked as private since
I plan to change the interface to get rid of closures capturing context and


diff --git a/hgext/lfs/ b/hgext/lfs/
--- a/hgext/lfs/
+++ b/hgext/lfs/
@@ -356,12 +356,11 @@  def lfsfileset(mctx, x):
     return [f for f in mctx.subset
             if wrapper.pointerfromctx(mctx.ctx, f, removed=True) is not None]
-@templatekeyword('lfs_files', requires={'ctx', 'templ'})
+@templatekeyword('lfs_files', requires={'ctx'})
 def lfsfiles(context, mapping):
     """List of strings. All files modified, added, or removed by this
     ctx = context.resource(mapping, 'ctx')
-    templ = context.resource(mapping, 'templ')
     pointers = wrapper.pointersfromctx(ctx, removed=True) # {path: pointer}
     files = sorted(pointers.keys())
@@ -379,7 +378,7 @@  def lfsfiles(context, mapping):
     # TODO: make the separator ', '?
-    f = templateutil._showlist('lfs_file', files, templ, mapping)
+    f = templateutil._showcompatlist(context, mapping, 'lfs_file', files)
     return templateutil.hybrid(f, files, makemap, pycompat.identity)
diff --git a/hgext/ b/hgext/
--- a/hgext/
+++ b/hgext/
@@ -230,7 +230,7 @@  def reposetup(ui, repo):
                 repo._remotenames.nodetobranch().get(node, []))
-@templatekeyword('remotenames', requires={'repo', 'ctx', 'templ'})
+@templatekeyword('remotenames', requires={'repo', 'ctx'})
 def remotenameskw(context, mapping):
     """List of strings. Remote names associated with the changeset."""
     repo = context.resource(mapping, 'repo')
@@ -246,7 +246,7 @@  def remotenameskw(context, mapping):
     return templateutil.compatlist(context, mapping, 'remotename', remotenames,
-@templatekeyword('remotebookmarks', requires={'repo', 'ctx', 'templ'})
+@templatekeyword('remotebookmarks', requires={'repo', 'ctx'})
 def remotebookmarkskw(context, mapping):
     """List of strings. Remote bookmarks associated with the changeset."""
     repo = context.resource(mapping, 'repo')
@@ -259,7 +259,7 @@  def remotebookmarkskw(context, mapping):
     return templateutil.compatlist(context, mapping, 'remotebookmark',
                                    remotebmarks, plural='remotebookmarks')
-@templatekeyword('remotebranches', requires={'repo', 'ctx', 'templ'})
+@templatekeyword('remotebranches', requires={'repo', 'ctx'})
 def remotebrancheskw(context, mapping):
     """List of strings. Remote branches associated with the changeset."""
     repo = context.resource(mapping, 'repo')
diff --git a/mercurial/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -89,7 +89,7 @@  class namespaces(object):
         # we only generate a template keyword if one does not already exist
         if not in templatekw.keywords:
             templatekeyword = registrar.templatekeyword(templatekw.keywords)
-            @templatekeyword(, requires={'repo', 'ctx', 'templ'})
+            @templatekeyword(, requires={'repo', 'ctx'})
             def generatekw(context, mapping):
                 return templatekw.shownames(context, mapping,
diff --git a/mercurial/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -29,11 +29,26 @@  from . import (
 _hybrid = templateutil.hybrid
 _mappable = templateutil.mappable
-_showlist = templateutil._showlist
 hybriddict = templateutil.hybriddict
 hybridlist = templateutil.hybridlist
 compatdict = templateutil.compatdict
 compatlist = templateutil.compatlist
+_showcompatlist = templateutil._showcompatlist
+# TODO: temporary hack for porting; will be removed soon
+class _fakecontextwrapper(object):
+    def __init__(self, templ):
+        self._templ = templ
+    def preload(self, t):
+        return t in self._templ
+    def process(self, t, mapping):
+        return self._templ.generatenamed(t, mapping)
+def _showlist(name, values, templ, mapping, plural=None, separator=' '):
+    context = _fakecontextwrapper(templ)
+    return _showcompatlist(context, mapping, name, values, plural, separator)
 def showdict(name, data, mapping, plural=None, key='key', value='value',
              fmt=None, separator=' '):
@@ -203,7 +218,7 @@  def showbranch(context, mapping):
     ctx = context.resource(mapping, 'ctx')
     return ctx.branch()
-@templatekeyword('branches', requires={'ctx', 'templ'})
+@templatekeyword('branches', requires={'ctx'})
 def showbranches(context, mapping):
     """List of strings. The name of the branch on which the
     changeset was committed. Will be empty if the branch name was
@@ -230,7 +245,7 @@  def showbookmarks(context, mapping):
     f = _showlist('bookmark', bookmarks, templ, mapping)
     return _hybrid(f, bookmarks, makemap, pycompat.identity)
-@templatekeyword('children', requires={'ctx', 'templ'})
+@templatekeyword('children', requires={'ctx'})
 def showchildren(context, mapping):
     """List of strings. The children of the changeset."""
     ctx = context.resource(mapping, 'ctx')
@@ -281,7 +296,7 @@  def showdiffstat(context, mapping):
     maxname, maxtotal, adds, removes, binary = patch.diffstatsum(stats)
     return '%d: +%d/-%d' % (len(stats), adds, removes)
-@templatekeyword('envvars', requires={'ui', 'templ'})
+@templatekeyword('envvars', requires={'ui'})
 def showenvvars(context, mapping):
     """A dictionary of environment variables. (EXPERIMENTAL)"""
     ui = context.resource(mapping, 'ui')
@@ -312,13 +327,13 @@  def _showfilesbystat(context, mapping, n
     files = revcache['files'][index]
     return compatlist(context, mapping, name, files, element='file')
-@templatekeyword('file_adds', requires={'repo', 'ctx', 'revcache', 'templ'})
+@templatekeyword('file_adds', requires={'repo', 'ctx', 'revcache'})
 def showfileadds(context, mapping):
     """List of strings. Files added by this changeset."""
     return _showfilesbystat(context, mapping, 'file_add', 1)
-                 requires={'repo', 'ctx', 'cache', 'revcache', 'templ'})
+                 requires={'repo', 'ctx', 'cache', 'revcache'})
 def showfilecopies(context, mapping):
     """List of strings. Files copied in this changeset with
     their sources.
@@ -345,7 +360,7 @@  def showfilecopies(context, mapping):
 # showfilecopiesswitch() displays file copies only if copy records are
 # provided before calling the templater, usually with a --copies
 # command line switch.
-@templatekeyword('file_copies_switch', requires={'revcache', 'templ'})
+@templatekeyword('file_copies_switch', requires={'revcache'})
 def showfilecopiesswitch(context, mapping):
     """List of strings. Like "file_copies" but displayed
     only if the --copied switch is set.
@@ -356,17 +371,17 @@  def showfilecopiesswitch(context, mappin
                       key='name', value='source', fmt='%s (%s)',
-@templatekeyword('file_dels', requires={'repo', 'ctx', 'revcache', 'templ'})
+@templatekeyword('file_dels', requires={'repo', 'ctx', 'revcache'})
 def showfiledels(context, mapping):
     """List of strings. Files removed by this changeset."""
     return _showfilesbystat(context, mapping, 'file_del', 2)
-@templatekeyword('file_mods', requires={'repo', 'ctx', 'revcache', 'templ'})
+@templatekeyword('file_mods', requires={'repo', 'ctx', 'revcache'})
 def showfilemods(context, mapping):
     """List of strings. Files modified by this changeset."""
     return _showfilesbystat(context, mapping, 'file_mod', 0)
-@templatekeyword('files', requires={'ctx', 'templ'})
+@templatekeyword('files', requires={'ctx'})
 def showfiles(context, mapping):
     """List of strings. All files modified, added, or removed by this
@@ -465,11 +480,10 @@  def _showchangessincetag(context, mappin
 # teach templater latesttags.changes is switched to (context, mapping) API
 _showchangessincetag._requires = {'repo', 'ctx'}
-@templatekeyword('manifest', requires={'repo', 'ctx', 'templ'})
+@templatekeyword('manifest', requires={'repo', 'ctx'})
 def showmanifest(context, mapping):
     repo = context.resource(mapping, 'repo')
     ctx = context.resource(mapping, 'ctx')
-    templ = context.resource(mapping, 'templ')
     mnode = ctx.manifestnode()
     if mnode is None:
         # just avoid crash, we might want to use the 'ff...' hash in future
@@ -478,7 +492,7 @@  def showmanifest(context, mapping):
     mhex = hex(mnode)
     mapping = mapping.copy()
     mapping.update({'rev': mrev, 'node': mhex})
-    f = templ.generate('manifest', mapping)
+    f = context.process('manifest', mapping)
     # TODO: perhaps 'ctx' should be dropped from mapping because manifest
     # rev and node are completely different from changeset's.
     return _mappable(f, None, f, lambda x: {'rev': mrev, 'node': mhex})
@@ -738,7 +752,7 @@  def showrevslist(context, mapping, name,
                    lambda x: {name: x, 'ctx': repo[x], 'revcache': {}},
                    pycompat.identity, keytype=int)
-@templatekeyword('subrepos', requires={'ctx', 'templ'})
+@templatekeyword('subrepos', requires={'ctx'})
 def showsubrepos(context, mapping):
     """List of strings. Updated subrepositories in the changeset."""
     ctx = context.resource(mapping, 'ctx')
@@ -758,7 +772,7 @@  def showsubrepos(context, mapping):
 # don't remove "showtags" definition, even though namespaces will put
 # a helper function for "tags" keyword into "keywords" map automatically,
 # because online help text is built without namespaces initialization
-@templatekeyword('tags', requires={'repo', 'ctx', 'templ'})
+@templatekeyword('tags', requires={'repo', 'ctx'})
 def showtags(context, mapping):
     """List of strings. Any tags associated with the changeset."""
     return shownames(context, mapping, 'tags')
@@ -769,7 +783,7 @@  def showtermwidth(context, mapping):
     ui = context.resource(mapping, 'ui')
     return ui.termwidth()
-@templatekeyword('instabilities', requires={'ctx', 'templ'})
+@templatekeyword('instabilities', requires={'ctx'})
 def showinstabilities(context, mapping):
     """List of strings. Evolution instabilities affecting the changeset.
diff --git a/mercurial/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -148,8 +148,7 @@  def compatdict(context, mapping, name, d
     hybriddict() for new template keywords.
     c = [{key: k, value: v} for k, v in data.iteritems()]
-    t = context.resource(mapping, 'templ')
-    f = _showlist(name, c, t, mapping, plural, separator)
+    f = _showcompatlist(context, mapping, name, c, plural, separator)
     return hybriddict(data, key=key, value=value, fmt=fmt, gen=f)
 def compatlist(context, mapping, name, data, element=None, fmt=None,
@@ -159,12 +158,12 @@  def compatlist(context, mapping, name, d
     This exists for backward compatibility with the old-style template. Use
     hybridlist() for new template keywords.
-    t = context.resource(mapping, 'templ')
-    f = _showlist(name, data, t, mapping, plural, separator)
+    f = _showcompatlist(context, mapping, name, data, plural, separator)
     return hybridlist(data, name=element or name, fmt=fmt, gen=f)
-def _showlist(name, values, templ, mapping, plural=None, separator=' '):
-    '''expand set of values.
+def _showcompatlist(context, mapping, name, values, plural=None, separator=' '):
+    """Return a generator that renders old-style list template
     name is name of key in template map.
     values is list of strings or dicts.
     plural is plural of name, if not simply name + 's'.
@@ -183,15 +182,15 @@  def _showlist(name, values, templ, mappi
     map, expand it instead of 'foo' for last key.
     expand 'end_foos'.
-    '''
+    """
     if not plural:
         plural = name + 's'
     if not values:
         noname = 'no_' + plural
-        if noname in templ:
-            yield templ.generate(noname, mapping)
+        if context.preload(noname):
+            yield context.process(noname, mapping)
-    if name not in templ:
+    if not context.preload(name):
         if isinstance(values[0], bytes):
             yield separator.join(values)
@@ -201,8 +200,8 @@  def _showlist(name, values, templ, mappi
                 yield r
     startname = 'start_' + plural
-    if startname in templ:
-        yield templ.generate(startname, mapping)
+    if context.preload(startname):
+        yield context.process(startname, mapping)
     vmapping = mapping.copy()
     def one(v, tag=name):
@@ -217,9 +216,9 @@  def _showlist(name, values, templ, mappi
                     vmapping[a] = b
             except (TypeError, ValueError):
                 vmapping[name] = v
-        return templ.generate(tag, vmapping)
+        return context.process(tag, vmapping)
     lastname = 'last_' + name
-    if lastname in templ:
+    if context.preload(lastname):
         last = values.pop()
         last = None
@@ -228,8 +227,8 @@  def _showlist(name, values, templ, mappi
     if last is not None:
         yield one(last, tag=lastname)
     endname = 'end_' + plural
-    if endname in templ:
-        yield templ.generate(endname, mapping)
+    if context.preload(endname):
+        yield context.process(endname, mapping)
 def stringify(thing):
     """Turn values into bytes by converting into text and concatenating them"""