From 8090986d39f04fb0cac0af1cb0e00836de24e43c Mon Sep 17 00:00:00 2001 From: Christoph Hack Date: Mon, 14 Apr 2008 01:35:10 +0200 Subject: [PATCH] continued convertig filters --HG-- branch : trunk --- jinja2/compiler.py | 10 +++++--- jinja2/filters.py | 30 ++-------------------- jinja2/utils.py | 59 +++++++++++++++++++++++++++++++++++++++++++ tests/test_filters.py | 8 +----- 4 files changed, 68 insertions(+), 39 deletions(-) diff --git a/jinja2/compiler.py b/jinja2/compiler.py index 4ddacd9..162192d 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -981,12 +981,14 @@ class CodeGenerator(NodeVisitor): def visit_Filter(self, node, frame, initial=None): self.write('f_%s(' % node.name) - if initial is not None: + func = self.environment.filters.get(node.name) + if getattr(func, 'contextfilter', False): + self.write('context, ') + if isinstance(node.node, nodes.Filter): + self.visit_Filter(node.node, frame, initial) + elif node.node is None: self.write(initial) else: - func = self.environment.filters.get(node.name) - if getattr(func, 'contextfilter', False): - self.write('context, ') self.visit(node.node, frame) self.signature(node, frame) self.write(')') diff --git a/jinja2/filters.py b/jinja2/filters.py index db0ea22..300e0cd 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -15,8 +15,8 @@ try: except ImportError: itemgetter = lambda a: lambda b: b[a] from urllib import urlencode, quote -from jinja2.utils import escape, pformat -from jinja2.nodes import Undefined +from jinja2.utils import escape, pformat, urlize +from jinja2.runtime import Undefined @@ -287,31 +287,6 @@ def do_random(seq): return env.undefined_singleton -def do_urlencode(value): - """ - urlencode a string or directory. - - .. sourcecode:: jinja - - {{ {'foo': 'bar', 'blub': 'blah'}|urlencode }} - -> foo=bar&blub=blah - - {{ 'Hello World' }} - -> Hello%20World - """ - if isinstance(value, dict): - tmp = {} - for key, value in value.iteritems(): - # XXX env.charset? - key = unicode(key).encode(env.charset) - value = unicode(value).encode(env.charset) - tmp[key] = value - return urlencode(tmp) - else: - # XXX: env.charset? - return quote(unicode(value).encode(env.charset)) - - def do_jsonencode(value): """ JSON dump a variable. just works if simplejson is installed. @@ -825,7 +800,6 @@ FILTERS = { 'first': do_first, 'last': do_last, 'random': do_random, - 'urlencode': do_urlencode, 'jsonencode': do_jsonencode, 'filesizeformat': do_filesizeformat, 'pprint': do_pprint, diff --git a/jinja2/utils.py b/jinja2/utils.py index 90f30e9..b597ed0 100644 --- a/jinja2/utils.py +++ b/jinja2/utils.py @@ -8,6 +8,8 @@ :copyright: 2008 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ +import re +import string def escape(obj, attribute=False): @@ -32,3 +34,60 @@ def pformat(obj, verbose=False): except ImportError: from pprint import pformat return pformat(obj) + + +_word_split_re = re.compile(r'(\s+)') + +_punctuation_re = re.compile( + '^(?P(?:%s)*)(?P.*?)(?P(?:%s)*)$' % ( + '|'.join([re.escape(p) for p in ('(', '<', '<')]), + '|'.join([re.escape(p) for p in ('.', ',', ')', '>', '\n', '>')]) + ) +) + +_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') + + +def urlize(text, trim_url_limit=None, nofollow=False): + """ + Converts any URLs in text into clickable links. Works on http://, + https:// and www. links. Links can have trailing punctuation (periods, + commas, close-parens) and leading punctuation (opening parens) and + it'll still do the right thing. + + If trim_url_limit is not None, the URLs in link text will be limited + to trim_url_limit characters. + + If nofollow is True, the URLs in link text will get a rel="nofollow" + attribute. + """ + trim_url = lambda x, limit=trim_url_limit: limit is not None \ + and (x[:limit] + (len(x) >=limit and '...' + or '')) or x + words = _word_split_re.split(text) + nofollow_attr = nofollow and ' rel="nofollow"' or '' + for i, word in enumerate(words): + match = _punctuation_re.match(word) + if match: + lead, middle, trail = match.groups() + if middle.startswith('www.') or ( + '@' not in middle and + not middle.startswith('http://') and + len(middle) > 0 and + middle[0] in string.letters + string.digits and ( + middle.endswith('.org') or + middle.endswith('.net') or + middle.endswith('.com') + )): + middle = '%s' % (middle, + nofollow_attr, trim_url(middle)) + if middle.startswith('http://') or \ + middle.startswith('https://'): + middle = '%s' % (middle, + nofollow_attr, trim_url(middle)) + if '@' in middle and not middle.startswith('www.') and \ + not ':' in middle and _simple_email_re.match(middle): + middle = '%s' % (middle, middle) + if lead + middle + trail != word: + words[i] = lead + middle + trail + return u''.join(words) diff --git a/tests/test_filters.py b/tests/test_filters.py index 2378d10..7a7ea9f 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -49,7 +49,6 @@ TRUNCATE = '''{{ data|truncate(15, true, ">>>") }}|\ {{ data|truncate(15, false, ">>>") }}|\ {{ smalldata|truncate(15) }}''' UPPER = '''{{ "foo"|upper }}''' -URLENCODE = '''{{ "f#b"|urlencode }}''' URLIZE = '''{{ "foo http://www.example.com/ bar"|urlize }}''' WORDCOUNT = '''{{ "foo bar baz"|wordcount }}''' BLOCK = '''{% filter lower|escape %}{% endfilter %}''' @@ -203,7 +202,7 @@ def test_reverse(env): def test_string(env): tmpl = env.from_string(STRING) - assert tmpl.render(foo=range(10)) == str(range(10)) + assert tmpl.render(foo=range(10)) == unicode(xrange(10)) def test_title(env): @@ -228,11 +227,6 @@ def test_upper(env): assert tmpl.render() == 'FOO' -def test_urlencode(env): - tmpl = env.from_string(URLENCODE) - assert tmpl.render() == 'f%23b' - - def test_urlize(env): tmpl = env.from_string(URLIZE) assert tmpl.render() == 'foo '\ -- 2.26.2