templatefilters: declare input type as bytes where appropriate
authorYuya Nishihara <yuya@tcha.org>
Sun, 18 Mar 2018 15:42:28 +0900
changeset 37223 08e042f0a67c
parent 37222 54355c243042
child 37224 67efce231633
templatefilters: declare input type as bytes where appropriate Some test outputs changed since input is now coerced to a byte string. I think that's okay. Maybe {date} should have some readable representation?
mercurial/hgweb/hgweb_mod.py
mercurial/templatefilters.py
tests/test-command-template.t
--- a/mercurial/hgweb/hgweb_mod.py	Sun Mar 18 15:14:58 2018 +0900
+++ b/mercurial/hgweb/hgweb_mod.py	Sun Mar 18 15:42:28 2018 +0900
@@ -173,7 +173,7 @@
 
         filters = {}
         templatefilter = registrar.templatefilter(filters)
-        @templatefilter('websub')
+        @templatefilter('websub', intype=bytes)
         def websubfilter(text):
             return templatefilters.websub(text, self.websubtable)
 
--- a/mercurial/templatefilters.py	Sun Mar 18 15:14:58 2018 +0900
+++ b/mercurial/templatefilters.py	Sun Mar 18 15:42:28 2018 +0900
@@ -40,7 +40,7 @@
 
 templatefilter = registrar.templatefilter(filters)
 
-@templatefilter('addbreaks')
+@templatefilter('addbreaks', intype=bytes)
 def addbreaks(text):
     """Any text. Add an XHTML "<br />" tag before the end of
     every line except the last.
@@ -90,7 +90,7 @@
                 return '%s from now' % fmt(t, n, a)
             return '%s ago' % fmt(t, n, a)
 
-@templatefilter('basename')
+@templatefilter('basename', intype=bytes)
 def basename(path):
     """Any text. Treats the text as a path, and returns the last
     component of the path after splitting by the path separator.
@@ -103,14 +103,14 @@
     """List or text. Returns the length as an integer."""
     return len(i)
 
-@templatefilter('dirname')
+@templatefilter('dirname', intype=bytes)
 def dirname(path):
     """Any text. Treats the text as a path, and strips the last
     component of the path after splitting by the path separator.
     """
     return os.path.dirname(path)
 
-@templatefilter('domain')
+@templatefilter('domain', intype=bytes)
 def domain(author):
     """Any text. Finds the first string that looks like an email
     address, and extracts just the domain component. Example: ``User
@@ -125,7 +125,7 @@
         author = author[:f]
     return author
 
-@templatefilter('email')
+@templatefilter('email', intype=bytes)
 def email(text):
     """Any text. Extracts the first string that looks like an email
     address. Example: ``User <user@example.com>`` becomes
@@ -133,7 +133,7 @@
     """
     return stringutil.email(text)
 
-@templatefilter('escape')
+@templatefilter('escape', intype=bytes)
 def escape(text):
     """Any text. Replaces the special XML/XHTML characters "&", "<"
     and ">" with XML entities, and filters out NUL characters.
@@ -170,17 +170,17 @@
                                     width, initindent, hangindent) + rest
                     for para, rest in findparas()])
 
-@templatefilter('fill68')
+@templatefilter('fill68', intype=bytes)
 def fill68(text):
     """Any text. Wraps the text to fit in 68 columns."""
     return fill(text, 68)
 
-@templatefilter('fill76')
+@templatefilter('fill76', intype=bytes)
 def fill76(text):
     """Any text. Wraps the text to fit in 76 columns."""
     return fill(text, 76)
 
-@templatefilter('firstline')
+@templatefilter('firstline', intype=bytes)
 def firstline(text):
     """Any text. Returns the first line of text."""
     try:
@@ -188,7 +188,7 @@
     except IndexError:
         return ''
 
-@templatefilter('hex')
+@templatefilter('hex', intype=bytes)
 def hexfilter(text):
     """Any text. Convert a binary Mercurial node identifier into
     its long hexadecimal representation.
@@ -262,17 +262,17 @@
     else:
         raise TypeError('cannot encode type %s' % obj.__class__.__name__)
 
-@templatefilter('lower')
+@templatefilter('lower', intype=bytes)
 def lower(text):
     """Any text. Converts the text to lowercase."""
     return encoding.lower(text)
 
-@templatefilter('nonempty')
+@templatefilter('nonempty', intype=bytes)
 def nonempty(text):
     """Any text. Returns '(none)' if the string is empty."""
     return text or "(none)"
 
-@templatefilter('obfuscate')
+@templatefilter('obfuscate', intype=bytes)
 def obfuscate(text):
     """Any text. Returns the input text rendered as a sequence of
     XML entities.
@@ -280,7 +280,7 @@
     text = unicode(text, pycompat.sysstr(encoding.encoding), r'replace')
     return ''.join(['&#%d;' % ord(c) for c in text])
 
-@templatefilter('permissions')
+@templatefilter('permissions', intype=bytes)
 def permissions(flags):
     if "l" in flags:
         return "lrwxrwxrwx"
@@ -288,14 +288,14 @@
         return "-rwxr-xr-x"
     return "-rw-r--r--"
 
-@templatefilter('person')
+@templatefilter('person', intype=bytes)
 def person(author):
     """Any text. Returns the name before an email address,
     interpreting it as per RFC 5322.
     """
     return stringutil.person(author)
 
