Patchwork [1,of,8] templater: pass context down to unwraphybrid()

login
register
mail settings
Submitter Yuya Nishihara
Date April 1, 2018, 2:45 a.m.
Message ID <529838f7858556c0f9d1.1522550758@mimosa>
Download mbox | patch
Permalink /patch/30088/
State Deferred
Headers show

Comments

Yuya Nishihara - April 1, 2018, 2:45 a.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1521284945 -32400
#      Sat Mar 17 20:09:05 2018 +0900
# Node ID 529838f7858556c0f9d180a276b483578c6d55d4
# Parent  02079695afdd39020b13f238c69691dce14b932f
templater: pass context down to unwraphybrid()

See the subsequent patches for why.

Unlike eval*() functions, flatten(), stringify(), and unwraphybrid() don't
take a mapping since these functions may be applied to a tree of generators
where each node should be bound to the mapping when it was evaluated.
Yuya Nishihara - April 2, 2018, 9:45 a.m.
On Sun, 01 Apr 2018 11:45:58 +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1521284945 -32400
> #      Sat Mar 17 20:09:05 2018 +0900
> # Node ID 529838f7858556c0f9d180a276b483578c6d55d4
> # Parent  02079695afdd39020b13f238c69691dce14b932f
> templater: pass context down to unwraphybrid()

Please disregard this series.

> Unlike eval*() functions, flatten(), stringify(), and unwraphybrid() don't
> take a mapping since these functions may be applied to a tree of generators
> where each node should be bound to the mapping when it was evaluated.

Rethinking about it, this appears not to actually matter. And the mapping dict
will be necessary to fix a web template bug.

Patch

diff --git a/mercurial/templatefuncs.py b/mercurial/templatefuncs.py
--- a/mercurial/templatefuncs.py
+++ b/mercurial/templatefuncs.py
@@ -283,7 +283,7 @@  def ifcontains(context, mapping, args):
     keytype = getattr(haystack, 'keytype', None)
     try:
         needle = evalrawexp(context, mapping, args[0])
-        needle = templateutil.unwrapastype(needle, keytype or bytes)
+        needle = templateutil.unwrapastype(context, needle, keytype or bytes)
         found = (needle in haystack)
     except error.ParseError:
         found = False
diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -679,7 +679,7 @@  class engine(object):
         if extramapping:
             extramapping.update(mapping)
             mapping = extramapping
-        return templateutil.flatten(func(self, mapping, data))
+        return templateutil.flatten(self, func(self, mapping, data))
 
 engines = {'default': engine}
 
diff --git a/mercurial/templateutil.py b/mercurial/templateutil.py
--- a/mercurial/templateutil.py
+++ b/mercurial/templateutil.py
@@ -120,7 +120,7 @@  def hybridlist(data, name, fmt=None, gen
         prefmt = pycompat.bytestr
     return hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % prefmt(x))
 
-def unwraphybrid(thing):
+def unwraphybrid(context, thing):
     """Return an object which can be stringified possibly by using a legacy
     template"""
     gen = getattr(thing, 'gen', None)
@@ -241,9 +241,9 @@  def _showcompatlist(context, mapping, na
     if context.preload(endname):
         yield context.process(endname, mapping)
 
-def flatten(thing):
+def flatten(context, thing):
     """Yield a single stream from a possibly nested set of iterators"""
-    thing = unwraphybrid(thing)
+    thing = unwraphybrid(context, thing)
     if isinstance(thing, bytes):
         yield thing
     elif isinstance(thing, str):
@@ -257,7 +257,7 @@  def flatten(thing):
         yield pycompat.bytestr(thing)
     else:
         for i in thing:
-            i = unwraphybrid(i)
+            i = unwraphybrid(context, i)
             if isinstance(i, bytes):
                 yield i
             elif i is None:
@@ -265,14 +265,14 @@  def flatten(thing):
             elif not util.safehasattr(i, '__iter__'):
                 yield pycompat.bytestr(i)
             else:
-                for j in flatten(i):
+                for j in flatten(context, i):
                     yield j
 
-def stringify(thing):
+def stringify(context, thing):
     """Turn values into bytes by converting into text and concatenating them"""
     if isinstance(thing, bytes):
         return thing  # retain localstr to be round-tripped
-    return b''.join(flatten(thing))
+    return b''.join(flatten(context, thing))
 
 def findsymbolicname(arg):
     """Find symbolic name for the given compiled expression; returns None
@@ -294,17 +294,17 @@  def evalrawexp(context, mapping, arg):
 
 def evalfuncarg(context, mapping, arg):
     """Evaluate given argument as value type"""
-    return _unwrapvalue(evalrawexp(context, mapping, arg))
+    return _unwrapvalue(context, evalrawexp(context, mapping, arg))
 
 # TODO: unify this with unwrapvalue() once the bug of templatefunc.join()
 # is fixed. we can't do that right now because join() has to take a generator
 # of byte strings as it is, not a lazy byte string.
-def _unwrapvalue(thing):
+def _unwrapvalue(context, thing):
     thing = unwrapvalue(thing)
     # evalrawexp() may return string, generator of strings or arbitrary object
     # such as date tuple, but filter does not want generator.
     if isinstance(thing, types.GeneratorType):
-        thing = stringify(thing)
+        thing = stringify(context, thing)
     return thing
 
 def evalboolean(context, mapping, arg):
@@ -322,15 +322,15 @@  def evalboolean(context, mapping, arg):
         return thing
     # other objects are evaluated as strings, which means 0 is True, but
     # empty dict/list should be False as they are expected to be ''
-    return bool(stringify(thing))
+    return bool(stringify(context, thing))
 
 def evaldate(context, mapping, arg, err=None):
     """Evaluate given argument as a date tuple or a date string; returns
     a (unixtime, offset) tuple"""
-    return unwrapdate(evalrawexp(context, mapping, arg), err)
+    return unwrapdate(context, evalrawexp(context, mapping, arg), err)
 
-def unwrapdate(thing, err=None):
-    thing = _unwrapvalue(thing)
+def unwrapdate(context, thing, err=None):
+    thing = _unwrapvalue(context, thing)
     try:
         return dateutil.parsedate(thing)
     except AttributeError:
@@ -341,17 +341,17 @@  def unwrapdate(thing, err=None):
         raise error.ParseError(err)
 
 def evalinteger(context, mapping, arg, err=None):
-    return unwrapinteger(evalrawexp(context, mapping, arg), err)
+    return unwrapinteger(context, evalrawexp(context, mapping, arg), err)
 
-def unwrapinteger(thing, err=None):
-    thing = _unwrapvalue(thing)
+def unwrapinteger(context, thing, err=None):
+    thing = _unwrapvalue(context, thing)
     try:
         return int(thing)
     except (TypeError, ValueError):
         raise error.ParseError(err or _('not an integer'))
 
 def evalstring(context, mapping, arg):
-    return stringify(evalrawexp(context, mapping, arg))
+    return stringify(context, evalrawexp(context, mapping, arg))
 
 def evalstringliteral(context, mapping, arg):
     """Evaluate given argument as string template, but returns symbol name
@@ -361,7 +361,7 @@  def evalstringliteral(context, mapping, 
         thing = func(context, mapping, data, default=data)
     else:
         thing = func(context, mapping, data)
-    return stringify(thing)
+    return stringify(context, thing)
 
 _unwrapfuncbytype = {
     None: _unwrapvalue,
@@ -370,13 +370,13 @@  def evalstringliteral(context, mapping, 
     int: unwrapinteger,
 }
 
-def unwrapastype(thing, typ):
+def unwrapastype(context, thing, typ):
     """Move the inner value object out of the wrapper and coerce its type"""
     try:
         f = _unwrapfuncbytype[typ]
     except KeyError:
         raise error.ProgrammingError('invalid type specified: %r' % typ)
-    return f(thing)
+    return f(context, thing)
 
 def runinteger(context, mapping, data):
     return int(data)
@@ -426,7 +426,7 @@  def runfilter(context, mapping, data):
     arg, filt = data
     thing = evalrawexp(context, mapping, arg)
     try:
-        thing = unwrapastype(thing, getattr(filt, '_intype', None))
+        thing = unwrapastype(context, thing, getattr(filt, '_intype', None))
         return filt(thing)
     except error.ParseError as e:
         raise error.ParseError(bytes(e), hint=_formatfiltererror(arg, filt))
diff --git a/tests/test-template-engine.t b/tests/test-template-engine.t
--- a/tests/test-template-engine.t
+++ b/tests/test-template-engine.t
@@ -24,7 +24,7 @@ 
   >                 v = v(**pycompat.strkwargs(props))
   >             elif callable(v):
   >                 v = v(self, props)
-  >             v = templateutil.stringify(v)
+  >             v = templateutil.stringify(self, v)
   >             tmpl = tmpl.replace(b'{{%s}}' % k, v)
   >         yield tmpl
   >