updated filters: wordwraps uses the wordwrap module and urlize marks the result as...
authorArmin Ronacher <armin.ronacher@active-4.com>
Sat, 3 May 2008 15:10:05 +0000 (17:10 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sat, 3 May 2008 15:10:05 +0000 (17:10 +0200)
--HG--
branch : trunk

docs/api.rst
jinja2/filters.py
jinja2/lexer.py
jinja2/loaders.py
jinja2/parser.py
jinja2/utils.py

index 6cab9837ff489ecfbcb97ce3511a6971e055c1d1..fa601ddfe2b5b224e64b7745ec683de099e2920c 100644 (file)
@@ -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
 ---------
index 09b1babce9a2cbe0c192324b7cf6d00ce80549ea..50171563ef9c4e59229ad8847c5ec7ef24cd7ea6 100644 (file)
@@ -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
 
-        <ul{{ {'class': 'my_list', 'missing': None,
+        <ul{{ {'class': 'my_list', 'missing': none,
                 'id': 'list-%d'|format(variable)}|xmlattr }}>
         ...
         </ul>
@@ -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):
index 3b65b95b3f318116f16f7ed19c1404511e4149ff..f01b85dade86454a9d7086b1d46ad886b2185760 100644 (file)
@@ -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):
index cdda94abab6c6755d0dc2ad488e4191e9480ad60..45959a5df58bf795edfeb6c8e8623e4454b054a1 100644 (file)
@@ -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.
index 4239e25d8b13211ef00432a25934767913c836c3..05d2e3293d31d4d6bb73bb1c8027d40c7c7a62a0 100644 (file)
@@ -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')
index d9a45a8188a4aba131279b9b678d4afff0d10512..610e4a1c73ff1b8d6f608c5eb1c3021668c04b12 100644 (file)
@@ -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