Patchwork [3,of,4] templater: make pad() evaluate boolean argument (BC)

login
register
mail settings
Submitter Yuya Nishihara
Date Aug. 21, 2016, 2:53 p.m.
Message ID <741d99b3efd996c3e3d9.1471791237@mimosa>
Download mbox | patch
Permalink /patch/16374/
State Accepted
Headers show

Comments

Yuya Nishihara - Aug. 21, 2016, 2:53 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1461328153 -32400
#      Fri Apr 22 21:29:13 2016 +0900
# Node ID 741d99b3efd996c3e3d96a4a7d452b23d38fb74c
# Parent  dfc5b7368fa170e8535202bc4c7057f53d65d3ff
templater: make pad() evaluate boolean argument (BC)

Otherwise it would crash if template expression was passed.

This patch unifies the way how boolean expression is evaluated, which involves
BC. Before "if(true)" and "pad(..., 'false')" were False, which are now True
since they are boolean literal and non-empty string respectively.

"func is runsymbol" is the same hack as evalstringliteral(), which is needed
for label() to take color literals.

Patch

diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -290,8 +290,15 @@  def evalfuncarg(context, mapping, arg):
     return thing
 
 def evalboolean(context, mapping, arg):
+    """Evaluate given argument as boolean, but also takes boolean literals"""
     func, data = arg
-    thing = func(context, mapping, data)
+    if func is runsymbol:
+        thing = func(context, mapping, data, default=None)
+        if thing is None:
+            # not a template keyword, takes as a boolean literal
+            thing = util.parsebool(data)
+    else:
+        thing = func(context, mapping, data)
     if isinstance(thing, bool):
         return thing
     # other objects are evaluated as strings, which means 0 is True, but
@@ -516,7 +523,7 @@  def pad(context, mapping, args):
     if len(args) > 2:
         fillchar = evalstring(context, mapping, args[2])
     if len(args) > 3:
-        right = util.parsebool(args[3][1])
+        right = evalboolean(context, mapping, args[3])
 
     if right:
         return text.rjust(width, fillchar)
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
@@ -3323,6 +3323,27 @@  Test width argument passed to pad functi
   hg: parse error: pad() expects an integer width
   [255]
 
+Test boolean argument passed to pad function
+
+ no crash
+
+  $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n'
+  ---------0
+
+ string/literal
+
+  $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n'
+  ---------0
+  $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n'
+  0---------
+  $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n'
+  0---------
+
+ unknown keyword is evaluated to ''
+
+  $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n'
+  0---------
+
 Test separate function
 
   $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n'
@@ -3332,6 +3353,23 @@  Test separate function
   $ hg log -r 0 --color=always -T '{separate(" ", "a", label(red, "b"), "c", label(red, ""), "d")}\n'
   a \x1b[0;31mb\x1b[0m c d (esc)
 
+Test boolean expression/literal passed to if function
+
+  $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n'
+  rev 0 is True
+  $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n'
+  literal 0 is True as well
+  $ hg log -r 0 -T '{if("", "", "empty string is False")}\n'
+  empty string is False
+  $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n'
+  empty list is False
+  $ hg log -r 0 -T '{if(true, "true is True")}\n'
+  true is True
+  $ hg log -r 0 -T '{if(false, "", "false is False")}\n'
+  false is False
+  $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n'
+  non-empty string is True
+
 Test ifcontains function
 
   $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n'