Patchwork [4,of,6] templater: always join() over a wrapped object (BC)

login
register
mail settings
Submitter Yuya Nishihara
Date June 4, 2018, 1:10 p.m.
Message ID <15bb4fc7c5d6190351bb.1528117811@mimosa>
Download mbox | patch
Permalink /patch/31963/
State Accepted
Headers show

Comments

Yuya Nishihara - June 4, 2018, 1:10 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1524297621 -32400
#      Sat Apr 21 17:00:21 2018 +0900
# Node ID 15bb4fc7c5d6190351bb891e29f612e749d1e2e5
# Parent  530b24e26effdc6f4611e444d8f41c6bf88e706b
templater: always join() over a wrapped object (BC)

This is a behavior change in a sense that join() of a byte string is no
longer "implementation dependent." Before, if a byte string was backed by
a lazy generator, join() would concatenate each chunk with the specified
separator, which seems wrong. The new behavior is always join() each byte.

TypeError on join() over uniterable is also fixed.

Patch

diff --git a/mercurial/templatefuncs.py b/mercurial/templatefuncs.py
--- a/mercurial/templatefuncs.py
+++ b/mercurial/templatefuncs.py
@@ -36,6 +36,7 @@  from .utils import (
 )
 
 evalrawexp = templateutil.evalrawexp
+evalwrapped = templateutil.evalwrapped
 evalfuncarg = templateutil.evalfuncarg
 evalboolean = templateutil.evalboolean
 evaldate = templateutil.evaldate
@@ -327,17 +328,11 @@  def join(context, mapping, args):
         # i18n: "join" is a keyword
         raise error.ParseError(_("join expects one or two arguments"))
 
-    joinset = evalrawexp(context, mapping, args[0])
+    joinset = evalwrapped(context, mapping, args[0])
     joiner = " "
     if len(args) > 1:
         joiner = evalstring(context, mapping, args[1])
-    if isinstance(joinset, templateutil.wrapped):
-        return joinset.join(context, mapping, joiner)
-    # TODO: rethink about join() of a byte string, which had no defined
-    # behavior since a string may be either a bytes or a generator.
-    # TODO: fix type error on join() of non-iterable
-    joinset = templateutil.unwrapvalue(context, mapping, joinset)
-    return templateutil.joinitems(pycompat.maybebytestr(joinset), joiner)
+    return joinset.join(context, mapping, joiner)
 
 @templatefunc('label(label, expr)')
 def label(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
@@ -3249,6 +3249,17 @@  Test manifest/get() can be join()-ed as 
   $ hg log -R latesttag -r tip -T '{join(get(extras, "branch"), "")}\n'
   default
 
+Test join() over string
+
+  $ hg log -R latesttag -r tip -T '{join(rev|stringify, ".")}\n'
+  1.1
+
+Test join() over uniterable
+
+  $ hg log -R latesttag -r tip -T '{join(rev, "")}\n'
+  hg: parse error: 11 is not iterable
+  [255]
+
 Test min/max of integers
 
   $ hg log -R latesttag -l1 -T '{min(revset("9:10"))}\n'