-@templatefilter('revescape')
+@templatefilter('revescape', intype=bytes)
 def revescape(text):
     """Any text. Escapes all "special" characters, except @.
     Forward slashes are escaped twice to prevent web servers from prematurely
@@ -317,14 +317,14 @@
     """
     return dateutil.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
 
-@templatefilter('short')
+@templatefilter('short', intype=bytes)
 def short(text):
     """Changeset hash. Returns the short form of a changeset hash,
     i.e. a 12 hexadecimal digit string.
     """
     return text[:12]
 
-@templatefilter('shortbisect')
+@templatefilter('shortbisect', intype=bytes)
 def shortbisect(label):
     """Any text. Treats `label` as a bisection status, and
     returns a single-character representing the status (G: good, B: bad,
@@ -340,17 +340,17 @@
     """Date. Returns a date like "2006-09-18"."""
     return dateutil.shortdate(text)
 
-@templatefilter('slashpath')
+@templatefilter('slashpath', intype=bytes)
 def slashpath(path):
     """Any text. Replaces the native path separator with slash."""
     return util.pconvert(path)
 
-@templatefilter('splitlines')
+@templatefilter('splitlines', intype=bytes)
 def splitlines(text):
     """Any text. Split text into a list of lines."""
     return templateutil.hybridlist(text.splitlines(), name='line')
 
-@templatefilter('stringescape')
+@templatefilter('stringescape', intype=bytes)
 def stringescape(text):
     return stringutil.escapestr(text)
 
@@ -361,7 +361,7 @@
     """
     return thing  # coerced by the intype
 
-@templatefilter('stripdir')
+@templatefilter('stripdir', intype=bytes)
 def stripdir(text):
     """Treat the text as path and strip a directory level, if
     possible. For example, "foo" and "foo/bar" becomes "foo".
@@ -372,42 +372,42 @@
     else:
         return dir
 
-@templatefilter('tabindent')
+@templatefilter('tabindent', intype=bytes)
 def tabindent(text):
     """Any text. Returns the text, with every non-empty line
     except the first starting with a tab character.
     """
     return indent(text, '\t')
 
-@templatefilter('upper')
+@templatefilter('upper', intype=bytes)
 def upper(text):
     """Any text. Converts the text to uppercase."""
     return encoding.upper(text)
 
-@templatefilter('urlescape')
+@templatefilter('urlescape', intype=bytes)
 def urlescape(text):
     """Any text. Escapes all "special" characters. For example,
     "foo bar" becomes "foo%20bar".
     """
     return urlreq.quote(text)
 
-@templatefilter('user')
+@templatefilter('user', intype=bytes)
 def userfilter(text):
     """Any text. Returns a short representation of a user name or email
     address."""
     return stringutil.shortuser(text)
 
-@templatefilter('emailuser')
+@templatefilter('emailuser', intype=bytes)
 def emailuser(text):
     """Any text. Returns the user portion of an email address."""
     return stringutil.emailuser(text)
 
-@templatefilter('utf8')
+@templatefilter('utf8', intype=bytes)
 def utf8(text):
     """Any text. Converts from the local character encoding to UTF-8."""
     return encoding.fromlocal(text)
 
-@templatefilter('xmlescape')
+@templatefilter('xmlescape', intype=bytes)
 def xmlescape(text):
     text = (text
             .replace('&', '&amp;')
--- a/tests/test-command-template.t	Sun Mar 18 15:14:58 2018 +0900
+++ b/tests/test-command-template.t	Sun Mar 18 15:42:28 2018 +0900
@@ -2284,8 +2284,7 @@
   $ hg log -r0 --template '{author|lower}\n'
   user name <user@hostname>
   $ hg log -r0 --template '{date|upper}\n'
-  abort: template filter 'upper' is not compatible with keyword 'date'
-  [255]
+  1000000.00
 
 Add a commit that does all possible modifications at once
 
@@ -2797,11 +2796,12 @@
   hg: parse error: missing argument
   [255]
 
-Behind the scenes, this will throw TypeError
+Behind the scenes, this would throw TypeError without intype=bytes
 
   $ hg log -l 3 --template '{date|obfuscate}\n'
-  abort: template filter 'obfuscate' is not compatible with keyword 'date'
-  [255]
+  &#48;&#46;&#48;&#48;
+  &#48;&#46;&#48;&#48;
+  &#49;&#53;&#55;&#55;&#56;&#55;&#50;&#56;&#54;&#48;&#46;&#48;&#48;
 
 Behind the scenes, this will throw a ValueError
 
@@ -2809,11 +2809,12 @@
   abort: template filter 'shortdate' is not compatible with keyword 'desc'
   [255]
 
-Behind the scenes, this will throw AttributeError
+Behind the scenes, this would throw AttributeError without intype=bytes
 
   $ hg log -l 3 --template 'line: {date|escape}\n'
-  abort: template filter 'escape' is not compatible with keyword 'date'
-  [255]
+  line: 0.00
+  line: 0.00
+  line: 1577872860.00
 
   $ hg log -l 3 --template 'line: {extras|localdate}\n'
   hg: parse error: localdate expects a date information
@@ -4656,9 +4657,8 @@
   $ HGENCODING=ascii hg log -T "replaced: {'`cat latin1`'|utf8|hex}\n" -l1
   abort: decoding near * (glob)
   [255]
-  $ hg log -T "invalid type: {rev|utf8}\n" -r0
-  abort: template filter 'utf8' is not compatible with keyword 'rev'
-  [255]
+  $ hg log -T "coerced to string: {rev|utf8}\n" -r0
+  coerced to string: 0
 
 pad width: