OLD | NEW |
1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
2 """ | 2 """ |
3 jinja2.filters | 3 jinja2.filters |
4 ~~~~~~~~~~~~~~ | 4 ~~~~~~~~~~~~~~ |
5 | 5 |
6 Bundled jinja filters. | 6 Bundled jinja filters. |
7 | 7 |
8 :copyright: (c) 2010 by the Jinja Team. | 8 :copyright: (c) 2010 by the Jinja Team. |
9 :license: BSD, see LICENSE for more details. | 9 :license: BSD, see LICENSE for more details. |
10 """ | 10 """ |
11 import re | 11 import re |
12 import math | 12 import math |
| 13 |
13 from random import choice | 14 from random import choice |
14 from operator import itemgetter | 15 from operator import itemgetter |
15 from itertools import imap, groupby | 16 from itertools import groupby |
16 from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode | 17 from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \ |
| 18 unicode_urlencode |
17 from jinja2.runtime import Undefined | 19 from jinja2.runtime import Undefined |
18 from jinja2.exceptions import FilterArgumentError, SecurityError | 20 from jinja2.exceptions import FilterArgumentError |
| 21 from jinja2._compat import next, imap, string_types, text_type, iteritems |
19 | 22 |
20 | 23 |
21 _word_re = re.compile(r'\w+(?u)') | 24 _word_re = re.compile(r'\w+(?u)') |
22 | 25 |
23 | 26 |
24 def contextfilter(f): | 27 def contextfilter(f): |
25 """Decorator for marking context dependent filters. The current | 28 """Decorator for marking context dependent filters. The current |
26 :class:`Context` will be passed as first argument. | 29 :class:`Context` will be passed as first argument. |
27 """ | 30 """ |
28 f.contextfilter = True | 31 f.contextfilter = True |
(...skipping 15 matching lines...) Expand all Loading... |
44 """Decorator for marking evironment dependent filters. The current | 47 """Decorator for marking evironment dependent filters. The current |
45 :class:`Environment` is passed to the filter as first argument. | 48 :class:`Environment` is passed to the filter as first argument. |
46 """ | 49 """ |
47 f.environmentfilter = True | 50 f.environmentfilter = True |
48 return f | 51 return f |
49 | 52 |
50 | 53 |
51 def make_attrgetter(environment, attribute): | 54 def make_attrgetter(environment, attribute): |
52 """Returns a callable that looks up the given attribute from a | 55 """Returns a callable that looks up the given attribute from a |
53 passed object with the rules of the environment. Dots are allowed | 56 passed object with the rules of the environment. Dots are allowed |
54 to access attributes of attributes. | 57 to access attributes of attributes. Integer parts in paths are |
| 58 looked up as integers. |
55 """ | 59 """ |
56 if not isinstance(attribute, basestring) or '.' not in attribute: | 60 if not isinstance(attribute, string_types) \ |
| 61 or ('.' not in attribute and not attribute.isdigit()): |
57 return lambda x: environment.getitem(x, attribute) | 62 return lambda x: environment.getitem(x, attribute) |
58 attribute = attribute.split('.') | 63 attribute = attribute.split('.') |
59 def attrgetter(item): | 64 def attrgetter(item): |
60 for part in attribute: | 65 for part in attribute: |
| 66 if part.isdigit(): |
| 67 part = int(part) |
61 item = environment.getitem(item, part) | 68 item = environment.getitem(item, part) |
62 return item | 69 return item |
63 return attrgetter | 70 return attrgetter |
64 | 71 |
65 | 72 |
66 def do_forceescape(value): | 73 def do_forceescape(value): |
67 """Enforce HTML escaping. This will probably double escape variables.""" | 74 """Enforce HTML escaping. This will probably double escape variables.""" |
68 if hasattr(value, '__html__'): | 75 if hasattr(value, '__html__'): |
69 value = value.__html__() | 76 value = value.__html__() |
70 return escape(unicode(value)) | 77 return escape(text_type(value)) |
| 78 |
| 79 |
| 80 def do_urlencode(value): |
| 81 """Escape strings for use in URLs (uses UTF-8 encoding). It accepts both |
| 82 dictionaries and regular strings as well as pairwise iterables. |
| 83 |
| 84 .. versionadded:: 2.7 |
| 85 """ |
| 86 itemiter = None |
| 87 if isinstance(value, dict): |
| 88 itemiter = iteritems(value) |
| 89 elif not isinstance(value, string_types): |
| 90 try: |
| 91 itemiter = iter(value) |
| 92 except TypeError: |
| 93 pass |
| 94 if itemiter is None: |
| 95 return unicode_urlencode(value) |
| 96 return u'&'.join(unicode_urlencode(k) + '=' + |
| 97 unicode_urlencode(v) for k, v in itemiter) |
71 | 98 |
72 | 99 |
73 @evalcontextfilter | 100 @evalcontextfilter |
74 def do_replace(eval_ctx, s, old, new, count=None): | 101 def do_replace(eval_ctx, s, old, new, count=None): |
75 """Return a copy of the value with all occurrences of a substring | 102 """Return a copy of the value with all occurrences of a substring |
76 replaced with a new one. The first argument is the substring | 103 replaced with a new one. The first argument is the substring |
77 that should be replaced, the second is the replacement string. | 104 that should be replaced, the second is the replacement string. |
78 If the optional third argument ``count`` is given, only the first | 105 If the optional third argument ``count`` is given, only the first |
79 ``count`` occurrences are replaced: | 106 ``count`` occurrences are replaced: |
80 | 107 |
81 .. sourcecode:: jinja | 108 .. sourcecode:: jinja |
82 | 109 |
83 {{ "Hello World"|replace("Hello", "Goodbye") }} | 110 {{ "Hello World"|replace("Hello", "Goodbye") }} |
84 -> Goodbye World | 111 -> Goodbye World |
85 | 112 |
86 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} | 113 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} |
87 -> d'oh, d'oh, aaargh | 114 -> d'oh, d'oh, aaargh |
88 """ | 115 """ |
89 if count is None: | 116 if count is None: |
90 count = -1 | 117 count = -1 |
91 if not eval_ctx.autoescape: | 118 if not eval_ctx.autoescape: |
92 return unicode(s).replace(unicode(old), unicode(new), count) | 119 return text_type(s).replace(text_type(old), text_type(new), count) |
93 if hasattr(old, '__html__') or hasattr(new, '__html__') and \ | 120 if hasattr(old, '__html__') or hasattr(new, '__html__') and \ |
94 not hasattr(s, '__html__'): | 121 not hasattr(s, '__html__'): |
95 s = escape(s) | 122 s = escape(s) |
96 else: | 123 else: |
97 s = soft_unicode(s) | 124 s = soft_unicode(s) |
98 return s.replace(soft_unicode(old), soft_unicode(new), count) | 125 return s.replace(soft_unicode(old), soft_unicode(new), count) |
99 | 126 |
100 | 127 |
101 def do_upper(s): | 128 def do_upper(s): |
102 """Convert a value to uppercase.""" | 129 """Convert a value to uppercase.""" |
(...skipping 24 matching lines...) Expand all Loading... |
127 | 154 |
128 <ul class="my_list" id="list-42"> | 155 <ul class="my_list" id="list-42"> |
129 ... | 156 ... |
130 </ul> | 157 </ul> |
131 | 158 |
132 As you can see it automatically prepends a space in front of the item | 159 As you can see it automatically prepends a space in front of the item |
133 if the filter returned something unless the second parameter is false. | 160 if the filter returned something unless the second parameter is false. |
134 """ | 161 """ |
135 rv = u' '.join( | 162 rv = u' '.join( |
136 u'%s="%s"' % (escape(key), escape(value)) | 163 u'%s="%s"' % (escape(key), escape(value)) |
137 for key, value in d.iteritems() | 164 for key, value in iteritems(d) |
138 if value is not None and not isinstance(value, Undefined) | 165 if value is not None and not isinstance(value, Undefined) |
139 ) | 166 ) |
140 if autospace and rv: | 167 if autospace and rv: |
141 rv = u' ' + rv | 168 rv = u' ' + rv |
142 if _eval_ctx.autoescape: | 169 if _eval_ctx.autoescape: |
143 rv = Markup(rv) | 170 rv = Markup(rv) |
144 return rv | 171 return rv |
145 | 172 |
146 | 173 |
147 def do_capitalize(s): | 174 def do_capitalize(s): |
148 """Capitalize a value. The first character will be uppercase, all others | 175 """Capitalize a value. The first character will be uppercase, all others |
149 lowercase. | 176 lowercase. |
150 """ | 177 """ |
151 return soft_unicode(s).capitalize() | 178 return soft_unicode(s).capitalize() |
152 | 179 |
153 | 180 |
154 def do_title(s): | 181 def do_title(s): |
155 """Return a titlecased version of the value. I.e. words will start with | 182 """Return a titlecased version of the value. I.e. words will start with |
156 uppercase letters, all remaining characters are lowercase. | 183 uppercase letters, all remaining characters are lowercase. |
157 """ | 184 """ |
158 return soft_unicode(s).title() | 185 rv = [] |
| 186 for item in re.compile(r'([-\s]+)(?u)').split(s): |
| 187 if not item: |
| 188 continue |
| 189 rv.append(item[0].upper() + item[1:].lower()) |
| 190 return ''.join(rv) |
159 | 191 |
160 | 192 |
161 def do_dictsort(value, case_sensitive=False, by='key'): | 193 def do_dictsort(value, case_sensitive=False, by='key'): |
162 """Sort a dict and yield (key, value) pairs. Because python dicts are | 194 """Sort a dict and yield (key, value) pairs. Because python dicts are |
163 unsorted you may want to use this function to order them by either | 195 unsorted you may want to use this function to order them by either |
164 key or value: | 196 key or value: |
165 | 197 |
166 .. sourcecode:: jinja | 198 .. sourcecode:: jinja |
167 | 199 |
168 {% for item in mydict|dictsort %} | 200 {% for item in mydict|dictsort %} |
169 sort the dict by key, case insensitive | 201 sort the dict by key, case insensitive |
170 | 202 |
171 {% for item in mydict|dicsort(true) %} | 203 {% for item in mydict|dictsort(true) %} |
172 sort the dict by key, case sensitive | 204 sort the dict by key, case sensitive |
173 | 205 |
174 {% for item in mydict|dictsort(false, 'value') %} | 206 {% for item in mydict|dictsort(false, 'value') %} |
175 sort the dict by key, case insensitive, sorted | 207 sort the dict by key, case insensitive, sorted |
176 normally and ordered by value. | 208 normally and ordered by value. |
177 """ | 209 """ |
178 if by == 'key': | 210 if by == 'key': |
179 pos = 0 | 211 pos = 0 |
180 elif by == 'value': | 212 elif by == 'value': |
181 pos = 1 | 213 pos = 1 |
182 else: | 214 else: |
183 raise FilterArgumentError('You can only sort by either ' | 215 raise FilterArgumentError('You can only sort by either ' |
184 '"key" or "value"') | 216 '"key" or "value"') |
185 def sort_func(item): | 217 def sort_func(item): |
186 value = item[pos] | 218 value = item[pos] |
187 if isinstance(value, basestring) and not case_sensitive: | 219 if isinstance(value, string_types) and not case_sensitive: |
188 value = value.lower() | 220 value = value.lower() |
189 return value | 221 return value |
190 | 222 |
191 return sorted(value.items(), key=sort_func) | 223 return sorted(value.items(), key=sort_func) |
192 | 224 |
193 | 225 |
194 @environmentfilter | 226 @environmentfilter |
195 def do_sort(environment, value, reverse=False, case_sensitive=False, | 227 def do_sort(environment, value, reverse=False, case_sensitive=False, |
196 attribute=None): | 228 attribute=None): |
197 """Sort an iterable. Per default it sorts ascending, if you pass it | 229 """Sort an iterable. Per default it sorts ascending, if you pass it |
(...skipping 16 matching lines...) Expand all Loading... |
214 | 246 |
215 {% for item in iterable|sort(attribute='date') %} | 247 {% for item in iterable|sort(attribute='date') %} |
216 ... | 248 ... |
217 {% endfor %} | 249 {% endfor %} |
218 | 250 |
219 .. versionchanged:: 2.6 | 251 .. versionchanged:: 2.6 |
220 The `attribute` parameter was added. | 252 The `attribute` parameter was added. |
221 """ | 253 """ |
222 if not case_sensitive: | 254 if not case_sensitive: |
223 def sort_func(item): | 255 def sort_func(item): |
224 if isinstance(item, basestring): | 256 if isinstance(item, string_types): |
225 item = item.lower() | 257 item = item.lower() |
226 return item | 258 return item |
227 else: | 259 else: |
228 sort_func = None | 260 sort_func = None |
229 if attribute is not None: | 261 if attribute is not None: |
230 getter = make_attrgetter(environment, attribute) | 262 getter = make_attrgetter(environment, attribute) |
231 def sort_func(item, processor=sort_func or (lambda x: x)): | 263 def sort_func(item, processor=sort_func or (lambda x: x)): |
232 return processor(getter(item)) | 264 return processor(getter(item)) |
233 return sorted(value, key=sort_func, reverse=reverse) | 265 return sorted(value, key=sort_func, reverse=reverse) |
234 | 266 |
235 | 267 |
236 def do_default(value, default_value=u'', boolean=False): | 268 def do_default(value, default_value=u'', boolean=False): |
237 """If the value is undefined it will return the passed default value, | 269 """If the value is undefined it will return the passed default value, |
238 otherwise the value of the variable: | 270 otherwise the value of the variable: |
239 | 271 |
240 .. sourcecode:: jinja | 272 .. sourcecode:: jinja |
241 | 273 |
242 {{ my_variable|default('my_variable is not defined') }} | 274 {{ my_variable|default('my_variable is not defined') }} |
243 | 275 |
244 This will output the value of ``my_variable`` if the variable was | 276 This will output the value of ``my_variable`` if the variable was |
245 defined, otherwise ``'my_variable is not defined'``. If you want | 277 defined, otherwise ``'my_variable is not defined'``. If you want |
246 to use default with variables that evaluate to false you have to | 278 to use default with variables that evaluate to false you have to |
247 set the second parameter to `true`: | 279 set the second parameter to `true`: |
248 | 280 |
249 .. sourcecode:: jinja | 281 .. sourcecode:: jinja |
250 | 282 |
251 {{ ''|default('the string was empty', true) }} | 283 {{ ''|default('the string was empty', true) }} |
252 """ | 284 """ |
253 if (boolean and not value) or isinstance(value, Undefined): | 285 if isinstance(value, Undefined) or (boolean and not value): |
254 return default_value | 286 return default_value |
255 return value | 287 return value |
256 | 288 |
257 | 289 |
258 @evalcontextfilter | 290 @evalcontextfilter |
259 def do_join(eval_ctx, value, d=u'', attribute=None): | 291 def do_join(eval_ctx, value, d=u'', attribute=None): |
260 """Return a string which is the concatenation of the strings in the | 292 """Return a string which is the concatenation of the strings in the |
261 sequence. The separator between elements is an empty string per | 293 sequence. The separator between elements is an empty string per |
262 default, you can define it with the optional parameter: | 294 default, you can define it with the optional parameter: |
263 | 295 |
(...skipping 12 matching lines...) Expand all Loading... |
276 {{ users|join(', ', attribute='username') }} | 308 {{ users|join(', ', attribute='username') }} |
277 | 309 |
278 .. versionadded:: 2.6 | 310 .. versionadded:: 2.6 |
279 The `attribute` parameter was added. | 311 The `attribute` parameter was added. |
280 """ | 312 """ |
281 if attribute is not None: | 313 if attribute is not None: |
282 value = imap(make_attrgetter(eval_ctx.environment, attribute), value) | 314 value = imap(make_attrgetter(eval_ctx.environment, attribute), value) |
283 | 315 |
284 # no automatic escaping? joining is a lot eaiser then | 316 # no automatic escaping? joining is a lot eaiser then |
285 if not eval_ctx.autoescape: | 317 if not eval_ctx.autoescape: |
286 return unicode(d).join(imap(unicode, value)) | 318 return text_type(d).join(imap(text_type, value)) |
287 | 319 |
288 # if the delimiter doesn't have an html representation we check | 320 # if the delimiter doesn't have an html representation we check |
289 # if any of the items has. If yes we do a coercion to Markup | 321 # if any of the items has. If yes we do a coercion to Markup |
290 if not hasattr(d, '__html__'): | 322 if not hasattr(d, '__html__'): |
291 value = list(value) | 323 value = list(value) |
292 do_escape = False | 324 do_escape = False |
293 for idx, item in enumerate(value): | 325 for idx, item in enumerate(value): |
294 if hasattr(item, '__html__'): | 326 if hasattr(item, '__html__'): |
295 do_escape = True | 327 do_escape = True |
296 else: | 328 else: |
297 value[idx] = unicode(item) | 329 value[idx] = text_type(item) |
298 if do_escape: | 330 if do_escape: |
299 d = escape(d) | 331 d = escape(d) |
300 else: | 332 else: |
301 d = unicode(d) | 333 d = text_type(d) |
302 return d.join(value) | 334 return d.join(value) |
303 | 335 |
304 # no html involved, to normal joining | 336 # no html involved, to normal joining |
305 return soft_unicode(d).join(imap(soft_unicode, value)) | 337 return soft_unicode(d).join(imap(soft_unicode, value)) |
306 | 338 |
307 | 339 |
308 def do_center(value, width=80): | 340 def do_center(value, width=80): |
309 """Centers the value in a field of a given width.""" | 341 """Centers the value in a field of a given width.""" |
310 return unicode(value).center(width) | 342 return text_type(value).center(width) |
311 | 343 |
312 | 344 |
313 @environmentfilter | 345 @environmentfilter |
314 def do_first(environment, seq): | 346 def do_first(environment, seq): |
315 """Return the first item of a sequence.""" | 347 """Return the first item of a sequence.""" |
316 try: | 348 try: |
317 return iter(seq).next() | 349 return next(iter(seq)) |
318 except StopIteration: | 350 except StopIteration: |
319 return environment.undefined('No first item, sequence was empty.') | 351 return environment.undefined('No first item, sequence was empty.') |
320 | 352 |
321 | 353 |
322 @environmentfilter | 354 @environmentfilter |
323 def do_last(environment, seq): | 355 def do_last(environment, seq): |
324 """Return the last item of a sequence.""" | 356 """Return the last item of a sequence.""" |
325 try: | 357 try: |
326 return iter(reversed(seq)).next() | 358 return next(iter(reversed(seq))) |
327 except StopIteration: | 359 except StopIteration: |
328 return environment.undefined('No last item, sequence was empty.') | 360 return environment.undefined('No last item, sequence was empty.') |
329 | 361 |
330 | 362 |
331 @environmentfilter | 363 @environmentfilter |
332 def do_random(environment, seq): | 364 def do_random(environment, seq): |
333 """Return a random item from the sequence.""" | 365 """Return a random item from the sequence.""" |
334 try: | 366 try: |
335 return choice(seq) | 367 return choice(seq) |
336 except IndexError: | 368 except IndexError: |
337 return environment.undefined('No random item, sequence was empty.') | 369 return environment.undefined('No random item, sequence was empty.') |
338 | 370 |
339 | 371 |
340 def do_filesizeformat(value, binary=False): | 372 def do_filesizeformat(value, binary=False): |
341 """Format the value like a 'human-readable' file size (i.e. 13 kB, | 373 """Format the value like a 'human-readable' file size (i.e. 13 kB, |
342 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, | 374 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, |
343 Giga, etc.), if the second parameter is set to `True` the binary | 375 Giga, etc.), if the second parameter is set to `True` the binary |
344 prefixes are used (Mebi, Gibi). | 376 prefixes are used (Mebi, Gibi). |
345 """ | 377 """ |
346 bytes = float(value) | 378 bytes = float(value) |
347 base = binary and 1024 or 1000 | 379 base = binary and 1024 or 1000 |
348 prefixes = [ | 380 prefixes = [ |
349 (binary and "KiB" or "kB"), | 381 (binary and 'KiB' or 'kB'), |
350 (binary and "MiB" or "MB"), | 382 (binary and 'MiB' or 'MB'), |
351 (binary and "GiB" or "GB"), | 383 (binary and 'GiB' or 'GB'), |
352 (binary and "TiB" or "TB"), | 384 (binary and 'TiB' or 'TB'), |
353 (binary and "PiB" or "PB"), | 385 (binary and 'PiB' or 'PB'), |
354 (binary and "EiB" or "EB"), | 386 (binary and 'EiB' or 'EB'), |
355 (binary and "ZiB" or "ZB"), | 387 (binary and 'ZiB' or 'ZB'), |
356 (binary and "YiB" or "YB") | 388 (binary and 'YiB' or 'YB') |
357 ] | 389 ] |
358 if bytes == 1: | 390 if bytes == 1: |
359 return "1 Byte" | 391 return '1 Byte' |
360 elif bytes < base: | 392 elif bytes < base: |
361 return "%d Bytes" % bytes | 393 return '%d Bytes' % bytes |
362 else: | 394 else: |
363 for i, prefix in enumerate(prefixes): | 395 for i, prefix in enumerate(prefixes): |
364 unit = base * base ** (i + 1) | 396 unit = base ** (i + 2) |
365 if bytes < unit: | 397 if bytes < unit: |
366 return "%.1f %s" % ((bytes / unit), prefix) | 398 return '%.1f %s' % ((base * bytes / unit), prefix) |
367 return "%.1f %s" % ((bytes / unit), prefix) | 399 return '%.1f %s' % ((base * bytes / unit), prefix) |
368 | 400 |
369 | 401 |
370 def do_pprint(value, verbose=False): | 402 def do_pprint(value, verbose=False): |
371 """Pretty print a variable. Useful for debugging. | 403 """Pretty print a variable. Useful for debugging. |
372 | 404 |
373 With Jinja 1.2 onwards you can pass it a parameter. If this parameter | 405 With Jinja 1.2 onwards you can pass it a parameter. If this parameter |
374 is truthy the output will be more verbose (this requires `pretty`) | 406 is truthy the output will be more verbose (this requires `pretty`) |
375 """ | 407 """ |
376 return pformat(value, verbose=verbose) | 408 return pformat(value, verbose=verbose) |
377 | 409 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
410 rv = (u'\n' + indention).join(s.splitlines()) | 442 rv = (u'\n' + indention).join(s.splitlines()) |
411 if indentfirst: | 443 if indentfirst: |
412 rv = indention + rv | 444 rv = indention + rv |
413 return rv | 445 return rv |
414 | 446 |
415 | 447 |
416 def do_truncate(s, length=255, killwords=False, end='...'): | 448 def do_truncate(s, length=255, killwords=False, end='...'): |
417 """Return a truncated copy of the string. The length is specified | 449 """Return a truncated copy of the string. The length is specified |
418 with the first parameter which defaults to ``255``. If the second | 450 with the first parameter which defaults to ``255``. If the second |
419 parameter is ``true`` the filter will cut the text at length. Otherwise | 451 parameter is ``true`` the filter will cut the text at length. Otherwise |
420 it will try to save the last word. If the text was in fact | 452 it will discard the last word. If the text was in fact |
421 truncated it will append an ellipsis sign (``"..."``). If you want a | 453 truncated it will append an ellipsis sign (``"..."``). If you want a |
422 different ellipsis sign than ``"..."`` you can specify it using the | 454 different ellipsis sign than ``"..."`` you can specify it using the |
423 third parameter. | 455 third parameter. |
424 | 456 |
425 .. sourcecode jinja:: | 457 .. sourcecode:: jinja |
426 | 458 |
427 {{ mytext|truncate(300, false, '»') }} | 459 {{ "foo bar"|truncate(5) }} |
428 truncate mytext to 300 chars, don't split up words, use a | 460 -> "foo ..." |
429 right pointing double arrow as ellipsis sign. | 461 {{ "foo bar"|truncate(5, True) }} |
| 462 -> "foo b..." |
430 """ | 463 """ |
431 if len(s) <= length: | 464 if len(s) <= length: |
432 return s | 465 return s |
433 elif killwords: | 466 elif killwords: |
434 return s[:length] + end | 467 return s[:length] + end |
435 words = s.split(' ') | 468 words = s.split(' ') |
436 result = [] | 469 result = [] |
437 m = 0 | 470 m = 0 |
438 for word in words: | 471 for word in words: |
439 m += len(word) + 1 | 472 m += len(word) + 1 |
440 if m > length: | 473 if m > length: |
441 break | 474 break |
442 result.append(word) | 475 result.append(word) |
443 result.append(end) | 476 result.append(end) |
444 return u' '.join(result) | 477 return u' '.join(result) |
445 | 478 |
446 @environmentfilter | 479 @environmentfilter |
447 def do_wordwrap(environment, s, width=79, break_long_words=True): | 480 def do_wordwrap(environment, s, width=79, break_long_words=True, |
| 481 wrapstring=None): |
448 """ | 482 """ |
449 Return a copy of the string passed to the filter wrapped after | 483 Return a copy of the string passed to the filter wrapped after |
450 ``79`` characters. You can override this default using the first | 484 ``79`` characters. You can override this default using the first |
451 parameter. If you set the second parameter to `false` Jinja will not | 485 parameter. If you set the second parameter to `false` Jinja will not |
452 split words apart if they are longer than `width`. | 486 split words apart if they are longer than `width`. By default, the newlines |
| 487 will be the default newlines for the environment, but this can be changed |
| 488 using the wrapstring keyword argument. |
| 489 |
| 490 .. versionadded:: 2.7 |
| 491 Added support for the `wrapstring` parameter. |
453 """ | 492 """ |
| 493 if not wrapstring: |
| 494 wrapstring = environment.newline_sequence |
454 import textwrap | 495 import textwrap |
455 return environment.newline_sequence.join(textwrap.wrap(s, width=width, expan
d_tabs=False, | 496 return wrapstring.join(textwrap.wrap(s, width=width, expand_tabs=False, |
456 replace_whitespace=False, | 497 replace_whitespace=False, |
457 break_long_words=break_long_words)) | 498 break_long_words=break_long_words)) |
458 | 499 |
459 | 500 |
460 def do_wordcount(s): | 501 def do_wordcount(s): |
461 """Count the words in that string.""" | 502 """Count the words in that string.""" |
462 return len(_word_re.findall(s)) | 503 return len(_word_re.findall(s)) |
463 | 504 |
464 | 505 |
465 def do_int(value, default=0): | 506 def do_int(value, default=0): |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
506 def do_trim(value): | 547 def do_trim(value): |
507 """Strip leading and trailing whitespace.""" | 548 """Strip leading and trailing whitespace.""" |
508 return soft_unicode(value).strip() | 549 return soft_unicode(value).strip() |
509 | 550 |
510 | 551 |
511 def do_striptags(value): | 552 def do_striptags(value): |
512 """Strip SGML/XML tags and replace adjacent whitespace by one space. | 553 """Strip SGML/XML tags and replace adjacent whitespace by one space. |
513 """ | 554 """ |
514 if hasattr(value, '__html__'): | 555 if hasattr(value, '__html__'): |
515 value = value.__html__() | 556 value = value.__html__() |
516 return Markup(unicode(value)).striptags() | 557 return Markup(text_type(value)).striptags() |
517 | 558 |
518 | 559 |
519 def do_slice(value, slices, fill_with=None): | 560 def do_slice(value, slices, fill_with=None): |
520 """Slice an iterator and return a list of lists containing | 561 """Slice an iterator and return a list of lists containing |
521 those items. Useful if you want to create a div containing | 562 those items. Useful if you want to create a div containing |
522 three ul tags that represent columns: | 563 three ul tags that represent columns: |
523 | 564 |
524 .. sourcecode:: html+jinja | 565 .. sourcecode:: html+jinja |
525 | 566 |
526 <div class="columwrapper"> | 567 <div class="columwrapper"> |
527 {%- for column in items|slice(3) %} | 568 {%- for column in items|slice(3) %} |
528 <ul class="column-{{ loop.index }}"> | 569 <ul class="column-{{ loop.index }}"> |
529 {%- for item in column %} | 570 {%- for item in column %} |
530 <li>{{ item }}</li> | 571 <li>{{ item }}</li> |
531 {%- endfor %} | 572 {%- endfor %} |
532 </ul> | 573 </ul> |
533 {%- endfor %} | 574 {%- endfor %} |
534 </div> | 575 </div> |
535 | 576 |
536 If you pass it a second argument it's used to fill missing | 577 If you pass it a second argument it's used to fill missing |
537 values on the last iteration. | 578 values on the last iteration. |
538 """ | 579 """ |
539 seq = list(value) | 580 seq = list(value) |
540 length = len(seq) | 581 length = len(seq) |
541 items_per_slice = length // slices | 582 items_per_slice = length // slices |
542 slices_with_extra = length % slices | 583 slices_with_extra = length % slices |
543 offset = 0 | 584 offset = 0 |
544 for slice_number in xrange(slices): | 585 for slice_number in range(slices): |
545 start = offset + slice_number * items_per_slice | 586 start = offset + slice_number * items_per_slice |
546 if slice_number < slices_with_extra: | 587 if slice_number < slices_with_extra: |
547 offset += 1 | 588 offset += 1 |
548 end = offset + (slice_number + 1) * items_per_slice | 589 end = offset + (slice_number + 1) * items_per_slice |
549 tmp = seq[start:end] | 590 tmp = seq[start:end] |
550 if fill_with is not None and slice_number >= slices_with_extra: | 591 if fill_with is not None and slice_number >= slices_with_extra: |
551 tmp.append(fill_with) | 592 tmp.append(fill_with) |
552 yield tmp | 593 yield tmp |
553 | 594 |
554 | 595 |
555 def do_batch(value, linecount, fill_with=None): | 596 def do_batch(value, linecount, fill_with=None): |
556 """ | 597 """ |
557 A filter that batches items. It works pretty much like `slice` | 598 A filter that batches items. It works pretty much like `slice` |
558 just the other way round. It returns a list of lists with the | 599 just the other way round. It returns a list of lists with the |
559 given number of items. If you provide a second parameter this | 600 given number of items. If you provide a second parameter this |
560 is used to fill missing items. See this example: | 601 is used to fill up missing items. See this example: |
561 | 602 |
562 .. sourcecode:: html+jinja | 603 .. sourcecode:: html+jinja |
563 | 604 |
564 <table> | 605 <table> |
565 {%- for row in items|batch(3, ' ') %} | 606 {%- for row in items|batch(3, ' ') %} |
566 <tr> | 607 <tr> |
567 {%- for column in row %} | 608 {%- for column in row %} |
568 <td>{{ column }}</td> | 609 <td>{{ column }}</td> |
569 {%- endfor %} | 610 {%- endfor %} |
570 </tr> | 611 </tr> |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
659 """ | 700 """ |
660 expr = make_attrgetter(environment, attribute) | 701 expr = make_attrgetter(environment, attribute) |
661 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr))) | 702 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr))) |
662 | 703 |
663 | 704 |
664 class _GroupTuple(tuple): | 705 class _GroupTuple(tuple): |
665 __slots__ = () | 706 __slots__ = () |
666 grouper = property(itemgetter(0)) | 707 grouper = property(itemgetter(0)) |
667 list = property(itemgetter(1)) | 708 list = property(itemgetter(1)) |
668 | 709 |
669 def __new__(cls, (key, value)): | 710 def __new__(cls, xxx_todo_changeme): |
| 711 (key, value) = xxx_todo_changeme |
670 return tuple.__new__(cls, (key, list(value))) | 712 return tuple.__new__(cls, (key, list(value))) |
671 | 713 |
672 | 714 |
673 @environmentfilter | 715 @environmentfilter |
674 def do_sum(environment, iterable, attribute=None, start=0): | 716 def do_sum(environment, iterable, attribute=None, start=0): |
675 """Returns the sum of a sequence of numbers plus the value of parameter | 717 """Returns the sum of a sequence of numbers plus the value of parameter |
676 'start' (which defaults to 0). When the sequence is empty it returns | 718 'start' (which defaults to 0). When the sequence is empty it returns |
677 start. | 719 start. |
678 | 720 |
679 It is also possible to sum up only certain attributes: | 721 It is also possible to sum up only certain attributes: |
(...skipping 20 matching lines...) Expand all Loading... |
700 | 742 |
701 def do_mark_safe(value): | 743 def do_mark_safe(value): |
702 """Mark the value as safe which means that in an environment with automatic | 744 """Mark the value as safe which means that in an environment with automatic |
703 escaping enabled this variable will not be escaped. | 745 escaping enabled this variable will not be escaped. |
704 """ | 746 """ |
705 return Markup(value) | 747 return Markup(value) |
706 | 748 |
707 | 749 |
708 def do_mark_unsafe(value): | 750 def do_mark_unsafe(value): |
709 """Mark a value as unsafe. This is the reverse operation for :func:`safe`."
"" | 751 """Mark a value as unsafe. This is the reverse operation for :func:`safe`."
"" |
710 return unicode(value) | 752 return text_type(value) |
711 | 753 |
712 | 754 |
713 def do_reverse(value): | 755 def do_reverse(value): |
714 """Reverse the object or return an iterator the iterates over it the other | 756 """Reverse the object or return an iterator the iterates over it the other |
715 way round. | 757 way round. |
716 """ | 758 """ |
717 if isinstance(value, basestring): | 759 if isinstance(value, string_types): |
718 return value[::-1] | 760 return value[::-1] |
719 try: | 761 try: |
720 return reversed(value) | 762 return reversed(value) |
721 except TypeError: | 763 except TypeError: |
722 try: | 764 try: |
723 rv = list(value) | 765 rv = list(value) |
724 rv.reverse() | 766 rv.reverse() |
725 return rv | 767 return rv |
726 except TypeError: | 768 except TypeError: |
727 raise FilterArgumentError('argument must be iterable') | 769 raise FilterArgumentError('argument must be iterable') |
(...skipping 17 matching lines...) Expand all Loading... |
745 except AttributeError: | 787 except AttributeError: |
746 pass | 788 pass |
747 else: | 789 else: |
748 if environment.sandboxed and not \ | 790 if environment.sandboxed and not \ |
749 environment.is_safe_attribute(obj, name, value): | 791 environment.is_safe_attribute(obj, name, value): |
750 return environment.unsafe_undefined(obj, name) | 792 return environment.unsafe_undefined(obj, name) |
751 return value | 793 return value |
752 return environment.undefined(obj=obj, name=name) | 794 return environment.undefined(obj=obj, name=name) |
753 | 795 |
754 | 796 |
| 797 @contextfilter |
| 798 def do_map(*args, **kwargs): |
| 799 """Applies a filter on a sequence of objects or looks up an attribute. |
| 800 This is useful when dealing with lists of objects but you are really |
| 801 only interested in a certain value of it. |
| 802 |
| 803 The basic usage is mapping on an attribute. Imagine you have a list |
| 804 of users but you are only interested in a list of usernames: |
| 805 |
| 806 .. sourcecode:: jinja |
| 807 |
| 808 Users on this page: {{ users|map(attribute='username')|join(', ') }} |
| 809 |
| 810 Alternatively you can let it invoke a filter by passing the name of the |
| 811 filter and the arguments afterwards. A good example would be applying a |
| 812 text conversion filter on a sequence: |
| 813 |
| 814 .. sourcecode:: jinja |
| 815 |
| 816 Users on this page: {{ titles|map('lower')|join(', ') }} |
| 817 |
| 818 .. versionadded:: 2.7 |
| 819 """ |
| 820 context = args[0] |
| 821 seq = args[1] |
| 822 |
| 823 if len(args) == 2 and 'attribute' in kwargs: |
| 824 attribute = kwargs.pop('attribute') |
| 825 if kwargs: |
| 826 raise FilterArgumentError('Unexpected keyword argument %r' % |
| 827 next(iter(kwargs))) |
| 828 func = make_attrgetter(context.environment, attribute) |
| 829 else: |
| 830 try: |
| 831 name = args[2] |
| 832 args = args[3:] |
| 833 except LookupError: |
| 834 raise FilterArgumentError('map requires a filter argument') |
| 835 func = lambda item: context.environment.call_filter( |
| 836 name, item, args, kwargs, context=context) |
| 837 |
| 838 if seq: |
| 839 for item in seq: |
| 840 yield func(item) |
| 841 |
| 842 |
| 843 @contextfilter |
| 844 def do_select(*args, **kwargs): |
| 845 """Filters a sequence of objects by appying a test to either the object |
| 846 or the attribute and only selecting the ones with the test succeeding. |
| 847 |
| 848 Example usage: |
| 849 |
| 850 .. sourcecode:: jinja |
| 851 |
| 852 {{ numbers|select("odd") }} |
| 853 |
| 854 .. versionadded:: 2.7 |
| 855 """ |
| 856 return _select_or_reject(args, kwargs, lambda x: x, False) |
| 857 |
| 858 |
| 859 @contextfilter |
| 860 def do_reject(*args, **kwargs): |
| 861 """Filters a sequence of objects by appying a test to either the object |
| 862 or the attribute and rejecting the ones with the test succeeding. |
| 863 |
| 864 Example usage: |
| 865 |
| 866 .. sourcecode:: jinja |
| 867 |
| 868 {{ numbers|reject("odd") }} |
| 869 |
| 870 .. versionadded:: 2.7 |
| 871 """ |
| 872 return _select_or_reject(args, kwargs, lambda x: not x, False) |
| 873 |
| 874 |
| 875 @contextfilter |
| 876 def do_selectattr(*args, **kwargs): |
| 877 """Filters a sequence of objects by appying a test to either the object |
| 878 or the attribute and only selecting the ones with the test succeeding. |
| 879 |
| 880 Example usage: |
| 881 |
| 882 .. sourcecode:: jinja |
| 883 |
| 884 {{ users|selectattr("is_active") }} |
| 885 {{ users|selectattr("email", "none") }} |
| 886 |
| 887 .. versionadded:: 2.7 |
| 888 """ |
| 889 return _select_or_reject(args, kwargs, lambda x: x, True) |
| 890 |
| 891 |
| 892 @contextfilter |
| 893 def do_rejectattr(*args, **kwargs): |
| 894 """Filters a sequence of objects by appying a test to either the object |
| 895 or the attribute and rejecting the ones with the test succeeding. |
| 896 |
| 897 .. sourcecode:: jinja |
| 898 |
| 899 {{ users|rejectattr("is_active") }} |
| 900 {{ users|rejectattr("email", "none") }} |
| 901 |
| 902 .. versionadded:: 2.7 |
| 903 """ |
| 904 return _select_or_reject(args, kwargs, lambda x: not x, True) |
| 905 |
| 906 |
| 907 def _select_or_reject(args, kwargs, modfunc, lookup_attr): |
| 908 context = args[0] |
| 909 seq = args[1] |
| 910 if lookup_attr: |
| 911 try: |
| 912 attr = args[2] |
| 913 except LookupError: |
| 914 raise FilterArgumentError('Missing parameter for attribute name') |
| 915 transfunc = make_attrgetter(context.environment, attr) |
| 916 off = 1 |
| 917 else: |
| 918 off = 0 |
| 919 transfunc = lambda x: x |
| 920 |
| 921 try: |
| 922 name = args[2 + off] |
| 923 args = args[3 + off:] |
| 924 func = lambda item: context.environment.call_test( |
| 925 name, item, args, kwargs) |
| 926 except LookupError: |
| 927 func = bool |
| 928 |
| 929 if seq: |
| 930 for item in seq: |
| 931 if modfunc(func(transfunc(item))): |
| 932 yield item |
| 933 |
| 934 |
755 FILTERS = { | 935 FILTERS = { |
756 'attr': do_attr, | 936 'attr': do_attr, |
757 'replace': do_replace, | 937 'replace': do_replace, |
758 'upper': do_upper, | 938 'upper': do_upper, |
759 'lower': do_lower, | 939 'lower': do_lower, |
760 'escape': escape, | 940 'escape': escape, |
761 'e': escape, | 941 'e': escape, |
762 'forceescape': do_forceescape, | 942 'forceescape': do_forceescape, |
763 'capitalize': do_capitalize, | 943 'capitalize': do_capitalize, |
764 'title': do_title, | 944 'title': do_title, |
765 'default': do_default, | 945 'default': do_default, |
766 'd': do_default, | 946 'd': do_default, |
767 'join': do_join, | 947 'join': do_join, |
768 'count': len, | 948 'count': len, |
769 'dictsort': do_dictsort, | 949 'dictsort': do_dictsort, |
770 'sort': do_sort, | 950 'sort': do_sort, |
771 'length': len, | 951 'length': len, |
772 'reverse': do_reverse, | 952 'reverse': do_reverse, |
773 'center': do_center, | 953 'center': do_center, |
774 'indent': do_indent, | 954 'indent': do_indent, |
775 'title': do_title, | 955 'title': do_title, |
776 'capitalize': do_capitalize, | 956 'capitalize': do_capitalize, |
777 'first': do_first, | 957 'first': do_first, |
778 'last': do_last, | 958 'last': do_last, |
| 959 'map': do_map, |
779 'random': do_random, | 960 'random': do_random, |
| 961 'reject': do_reject, |
| 962 'rejectattr': do_rejectattr, |
780 'filesizeformat': do_filesizeformat, | 963 'filesizeformat': do_filesizeformat, |
781 'pprint': do_pprint, | 964 'pprint': do_pprint, |
782 'truncate': do_truncate, | 965 'truncate': do_truncate, |
783 'wordwrap': do_wordwrap, | 966 'wordwrap': do_wordwrap, |
784 'wordcount': do_wordcount, | 967 'wordcount': do_wordcount, |
785 'int': do_int, | 968 'int': do_int, |
786 'float': do_float, | 969 'float': do_float, |
787 'string': soft_unicode, | 970 'string': soft_unicode, |
788 'list': do_list, | 971 'list': do_list, |
789 'urlize': do_urlize, | 972 'urlize': do_urlize, |
790 'format': do_format, | 973 'format': do_format, |
791 'trim': do_trim, | 974 'trim': do_trim, |
792 'striptags': do_striptags, | 975 'striptags': do_striptags, |
| 976 'select': do_select, |
| 977 'selectattr': do_selectattr, |
793 'slice': do_slice, | 978 'slice': do_slice, |
794 'batch': do_batch, | 979 'batch': do_batch, |
795 'sum': do_sum, | 980 'sum': do_sum, |
796 'abs': abs, | 981 'abs': abs, |
797 'round': do_round, | 982 'round': do_round, |
798 'groupby': do_groupby, | 983 'groupby': do_groupby, |
799 'safe': do_mark_safe, | 984 'safe': do_mark_safe, |
800 'xmlattr': do_xmlattr | 985 'xmlattr': do_xmlattr, |
| 986 'urlencode': do_urlencode |
801 } | 987 } |
OLD | NEW |