From cda43df129f9df7bb1b81879031d37f072e093f0 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 3 May 2008 17:10:05 +0200 Subject: [PATCH] updated filters: wordwraps uses the wordwrap module and urlize marks the result as HTML now if in autoescape mode --HG-- branch : trunk --- docs/api.rst | 14 ++++++++------ jinja2/filters.py | 47 +++++++++++++++++++++-------------------------- jinja2/lexer.py | 4 ++-- jinja2/loaders.py | 6 +----- jinja2/parser.py | 5 ++--- jinja2/utils.py | 11 ++++++----- 6 files changed, 40 insertions(+), 47 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 6cab983..fa601dd 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -170,6 +170,14 @@ Loaders are responsible for loading templates from a resource such as the file system. The environment will keep the compiled modules in memory like Python's `sys.modules`. Unlike `sys.modules` however this cache is limited in size by default and templates are automatically reloaded. +All loaders are subclasses of :class:`BaseLoader`. If you want to create your + +own loader, subclass :class:`BaseLoader` and override `get_source`. + +.. autoclass:: jinja2.loaders.BaseLoader + :members: get_source, load + +Here a list of the builtin loaders Jinja2 provides: .. autoclass:: jinja2.loaders.FileSystemLoader @@ -183,12 +191,6 @@ size by default and templates are automatically reloaded. .. autoclass:: jinja2.loaders.ChoiceLoader -All loaders are subclasses of :class:`BaseLoader`. If you want to create your -own loader, subclass :class:`BaseLoader` and override `get_source`. - -.. autoclass:: jinja2.loaders.BaseLoader - :members: get_source, load - Utilities --------- diff --git a/jinja2/filters.py b/jinja2/filters.py index 09b1bab..5017156 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -10,6 +10,7 @@ """ import re import math +import textwrap from random import choice from operator import itemgetter from itertools import imap, groupby @@ -94,7 +95,7 @@ def do_xmlattr(_environment, d, autospace=True): .. sourcecode:: html+jinja - ... @@ -291,7 +292,8 @@ def do_pprint(value, verbose=False): return pformat(value, verbose=verbose) -def do_urlize(value, trim_url_limit=None, nofollow=False): +@environmentfilter +def do_urlize(environment, value, trim_url_limit=None, nofollow=False): """Converts URLs in plain text into clickable links. If you pass the filter an additional integer it will shorten the urls @@ -300,10 +302,13 @@ def do_urlize(value, trim_url_limit=None, nofollow=False): .. sourcecode:: jinja - {{ mytext|urlize(40, True) }} + {{ mytext|urlize(40, true) }} links are shortened to 40 chars and defined with rel="nofollow" """ - return urlize(soft_unicode(value), trim_url_limit, nofollow) + rv = urlize(soft_unicode(value), trim_url_limit, nofollow) + if environment.autoescape: + rv = Markup(rv) + return rv def do_indent(s, width=4, indentfirst=False): @@ -314,7 +319,7 @@ def do_indent(s, width=4, indentfirst=False): .. sourcecode:: jinja - {{ mytext|indent(2, True) }} + {{ mytext|indent(2, true) }} indent by two spaces and indent the first line too. """ indention = ' ' * width @@ -354,26 +359,16 @@ def do_truncate(s, length=255, killwords=False, end='...'): return u' '.join(result) -def do_wordwrap(s, pos=79, hard=False): +def do_wordwrap(s, width=79, break_long_words=True): """ Return a copy of the string passed to the filter wrapped after - ``79`` characters. You can override this default using the first - parameter. If you set the second parameter to `true` Jinja will - also split words apart (usually a bad idea because it makes - reading hard). + ``79`` characters. You can override this default using the first + parameter. If you set the second parameter to `false` Jinja will not + split words apart if they are longer than `width`. """ - if len(s) < pos: - return s - if hard: - return u'\n'.join(s[idx:idx + pos] for idx in - xrange(0, len(s), pos)) - - # TODO: switch to wordwrap.wrap - # code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061 - return reduce(lambda line, word, pos=pos: u'%s%s%s' % - (line, u' \n'[(len(line)-line.rfind('\n') - 1 + - len(word.split('\n', 1)[0]) >= pos)], - word), s.split(' ')) + return textwrap.wrap(s, width=width, expand_tabs=False, + replace_whitespace=False, + break_long_words=break_long_words) def do_wordcount(s): @@ -416,10 +411,10 @@ def do_format(value, *args, **kwargs): {{ "%s - %s"|format("Hello?", "Foo!") }} -> Hello? - Foo! """ - if kwargs: - kwargs.update(idx, arg in enumerate(args)) - args = kwargs - return soft_unicode(value) % args + if args and kwargs: + raise FilterArgumentError('can\'t handle positional and keyword ' + 'arguments at the same time') + return soft_unicode(value) % (kwargs or args) def do_trim(value): diff --git a/jinja2/lexer.py b/jinja2/lexer.py index 3b65b95..f01b85d 100644 --- a/jinja2/lexer.py +++ b/jinja2/lexer.py @@ -137,7 +137,7 @@ class Token(tuple): token type or 'token_type:token_value'. This can only test against string values! """ - # here we do a regular string equality check as test_many is usually + # here we do a regular string equality check as test_any is usually # passed an iterable of not interned strings. if self.type == expr: return True @@ -145,7 +145,7 @@ class Token(tuple): return expr.split(':', 1) == [self.type, self.value] return False - def test_many(self, iterable): + def test_any(self, *iterable): """Test against multiple token expressions.""" for expr in iterable: if self.test(expr): diff --git a/jinja2/loaders.py b/jinja2/loaders.py index cdda94a..45959a5 100644 --- a/jinja2/loaders.py +++ b/jinja2/loaders.py @@ -5,10 +5,6 @@ Jinja loader classes. - XXX: move caching from the loaders to environment.get_template and add - environment overlays that allow to redefine escaping and other things but - shared the globals and filter mappings. - :copyright: 2008 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ @@ -259,7 +255,7 @@ class ChoiceLoader(BaseLoader): >>> loader = ChoiceLoader([ ... FileSystemLoader('/path/to/user/templates'), ... PackageLoader('myapplication') - ]) + .. ]) This is useful if you want to allow users to override builtin templates from a different location. diff --git a/jinja2/parser.py b/jinja2/parser.py index 4239e25..05d2e32 100644 --- a/jinja2/parser.py +++ b/jinja2/parser.py @@ -152,8 +152,7 @@ class Parser(object): return node def parse_import_context(self, node, default): - if (self.stream.current.test('name:with') or - self.stream.current.test('name:without')) and \ + if self.stream.current.test_any('name:with', 'name:without') and \ self.stream.look().test('name:context'): node.with_context = self.stream.next().value == 'with' self.stream.skip() @@ -722,7 +721,7 @@ class Parser(object): flush_data() self.stream.next() if end_tokens is not None and \ - self.stream.current.test_many(end_tokens): + self.stream.current.test_any(*end_tokens): return body body.append(self.parse_statement()) self.stream.expect('block_end') diff --git a/jinja2/utils.py b/jinja2/utils.py index d9a45a8..610e4a1 100644 --- a/jinja2/utils.py +++ b/jinja2/utils.py @@ -36,23 +36,24 @@ missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})() # concatenate a list of strings and convert them to unicode. # unfortunately there is a bug in python 2.4 and lower that causes # unicode.join trash the traceback. +_concat = u''.join try: def _test_gen_bug(): raise TypeError(_test_gen_bug) yield None - u''.join(_test_gen_bug()) + _concat(_test_gen_bug()) except TypeError, _error: - if _error.args and _error.args[0] is _test_gen_bug: - concat = u''.join - else: + if not _error.args or _error.args[0] is not _test_gen_bug: def concat(gen): try: - return u''.join(list(gen)) + return _concat(list(gen)) except: # this hack is needed so that the current frame # does not show up in the traceback. exc_type, exc_value, tb = sys.exc_info() raise exc_type, exc_value, tb.tb_next + else: + concat = _concat del _test_gen_bug, _error -- 2.26.2