comparison mercurial/templatefilters.py @ 37223:08e042f0a67c

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?
author Yuya Nishihara <yuya@tcha.org>
date Sun, 18 Mar 2018 15:42:28 +0900
parents 54355c243042
children 9bcf096a2da2
comparison
equal deleted inserted replaced
37222:54355c243042 37223:08e042f0a67c
38 # obj - object to be filtered (text, date, list and so on) 38 # obj - object to be filtered (text, date, list and so on)
39 filters = {} 39 filters = {}
40 40
41 templatefilter = registrar.templatefilter(filters) 41 templatefilter = registrar.templatefilter(filters)
42 42
43 @templatefilter('addbreaks') 43 @templatefilter('addbreaks', intype=bytes)
44 def addbreaks(text): 44 def addbreaks(text):
45 """Any text. Add an XHTML "<br />" tag before the end of 45 """Any text. Add an XHTML "<br />" tag before the end of
46 every line except the last. 46 every line except the last.
47 """ 47 """
48 return text.replace('\n', '<br/>\n') 48 return text.replace('\n', '<br/>\n')
88 if n >= 2 or s == 1: 88 if n >= 2 or s == 1:
89 if future: 89 if future:
90 return '%s from now' % fmt(t, n, a) 90 return '%s from now' % fmt(t, n, a)
91 return '%s ago' % fmt(t, n, a) 91 return '%s ago' % fmt(t, n, a)
92 92
93 @templatefilter('basename') 93 @templatefilter('basename', intype=bytes)
94 def basename(path): 94 def basename(path):
95 """Any text. Treats the text as a path, and returns the last 95 """Any text. Treats the text as a path, and returns the last
96 component of the path after splitting by the path separator. 96 component of the path after splitting by the path separator.
97 For example, "foo/bar/baz" becomes "baz" and "foo/bar//" becomes "". 97 For example, "foo/bar/baz" becomes "baz" and "foo/bar//" becomes "".
98 """ 98 """
101 @templatefilter('count') 101 @templatefilter('count')
102 def count(i): 102 def count(i):
103 """List or text. Returns the length as an integer.""" 103 """List or text. Returns the length as an integer."""
104 return len(i) 104 return len(i)
105 105
106 @templatefilter('dirname') 106 @templatefilter('dirname', intype=bytes)
107 def dirname(path): 107 def dirname(path):
108 """Any text. Treats the text as a path, and strips the last 108 """Any text. Treats the text as a path, and strips the last
109 component of the path after splitting by the path separator. 109 component of the path after splitting by the path separator.
110 """ 110 """
111 return os.path.dirname(path) 111 return os.path.dirname(path)
112 112
113 @templatefilter('domain') 113 @templatefilter('domain', intype=bytes)
114 def domain(author): 114 def domain(author):
115 """Any text. Finds the first string that looks like an email 115 """Any text. Finds the first string that looks like an email
116 address, and extracts just the domain component. Example: ``User 116 address, and extracts just the domain component. Example: ``User
117 <user@example.com>`` becomes ``example.com``. 117 <user@example.com>`` becomes ``example.com``.
118 """ 118 """
123 f = author.find('>') 123 f = author.find('>')
124 if f >= 0: 124 if f >= 0:
125 author = author[:f] 125 author = author[:f]
126 return author 126 return author
127 127
128 @templatefilter('email') 128 @templatefilter('email', intype=bytes)
129 def email(text): 129 def email(text):
130 """Any text. Extracts the first string that looks like an email 130 """Any text. Extracts the first string that looks like an email
131 address. Example: ``User <user@example.com>`` becomes 131 address. Example: ``User <user@example.com>`` becomes
132 ``user@example.com``. 132 ``user@example.com``.
133 """ 133 """
134 return stringutil.email(text) 134 return stringutil.email(text)
135 135
136 @templatefilter('escape') 136 @templatefilter('escape', intype=bytes)
137 def escape(text): 137 def escape(text):
138 """Any text. Replaces the special XML/XHTML characters "&", "<" 138 """Any text. Replaces the special XML/XHTML characters "&", "<"
139 and ">" with XML entities, and filters out NUL characters. 139 and ">" with XML entities, and filters out NUL characters.
140 """ 140 """
141 return url.escape(text.replace('\0', ''), True) 141 return url.escape(text.replace('\0', ''), True)
168 return "".join([stringutil.wrap(space_re.sub(' ', 168 return "".join([stringutil.wrap(space_re.sub(' ',
169 stringutil.wrap(para, width)), 169 stringutil.wrap(para, width)),
170 width, initindent, hangindent) + rest 170 width, initindent, hangindent) + rest
171 for para, rest in findparas()]) 171 for para, rest in findparas()])
172 172
173 @templatefilter('fill68') 173 @templatefilter('fill68', intype=bytes)
174 def fill68(text): 174 def fill68(text):
175 """Any text. Wraps the text to fit in 68 columns.""" 175 """Any text. Wraps the text to fit in 68 columns."""
176 return fill(text, 68) 176 return fill(text, 68)
177 177
178 @templatefilter('fill76') 178 @templatefilter('fill76', intype=bytes)
179 def fill76(text): 179 def fill76(text):
180 """Any text. Wraps the text to fit in 76 columns.""" 180 """Any text. Wraps the text to fit in 76 columns."""
181 return fill(text, 76) 181 return fill(text, 76)
182 182
183 @templatefilter('firstline') 183 @templatefilter('firstline', intype=bytes)
184 def firstline(text): 184 def firstline(text):
185 """Any text. Returns the first line of text.""" 185 """Any text. Returns the first line of text."""
186 try: 186 try:
187 return text.splitlines(True)[0].rstrip('\r\n') 187 return text.splitlines(True)[0].rstrip('\r\n')
188 except IndexError: 188 except IndexError:
189 return '' 189 return ''
190 190
191 @templatefilter('hex') 191 @templatefilter('hex', intype=bytes)
192 def hexfilter(text): 192 def hexfilter(text):
193 """Any text. Convert a binary Mercurial node identifier into 193 """Any text. Convert a binary Mercurial node identifier into
194 its long hexadecimal representation. 194 its long hexadecimal representation.
195 """ 195 """
196 return node.hex(text) 196 return node.hex(text)
260 out = [json(i, paranoid) for i in obj] 260 out = [json(i, paranoid) for i in obj]
261 return '[' + ', '.join(out) + ']' 261 return '[' + ', '.join(out) + ']'
262 else: 262 else:
263 raise TypeError('cannot encode type %s' % obj.__class__.__name__) 263 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
264 264
265 @templatefilter('lower') 265 @templatefilter('lower', intype=bytes)
266 def lower(text): 266 def lower(text):
267 """Any text. Converts the text to lowercase.""" 267 """Any text. Converts the text to lowercase."""
268 return encoding.lower(text) 268 return encoding.lower(text)
269 269
270 @templatefilter('nonempty') 270 @templatefilter('nonempty', intype=bytes)
271 def nonempty(text): 271 def nonempty(text):
272 """Any text. Returns '(none)' if the string is empty.""" 272 """Any text. Returns '(none)' if the string is empty."""
273 return text or "(none)" 273 return text or "(none)"
274 274
275 @templatefilter('obfuscate') 275 @templatefilter('obfuscate', intype=bytes)
276 def obfuscate(text): 276 def obfuscate(text):
277 """Any text. Returns the input text rendered as a sequence of 277 """Any text. Returns the input text rendered as a sequence of
278 XML entities. 278 XML entities.
279 """ 279 """
280 text = unicode(text, pycompat.sysstr(encoding.encoding), r'replace') 280 text = unicode(text, pycompat.sysstr(encoding.encoding), r'replace')
281 return ''.join(['&#%d;' % ord(c) for c in text]) 281 return ''.join(['&#%d;' % ord(c) for c in text])
282 282
283 @templatefilter('permissions') 283 @templatefilter('permissions', intype=bytes)
284 def permissions(flags): 284 def permissions(flags):
285 if "l" in flags: 285 if "l" in flags:
286 return "lrwxrwxrwx" 286 return "lrwxrwxrwx"
287 if "x" in flags: 287 if "x" in flags:
288 return "-rwxr-xr-x" 288 return "-rwxr-xr-x"
289 return "-rw-r--r--" 289 return "-rw-r--r--"
290 290
291 @templatefilter('person') 291 @templatefilter('person', intype=bytes)
292 def person(author): 292 def person(author):
293 """Any text. Returns the name before an email address, 293 """Any text. Returns the name before an email address,
294 interpreting it as per RFC 5322. 294 interpreting it as per RFC 5322.
295 """ 295 """
296 return stringutil.person(author) 296 return stringutil.person(author)
297 297
298 @templatefilter('revescape') 298 @templatefilter('revescape', intype=bytes)
299 def revescape(text): 299 def revescape(text):
300 """Any text. Escapes all "special" characters, except @. 300 """Any text. Escapes all "special" characters, except @.
301 Forward slashes are escaped twice to prevent web servers from prematurely 301 Forward slashes are escaped twice to prevent web servers from prematurely
302 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz". 302 unescaping them. For example, "@foo bar/baz" becomes "@foo%20bar%252Fbaz".
303 """ 303 """
315 """Date. Returns a date using the same format used in email 315 """Date. Returns a date using the same format used in email
316 headers: "Tue, 18 Aug 2009 13:00:13 +0200". 316 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
317 """ 317 """
318 return dateutil.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2") 318 return dateutil.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
319 319
320 @templatefilter('short') 320 @templatefilter('short', intype=bytes)
321 def short(text): 321 def short(text):
322 """Changeset hash. Returns the short form of a changeset hash, 322 """Changeset hash. Returns the short form of a changeset hash,
323 i.e. a 12 hexadecimal digit string. 323 i.e. a 12 hexadecimal digit string.
324 """ 324 """
325 return text[:12] 325 return text[:12]
326 326
327 @templatefilter('shortbisect') 327 @templatefilter('shortbisect', intype=bytes)
328 def shortbisect(label): 328 def shortbisect(label):
329 """Any text. Treats `label` as a bisection status, and 329 """Any text. Treats `label` as a bisection status, and
330 returns a single-character representing the status (G: good, B: bad, 330 returns a single-character representing the status (G: good, B: bad,
331 S: skipped, U: untested, I: ignored). Returns single space if `text` 331 S: skipped, U: untested, I: ignored). Returns single space if `text`
332 is not a valid bisection status. 332 is not a valid bisection status.
338 @templatefilter('shortdate') 338 @templatefilter('shortdate')
339 def shortdate(text): 339 def shortdate(text):
340 """Date. Returns a date like "2006-09-18".""" 340 """Date. Returns a date like "2006-09-18"."""
341 return dateutil.shortdate(text) 341 return dateutil.shortdate(text)
342 342
343 @templatefilter('slashpath') 343 @templatefilter('slashpath', intype=bytes)
344 def slashpath(path): 344 def slashpath(path):
345 """Any text. Replaces the native path separator with slash.""" 345 """Any text. Replaces the native path separator with slash."""
346 return util.pconvert(path) 346 return util.pconvert(path)
347 347
348 @templatefilter('splitlines') 348 @templatefilter('splitlines', intype=bytes)
349 def splitlines(text): 349 def splitlines(text):
350 """Any text. Split text into a list of lines.""" 350 """Any text. Split text into a list of lines."""
351 return templateutil.hybridlist(text.splitlines(), name='line') 351 return templateutil.hybridlist(text.splitlines(), name='line')
352 352
353 @templatefilter('stringescape') 353 @templatefilter('stringescape', intype=bytes)
354 def stringescape(text): 354 def stringescape(text):
355 return stringutil.escapestr(text) 355 return stringutil.escapestr(text)
356 356
357 @templatefilter('stringify', intype=bytes) 357 @templatefilter('stringify', intype=bytes)
358 def stringify(thing): 358 def stringify(thing):
359 """Any type. Turns the value into text by converting values into 359 """Any type. Turns the value into text by converting values into
360 text and concatenating them. 360 text and concatenating them.
361 """ 361 """
362 return thing # coerced by the intype 362 return thing # coerced by the intype
363 363
364 @templatefilter('stripdir') 364 @templatefilter('stripdir', intype=bytes)
365 def stripdir(text): 365 def stripdir(text):
366 """Treat the text as path and strip a directory level, if 366 """Treat the text as path and strip a directory level, if
367 possible. For example, "foo" and "foo/bar" becomes "foo". 367 possible. For example, "foo" and "foo/bar" becomes "foo".
368 """ 368 """
369 dir = os.path.dirname(text) 369 dir = os.path.dirname(text)
370 if dir == "": 370 if dir == "":
371 return os.path.basename(text) 371 return os.path.basename(text)
372 else: 372 else:
373 return dir 373 return dir
374 374
375 @templatefilter('tabindent') 375 @templatefilter('tabindent', intype=bytes)
376 def tabindent(text): 376 def tabindent(text):
377 """Any text. Returns the text, with every non-empty line 377 """Any text. Returns the text, with every non-empty line
378 except the first starting with a tab character. 378 except the first starting with a tab character.
379 """ 379 """
380 return indent(text, '\t') 380 return indent(text, '\t')
381 381
382 @templatefilter('upper') 382 @templatefilter('upper', intype=bytes)
383 def upper(text): 383 def upper(text):
384 """Any text. Converts the text to uppercase.""" 384 """Any text. Converts the text to uppercase."""
385 return encoding.upper(text) 385 return encoding.upper(text)
386 386
387 @templatefilter('urlescape') 387 @templatefilter('urlescape', intype=bytes)
388 def urlescape(text): 388 def urlescape(text):
389 """Any text. Escapes all "special" characters. For example, 389 """Any text. Escapes all "special" characters. For example,
390 "foo bar" becomes "foo%20bar". 390 "foo bar" becomes "foo%20bar".
391 """ 391 """
392 return urlreq.quote(text) 392 return urlreq.quote(text)
393 393
394 @templatefilter('user') 394 @templatefilter('user', intype=bytes)
395 def userfilter(text): 395 def userfilter(text):
396 """Any text. Returns a short representation of a user name or email 396 """Any text. Returns a short representation of a user name or email
397 address.""" 397 address."""
398 return stringutil.shortuser(text) 398 return stringutil.shortuser(text)
399 399
400 @templatefilter('emailuser') 400 @templatefilter('emailuser', intype=bytes)
401 def emailuser(text): 401 def emailuser(text):
402 """Any text. Returns the user portion of an email address.""" 402 """Any text. Returns the user portion of an email address."""
403 return stringutil.emailuser(text) 403 return stringutil.emailuser(text)
404 404
405 @templatefilter('utf8') 405 @templatefilter('utf8', intype=bytes)
406 def utf8(text): 406 def utf8(text):
407 """Any text. Converts from the local character encoding to UTF-8.""" 407 """Any text. Converts from the local character encoding to UTF-8."""
408 return encoding.fromlocal(text) 408 return encoding.fromlocal(text)
409 409
410 @templatefilter('xmlescape') 410 @templatefilter('xmlescape', intype=bytes)
411 def xmlescape(text): 411 def xmlescape(text):
412 text = (text 412 text = (text
413 .replace('&', '&amp;') 413 .replace('&', '&amp;')
414 .replace('<', '&lt;') 414 .replace('<', '&lt;')
415 .replace('>', '&gt;') 415 .replace('>', '&gt;')