Patchwork [8,of,8] templater: wrap get/min/max result so map operation can apply

login
register
mail settings
Submitter Yuya Nishihara
Date Sept. 24, 2017, 12:21 p.m.
Message ID <ae5017a7b3753295d946.1506255717@mimosa>
Download mbox | patch
Permalink /patch/24134/
State Accepted
Headers show

Comments

Yuya Nishihara - Sept. 24, 2017, 12:21 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1504952005 -32400
#      Sat Sep 09 19:13:25 2017 +0900
# Node ID ae5017a7b3753295d946a13421eecf65e675f3c1
# Parent  a0047461f18c7d3d341bcecda6975bb58b53651b
templater: wrap get/min/max result so map operation can apply

See the test for usage example.

wraphybridvalue() takes a key/value pair because a hybrid dict passes a key
to its makemap() function. Since makemap() of showmanifest() doesn't need
a key, it's set to None.
Yuya Nishihara - Sept. 24, 2017, 12:36 p.m.
On Sun, 24 Sep 2017 21:21:57 +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1504952005 -32400
> #      Sat Sep 09 19:13:25 2017 +0900
> # Node ID ae5017a7b3753295d946a13421eecf65e675f3c1
> # Parent  a0047461f18c7d3d341bcecda6975bb58b53651b
> templater: wrap get/min/max result so map operation can apply

Please disregard this, the last patch.

> --- a/mercurial/templater.py
> +++ b/mercurial/templater.py
> @@ -713,7 +713,10 @@ def get(context, mapping, args):
>          raise error.ParseError(_("get() expects a dict as first argument"))
>  
>      key = evalfuncarg(context, mapping, args[1])
> -    return dictarg.get(key)
> +    val = dictarg.get(key)
> +    if val is None:
> +        return
> +    return templatekw.wraphybridvalue(dictarg, key, dictarg.get(key))

s/dictarg.get(key)/val/

I'll include the fixed version in the next series.

Patch

diff --git a/mercurial/templatekw.py b/mercurial/templatekw.py
--- a/mercurial/templatekw.py
+++ b/mercurial/templatekw.py
@@ -81,13 +81,22 @@  class _mappable(object):
     value. Use unwrapvalue() or unwraphybrid() to obtain the inner object.
     """
 
-    def __init__(self, gen, value, makemap):
-        self.gen = gen
+    def __init__(self, gen, key, value, makemap):
+        if gen is not None:
+            self.gen = gen
+        self._key = key
         self._value = value  # may be generator of strings
         self._makemap = makemap
 
+    @util.propertycache
+    def gen(self):
+        return self._defaultgen()
+
+    def _defaultgen(self):
+        yield pycompat.bytestr(self._value)
+
     def tomap(self):
-        return self._makemap()
+        return self._makemap(self._key)
 
     def itermaps(self):
         yield self.tomap()
@@ -114,6 +123,20 @@  def unwrapvalue(thing):
         return thing
     return thing._value
 
+def wraphybridvalue(container, key, value):
+    """Wrap an element of hybrid container to be mappable
+
+    The key is passed to the makemap function of the given container. It
+    should be identical to the value if the given container is a list.
+    """
+    makemap = getattr(container, '_makemap', None)
+    if not makemap:
+        return value
+    if util.safehasattr(value, '_makemap'):
+        # a nested hybrid list/dict, which has its own way of map operation
+        return value
+    return _mappable(None, key, value, makemap)
+
 def showdict(name, data, mapping, plural=None, key='key', value='value',
              fmt='%s=%s', separator=' '):
     c = [{key: k, value: v} for k, v in data.iteritems()]
@@ -578,7 +601,7 @@  def showmanifest(**args):
     f = templ('manifest', **args)
     # TODO: perhaps 'ctx' should be dropped from mapping because manifest
     # rev and node are completely different from changeset's.
-    return _mappable(f, f, lambda: {'rev': mrev, 'node': mhex})
+    return _mappable(f, None, f, lambda x: {'rev': mrev, 'node': mhex})
 
 def shownames(namespace, **args):
     """helper method to generate a template keyword for a namespace"""
diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -713,7 +713,10 @@  def get(context, mapping, args):
         raise error.ParseError(_("get() expects a dict as first argument"))
 
     key = evalfuncarg(context, mapping, args[1])
-    return dictarg.get(key)
+    val = dictarg.get(key)
+    if val is None:
+        return
+    return templatekw.wraphybridvalue(dictarg, key, dictarg.get(key))
 
 @templatefunc('if(expr, then[, else])')
 def if_(context, mapping, args):
@@ -857,10 +860,11 @@  def max_(context, mapping, args, **kwarg
 
     iterable = evalfuncarg(context, mapping, args[0])
     try:
-        return max(iterable)
+        v = max(iterable)
     except (TypeError, ValueError):
         # i18n: "max" is a keyword
         raise error.ParseError(_("max first argument should be an iterable"))
+    return templatekw.wraphybridvalue(iterable, v, v)
 
 @templatefunc('min(iterable)')
 def min_(context, mapping, args, **kwargs):
@@ -871,10 +875,11 @@  def min_(context, mapping, args, **kwarg
 
     iterable = evalfuncarg(context, mapping, args[0])
     try:
-        return min(iterable)
+        v = min(iterable)
     except (TypeError, ValueError):
         # i18n: "min" is a keyword
         raise error.ParseError(_("min first argument should be an iterable"))
+    return templatekw.wraphybridvalue(iterable, v, v)
 
 @templatefunc('mod(a, b)')
 def mod(context, mapping, args):
diff --git a/tests/test-command-template.t b/tests/test-command-template.t
--- a/tests/test-command-template.t
+++ b/tests/test-command-template.t
@@ -3128,10 +3128,24 @@  Test new-style inline templating of non-
   $ hg log -R latesttag -r tip -T '{manifest % "{rev}:{node}"}\n'
   11:2bc6e9006ce29882383a22d39fd1f4e66dd3e2fc
 
-Test manifest can be join()-ed as before, though it's silly:
+  $ hg log -R latesttag -r tip -T '{get(extras, "branch") % "{key}: {value}\n"}'
+  branch: default
+  $ hg log -R latesttag -r tip -T '{get(extras, "unknown") % "{key}\n"}'
+  hg: parse error: None is not iterable
+  [255]
+  $ hg log -R latesttag -r tip -T '{min(extras) % "{key}: {value}\n"}'
+  branch: default
+  $ hg log -R latesttag -l1 -T '{min(revset("0:9")) % "{rev}:{node|short}\n"}'
+  0:ce3cec86e6c2
+  $ hg log -R latesttag -l1 -T '{max(revset("0:9")) % "{rev}:{node|short}\n"}'
+  9:fbc7cd862e9c
+
+Test manifest/get() can be join()-ed as before, though it's silly:
 
   $ hg log -R latesttag -r tip -T '{join(manifest, "")}\n'
   11:2bc6e9006ce2
+  $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), "")}\n'
+  default
 
 Test the sub function of templating for expansion: