templater: add wrapped types for pure non-list/dict values
authorYuya Nishihara <yuya@tcha.org>
Tue, 20 Mar 2018 23:56:26 +0900
changeset 38238 7824783a6d5e
parent 38237 d48b80d58848
child 38239 1c8098cf560a
templater: add wrapped types for pure non-list/dict values These wrapper types will allow us to get rid of some isinstance() business. A bytes object needs to support sequence-like operations (e.g. join(), ifcontains(), etc.) That's why we have two wrapper classes. Tests will be added later.
mercurial/templater.py
mercurial/templateutil.py
--- a/mercurial/templater.py	Wed Apr 04 21:06:14 2018 +0900
+++ b/mercurial/templater.py	Tue Mar 20 23:56:26 2018 +0900
@@ -32,6 +32,9 @@
 True, False, int, float
     can be stringified as such.
 
+wrappedbytes, wrappedvalue
+    a wrapper for the above printable types.
+
 date tuple
     a (unixtime, offset) tuple, which produces no meaningful output by itself.
 
--- a/mercurial/templateutil.py	Wed Apr 04 21:06:14 2018 +0900
+++ b/mercurial/templateutil.py	Tue Mar 20 23:56:26 2018 +0900
@@ -66,6 +66,44 @@
         A returned value must be serializable by templaterfilters.json().
         """
 
+class wrappedbytes(wrapped):
+    """Wrapper for byte string"""
+
+    def __init__(self, value):
+        self._value = value
+
+    def itermaps(self, context):
+        raise error.ParseError(_('%r is not iterable of mappings')
+                               % pycompat.bytestr(self._value))
+
+    def join(self, context, mapping, sep):
+        return joinitems(pycompat.iterbytestr(self._value), sep)
+
+    def show(self, context, mapping):
+        return self._value
+
+    def tovalue(self, context, mapping):
+        return self._value
+
+class wrappedvalue(wrapped):
+    """Generic wrapper for pure non-list/dict/bytes value"""
+
+    def __init__(self, value):
+        self._value = value
+
+    def itermaps(self, context):
+        raise error.ParseError(_('%r is not iterable of mappings')
+                               % self._value)
+
+    def join(self, context, mapping, sep):
+        raise error.ParseError(_('%r is not iterable') % self._value)
+
+    def show(self, context, mapping):
+        return pycompat.bytestr(self._value)
+
+    def tovalue(self, context, mapping):
+        return self._value
+
 # stub for representing a date type; may be a real date type that can
 # provide a readable string value
 class date(object):
@@ -447,6 +485,20 @@
     func, data = arg
     return func(context, mapping, data)
 
+def evalwrapped(context, mapping, arg):
+    """Evaluate given argument to wrapped object"""
+    thing = evalrawexp(context, mapping, arg)
+    return makewrapped(context, mapping, thing)
+
+def makewrapped(context, mapping, thing):
+    """Lift object to a wrapped type"""
+    if isinstance(thing, wrapped):
+        return thing
+    thing = _unthunk(context, mapping, thing)
+    if isinstance(thing, bytes):
+        return wrappedbytes(thing)
+    return wrappedvalue(thing)
+
 def evalfuncarg(context, mapping, arg):
     """Evaluate given argument as value type"""
     return unwrapvalue(context, mapping, evalrawexp(context, mapping, arg))