Mercurial > public > mercurial-scm > hg
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('&', '&') | 413 .replace('&', '&') |
414 .replace('<', '<') | 414 .replace('<', '<') |
415 .replace('>', '>') | 415 .replace('>', '>') |