1# -*- coding: utf-8 -*- 2""" 3 jinja2.filters 4 ~~~~~~~~~~~~~~ 5 6 Bundled jinja filters. 7 8 :copyright: (c) 2010 by the Jinja Team. 9 :license: BSD, see LICENSE for more details. 10""" 11import re 12import math 13from random import choice 14from operator import itemgetter 15from itertools import imap, groupby 16from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode 17from jinja2.runtime import Undefined 18from jinja2.exceptions import FilterArgumentError, SecurityError 19 20 21_word_re = re.compile(r'\w+(?u)') 22 23 24def contextfilter(f): 25 """Decorator for marking context dependent filters. The current 26 :class:`Context` will be passed as first argument. 27 """ 28 f.contextfilter = True 29 return f 30 31 32def evalcontextfilter(f): 33 """Decorator for marking eval-context dependent filters. An eval 34 context object is passed as first argument. For more information 35 about the eval context, see :ref:`eval-context`. 36 37 .. versionadded:: 2.4 38 """ 39 f.evalcontextfilter = True 40 return f 41 42 43def environmentfilter(f): 44 """Decorator for marking evironment dependent filters. The current 45 :class:`Environment` is passed to the filter as first argument. 46 """ 47 f.environmentfilter = True 48 return f 49 50 51def do_forceescape(value): 52 """Enforce HTML escaping. This will probably double escape variables.""" 53 if hasattr(value, '__html__'): 54 value = value.__html__() 55 return escape(unicode(value)) 56 57 58@evalcontextfilter 59def do_replace(eval_ctx, s, old, new, count=None): 60 """Return a copy of the value with all occurrences of a substring 61 replaced with a new one. The first argument is the substring 62 that should be replaced, the second is the replacement string. 63 If the optional third argument ``count`` is given, only the first 64 ``count`` occurrences are replaced: 65 66 .. sourcecode:: jinja 67 68 {{ "Hello World"|replace("Hello", "Goodbye") }} 69 -> Goodbye World 70 71 {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} 72 -> d'oh, d'oh, aaargh 73 """ 74 if count is None: 75 count = -1 76 if not eval_ctx.autoescape: 77 return unicode(s).replace(unicode(old), unicode(new), count) 78 if hasattr(old, '__html__') or hasattr(new, '__html__') and \ 79 not hasattr(s, '__html__'): 80 s = escape(s) 81 else: 82 s = soft_unicode(s) 83 return s.replace(soft_unicode(old), soft_unicode(new), count) 84 85 86def do_upper(s): 87 """Convert a value to uppercase.""" 88 return soft_unicode(s).upper() 89 90 91def do_lower(s): 92 """Convert a value to lowercase.""" 93 return soft_unicode(s).lower() 94 95 96@evalcontextfilter 97def do_xmlattr(_eval_ctx, d, autospace=True): 98 """Create an SGML/XML attribute string based on the items in a dict. 99 All values that are neither `none` nor `undefined` are automatically 100 escaped: 101 102 .. sourcecode:: html+jinja 103 104 <ul{{ {'class': 'my_list', 'missing': none, 105 'id': 'list-%d'|format(variable)}|xmlattr }}> 106 ... 107 </ul> 108 109 Results in something like this: 110 111 .. sourcecode:: html 112 113 <ul class="my_list" id="list-42"> 114 ... 115 </ul> 116 117 As you can see it automatically prepends a space in front of the item 118 if the filter returned something unless the second parameter is false. 119 """ 120 rv = u' '.join( 121 u'%s="%s"' % (escape(key), escape(value)) 122 for key, value in d.iteritems() 123 if value is not None and not isinstance(value, Undefined) 124 ) 125 if autospace and rv: 126 rv = u' ' + rv 127 if _eval_ctx.autoescape: 128 rv = Markup(rv) 129 return rv 130 131 132def do_capitalize(s): 133 """Capitalize a value. The first character will be uppercase, all others 134 lowercase. 135 """ 136 return soft_unicode(s).capitalize() 137 138 139def do_title(s): 140 """Return a titlecased version of the value. I.e. words will start with 141 uppercase letters, all remaining characters are lowercase. 142 """ 143 return soft_unicode(s).title() 144 145 146def do_dictsort(value, case_sensitive=False, by='key'): 147 """Sort a dict and yield (key, value) pairs. Because python dicts are 148 unsorted you may want to use this function to order them by either 149 key or value: 150 151 .. sourcecode:: jinja 152 153 {% for item in mydict|dictsort %} 154 sort the dict by key, case insensitive 155 156 {% for item in mydict|dicsort(true) %} 157 sort the dict by key, case sensitive 158 159 {% for item in mydict|dictsort(false, 'value') %} 160 sort the dict by key, case insensitive, sorted 161 normally and ordered by value. 162 """ 163 if by == 'key': 164 pos = 0 165 elif by == 'value': 166 pos = 1 167 else: 168 raise FilterArgumentError('You can only sort by either ' 169 '"key" or "value"') 170 def sort_func(item): 171 value = item[pos] 172 if isinstance(value, basestring) and not case_sensitive: 173 value = value.lower() 174 return value 175 176 return sorted(value.items(), key=sort_func) 177 178 179def do_sort(value, case_sensitive=False): 180 """Sort an iterable. If the iterable is made of strings the second 181 parameter can be used to control the case sensitiveness of the 182 comparison which is disabled by default. 183 184 .. sourcecode:: jinja 185 186 {% for item in iterable|sort %} 187 ... 188 {% endfor %} 189 """ 190 if not case_sensitive: 191 def sort_func(item): 192 if isinstance(item, basestring): 193 item = item.lower() 194 return item 195 else: 196 sort_func = None 197 return sorted(seq, key=sort_func) 198 199 200def do_default(value, default_value=u'', boolean=False): 201 """If the value is undefined it will return the passed default value, 202 otherwise the value of the variable: 203 204 .. sourcecode:: jinja 205 206 {{ my_variable|default('my_variable is not defined') }} 207 208 This will output the value of ``my_variable`` if the variable was 209 defined, otherwise ``'my_variable is not defined'``. If you want 210 to use default with variables that evaluate to false you have to 211 set the second parameter to `true`: 212 213 .. sourcecode:: jinja 214 215 {{ ''|default('the string was empty', true) }} 216 """ 217 if (boolean and not value) or isinstance(value, Undefined): 218 return default_value 219 return value 220 221 222@evalcontextfilter 223def do_join(eval_ctx, value, d=u''): 224 """Return a string which is the concatenation of the strings in the 225 sequence. The separator between elements is an empty string per 226 default, you can define it with the optional parameter: 227 228 .. sourcecode:: jinja 229 230 {{ [1, 2, 3]|join('|') }} 231 -> 1|2|3 232 233 {{ [1, 2, 3]|join }} 234 -> 123 235 """ 236 # no automatic escaping? joining is a lot eaiser then 237 if not eval_ctx.autoescape: 238 return unicode(d).join(imap(unicode, value)) 239 240 # if the delimiter doesn't have an html representation we check 241 # if any of the items has. If yes we do a coercion to Markup 242 if not hasattr(d, '__html__'): 243 value = list(value) 244 do_escape = False 245 for idx, item in enumerate(value): 246 if hasattr(item, '__html__'): 247 do_escape = True 248 else: 249 value[idx] = unicode(item) 250 if do_escape: 251 d = escape(d) 252 else: 253 d = unicode(d) 254 return d.join(value) 255 256 # no html involved, to normal joining 257 return soft_unicode(d).join(imap(soft_unicode, value)) 258 259 260def do_center(value, width=80): 261 """Centers the value in a field of a given width.""" 262 return unicode(value).center(width) 263 264 265@environmentfilter 266def do_first(environment, seq): 267 """Return the first item of a sequence.""" 268 try: 269 return iter(seq).next() 270 except StopIteration: 271 return environment.undefined('No first item, sequence was empty.') 272 273 274@environmentfilter 275def do_last(environment, seq): 276 """Return the last item of a sequence.""" 277 try: 278 return iter(reversed(seq)).next() 279 except StopIteration: 280 return environment.undefined('No last item, sequence was empty.') 281 282 283@environmentfilter 284def do_random(environment, seq): 285 """Return a random item from the sequence.""" 286 try: 287 return choice(seq) 288 except IndexError: 289 return environment.undefined('No random item, sequence was empty.') 290 291 292def do_filesizeformat(value, binary=False): 293 """Format the value like a 'human-readable' file size (i.e. 13 KB, 294 4.1 MB, 102 bytes, etc). Per default decimal prefixes are used (mega, 295 giga, etc.), if the second parameter is set to `True` the binary 296 prefixes are used (mebi, gibi). 297 """ 298 bytes = float(value) 299 base = binary and 1024 or 1000 300 middle = binary and 'i' or '' 301 if bytes < base: 302 return "%d Byte%s" % (bytes, bytes != 1 and 's' or '') 303 elif bytes < base * base: 304 return "%.1f K%sB" % (bytes / base, middle) 305 elif bytes < base * base * base: 306 return "%.1f M%sB" % (bytes / (base * base), middle) 307 return "%.1f G%sB" % (bytes / (base * base * base), middle) 308 309 310def do_pprint(value, verbose=False): 311 """Pretty print a variable. Useful for debugging. 312 313 With Jinja 1.2 onwards you can pass it a parameter. If this parameter 314 is truthy the output will be more verbose (this requires `pretty`) 315 """ 316 return pformat(value, verbose=verbose) 317 318 319@evalcontextfilter 320def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False): 321 """Converts URLs in plain text into clickable links. 322 323 If you pass the filter an additional integer it will shorten the urls 324 to that number. Also a third argument exists that makes the urls 325 "nofollow": 326 327 .. sourcecode:: jinja 328 329 {{ mytext|urlize(40, true) }} 330 links are shortened to 40 chars and defined with rel="nofollow" 331 """ 332 rv = urlize(value, trim_url_limit, nofollow) 333 if eval_ctx.autoescape: 334 rv = Markup(rv) 335 return rv 336 337 338def do_indent(s, width=4, indentfirst=False): 339 """Return a copy of the passed string, each line indented by 340 4 spaces. The first line is not indented. If you want to 341 change the number of spaces or indent the first line too 342 you can pass additional parameters to the filter: 343 344 .. sourcecode:: jinja 345 346 {{ mytext|indent(2, true) }} 347 indent by two spaces and indent the first line too. 348 """ 349 indention = u' ' * width 350 rv = (u'\n' + indention).join(s.splitlines()) 351 if indentfirst: 352 rv = indention + rv 353 return rv 354 355 356def do_truncate(s, length=255, killwords=False, end='...'): 357 """Return a truncated copy of the string. The length is specified 358 with the first parameter which defaults to ``255``. If the second 359 parameter is ``true`` the filter will cut the text at length. Otherwise 360 it will try to save the last word. If the text was in fact 361 truncated it will append an ellipsis sign (``"..."``). If you want a 362 different ellipsis sign than ``"..."`` you can specify it using the 363 third parameter. 364 365 .. sourcecode jinja:: 366 367 {{ mytext|truncate(300, false, '»') }} 368 truncate mytext to 300 chars, don't split up words, use a 369 right pointing double arrow as ellipsis sign. 370 """ 371 if len(s) <= length: 372 return s 373 elif killwords: 374 return s[:length] + end 375 words = s.split(' ') 376 result = [] 377 m = 0 378 for word in words: 379 m += len(word) + 1 380 if m > length: 381 break 382 result.append(word) 383 result.append(end) 384 return u' '.join(result) 385 386 387def do_wordwrap(s, width=79, break_long_words=True): 388 """ 389 Return a copy of the string passed to the filter wrapped after 390 ``79`` characters. You can override this default using the first 391 parameter. If you set the second parameter to `false` Jinja will not 392 split words apart if they are longer than `width`. 393 """ 394 import textwrap 395 return u'\n'.join(textwrap.wrap(s, width=width, expand_tabs=False, 396 replace_whitespace=False, 397 break_long_words=break_long_words)) 398 399 400def do_wordcount(s): 401 """Count the words in that string.""" 402 return len(_word_re.findall(s)) 403 404 405def do_int(value, default=0): 406 """Convert the value into an integer. If the 407 conversion doesn't work it will return ``0``. You can 408 override this default using the first parameter. 409 """ 410 try: 411 return int(value) 412 except (TypeError, ValueError): 413 # this quirk is necessary so that "42.23"|int gives 42. 414 try: 415 return int(float(value)) 416 except (TypeError, ValueError): 417 return default 418 419 420def do_float(value, default=0.0): 421 """Convert the value into a floating point number. If the 422 conversion doesn't work it will return ``0.0``. You can 423 override this default using the first parameter. 424 """ 425 try: 426 return float(value) 427 except (TypeError, ValueError): 428 return default 429 430 431def do_format(value, *args, **kwargs): 432 """ 433 Apply python string formatting on an object: 434 435 .. sourcecode:: jinja 436 437 {{ "%s - %s"|format("Hello?", "Foo!") }} 438 -> Hello? - Foo! 439 """ 440 if args and kwargs: 441 raise FilterArgumentError('can\'t handle positional and keyword ' 442 'arguments at the same time') 443 return soft_unicode(value) % (kwargs or args) 444 445 446def do_trim(value): 447 """Strip leading and trailing whitespace.""" 448 return soft_unicode(value).strip() 449 450 451def do_striptags(value): 452 """Strip SGML/XML tags and replace adjacent whitespace by one space. 453 """ 454 if hasattr(value, '__html__'): 455 value = value.__html__() 456 return Markup(unicode(value)).striptags() 457 458 459def do_slice(value, slices, fill_with=None): 460 """Slice an iterator and return a list of lists containing 461 those items. Useful if you want to create a div containing 462 three ul tags that represent columns: 463 464 .. sourcecode:: html+jinja 465 466 <div class="columwrapper"> 467 {%- for column in items|slice(3) %} 468 <ul class="column-{{ loop.index }}"> 469 {%- for item in column %} 470 <li>{{ item }}</li> 471 {%- endfor %} 472 </ul> 473 {%- endfor %} 474 </div> 475 476 If you pass it a second argument it's used to fill missing 477 values on the last iteration. 478 """ 479 seq = list(value) 480 length = len(seq) 481 items_per_slice = length // slices 482 slices_with_extra = length % slices 483 offset = 0 484 for slice_number in xrange(slices): 485 start = offset + slice_number * items_per_slice 486 if slice_number < slices_with_extra: 487 offset += 1 488 end = offset + (slice_number + 1) * items_per_slice 489 tmp = seq[start:end] 490 if fill_with is not None and slice_number >= slices_with_extra: 491 tmp.append(fill_with) 492 yield tmp 493 494 495def do_batch(value, linecount, fill_with=None): 496 """ 497 A filter that batches items. It works pretty much like `slice` 498 just the other way round. It returns a list of lists with the 499 given number of items. If you provide a second parameter this 500 is used to fill missing items. See this example: 501 502 .. sourcecode:: html+jinja 503 504 <table> 505 {%- for row in items|batch(3, ' ') %} 506 <tr> 507 {%- for column in row %} 508 <td>{{ column }}</td> 509 {%- endfor %} 510 </tr> 511 {%- endfor %} 512 </table> 513 """ 514 result = [] 515 tmp = [] 516 for item in value: 517 if len(tmp) == linecount: 518 yield tmp 519 tmp = [] 520 tmp.append(item) 521 if tmp: 522 if fill_with is not None and len(tmp) < linecount: 523 tmp += [fill_with] * (linecount - len(tmp)) 524 yield tmp 525 526 527def do_round(value, precision=0, method='common'): 528 """Round the number to a given precision. The first 529 parameter specifies the precision (default is ``0``), the 530 second the rounding method: 531 532 - ``'common'`` rounds either up or down 533 - ``'ceil'`` always rounds up 534 - ``'floor'`` always rounds down 535 536 If you don't specify a method ``'common'`` is used. 537 538 .. sourcecode:: jinja 539 540 {{ 42.55|round }} 541 -> 43.0 542 {{ 42.55|round(1, 'floor') }} 543 -> 42.5 544 545 Note that even if rounded to 0 precision, a float is returned. If 546 you need a real integer, pipe it through `int`: 547 548 .. sourcecode:: jinja 549 550 {{ 42.55|round|int }} 551 -> 43 552 """ 553 if not method in ('common', 'ceil', 'floor'): 554 raise FilterArgumentError('method must be common, ceil or floor') 555 if precision < 0: 556 raise FilterArgumentError('precision must be a postive integer ' 557 'or zero.') 558 if method == 'common': 559 return round(value, precision) 560 func = getattr(math, method) 561 if precision: 562 return func(value * 10 * precision) / (10 * precision) 563 else: 564 return func(value) 565 566 567def do_sort(value, reverse=False): 568 """Sort a sequence. Per default it sorts ascending, if you pass it 569 true as first argument it will reverse the sorting. 570 """ 571 return sorted(value, reverse=reverse) 572 573 574@environmentfilter 575def do_groupby(environment, value, attribute): 576 """Group a sequence of objects by a common attribute. 577 578 If you for example have a list of dicts or objects that represent persons 579 with `gender`, `first_name` and `last_name` attributes and you want to 580 group all users by genders you can do something like the following 581 snippet: 582 583 .. sourcecode:: html+jinja 584 585 <ul> 586 {% for group in persons|groupby('gender') %} 587 <li>{{ group.grouper }}<ul> 588 {% for person in group.list %} 589 <li>{{ person.first_name }} {{ person.last_name }}</li> 590 {% endfor %}</ul></li> 591 {% endfor %} 592 </ul> 593 594 Additionally it's possible to use tuple unpacking for the grouper and 595 list: 596 597 .. sourcecode:: html+jinja 598 599 <ul> 600 {% for grouper, list in persons|groupby('gender') %} 601 ... 602 {% endfor %} 603 </ul> 604 605 As you can see the item we're grouping by is stored in the `grouper` 606 attribute and the `list` contains all the objects that have this grouper 607 in common. 608 """ 609 expr = lambda x: environment.getitem(x, attribute) 610 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr))) 611 612 613class _GroupTuple(tuple): 614 __slots__ = () 615 grouper = property(itemgetter(0)) 616 list = property(itemgetter(1)) 617 618 def __new__(cls, (key, value)): 619 return tuple.__new__(cls, (key, list(value))) 620 621 622def do_list(value): 623 """Convert the value into a list. If it was a string the returned list 624 will be a list of characters. 625 """ 626 return list(value) 627 628 629def do_mark_safe(value): 630 """Mark the value as safe which means that in an environment with automatic 631 escaping enabled this variable will not be escaped. 632 """ 633 return Markup(value) 634 635 636def do_mark_unsafe(value): 637 """Mark a value as unsafe. This is the reverse operation for :func:`safe`.""" 638 return unicode(value) 639 640 641def do_reverse(value): 642 """Reverse the object or return an iterator the iterates over it the other 643 way round. 644 """ 645 if isinstance(value, basestring): 646 return value[::-1] 647 try: 648 return reversed(value) 649 except TypeError: 650 try: 651 rv = list(value) 652 rv.reverse() 653 return rv 654 except TypeError: 655 raise FilterArgumentError('argument must be iterable') 656 657 658@environmentfilter 659def do_attr(environment, obj, name): 660 """Get an attribute of an object. ``foo|attr("bar")`` works like 661 ``foo["bar"]`` just that always an attribute is returned and items are not 662 looked up. 663 664 See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details. 665 """ 666 try: 667 name = str(name) 668 except UnicodeError: 669 pass 670 else: 671 try: 672 value = getattr(obj, name) 673 except AttributeError: 674 pass 675 else: 676 if environment.sandboxed and not \ 677 environment.is_safe_attribute(obj, name, value): 678 return environment.unsafe_undefined(obj, name) 679 return value 680 return environment.undefined(obj=obj, name=name) 681 682 683FILTERS = { 684 'attr': do_attr, 685 'replace': do_replace, 686 'upper': do_upper, 687 'lower': do_lower, 688 'escape': escape, 689 'e': escape, 690 'forceescape': do_forceescape, 691 'capitalize': do_capitalize, 692 'title': do_title, 693 'default': do_default, 694 'd': do_default, 695 'join': do_join, 696 'count': len, 697 'dictsort': do_dictsort, 698 'sort': do_sort, 699 'length': len, 700 'reverse': do_reverse, 701 'center': do_center, 702 'indent': do_indent, 703 'title': do_title, 704 'capitalize': do_capitalize, 705 'first': do_first, 706 'last': do_last, 707 'random': do_random, 708 'filesizeformat': do_filesizeformat, 709 'pprint': do_pprint, 710 'truncate': do_truncate, 711 'wordwrap': do_wordwrap, 712 'wordcount': do_wordcount, 713 'int': do_int, 714 'float': do_float, 715 'string': soft_unicode, 716 'list': do_list, 717 'urlize': do_urlize, 718 'format': do_format, 719 'trim': do_trim, 720 'striptags': do_striptags, 721 'slice': do_slice, 722 'batch': do_batch, 723 'sum': sum, 724 'abs': abs, 725 'round': do_round, 726 'sort': do_sort, 727 'groupby': do_groupby, 728 'safe': do_mark_safe, 729 'xmlattr': do_xmlattr 730} 731