Add urlescape to default filters #85

Merged
merged 2 commits into from Jan 7, 2012
View
@@ -10,6 +10,7 @@
"""
import re
import math
+import urllib
from random import choice
from operator import itemgetter
from itertools import imap, groupby
@@ -69,6 +70,23 @@ def do_forceescape(value):
value = value.__html__()
return escape(unicode(value))
+def do_urlescape(value):
+ """Escape strings for use in URLs (uses UTF-8 encoding)."""
+ def utf8(o):
+ return unicode(o).encode('utf8')
+
+ if isinstance(value, basestring):
+ return urllib.quote(utf8(value))
+
+ if hasattr(value, 'items'):
+ # convert dictionaries to list of 2-tuples
+ value = value.items()
+
+ if hasattr(value, 'next'):
+ # convert generators to list
+ value = list(value)
+
+ return urllib.urlencode([(utf8(k), utf8(v)) for (k, v) in value])
@evalcontextfilter
def do_replace(eval_ctx, s, old, new, count=None):
@@ -797,5 +815,6 @@ def do_attr(environment, obj, name):
'round': do_round,
'groupby': do_groupby,
'safe': do_mark_safe,
- 'xmlattr': do_xmlattr
+ 'xmlattr': do_xmlattr,
+ 'urlescape': do_urlescape
}
@@ -366,7 +366,17 @@ def test_safe(self):
assert tmpl.render() == '<div>foo</div>'
tmpl = env.from_string('{{ "<div>foo</div>" }}')
assert tmpl.render() == '&lt;div&gt;foo&lt;/div&gt;'
-
+
+ def test_urlescape(self):
+ env = Environment(autoescape=True)
+ tmpl = env.from_string('{{ "Hello, world!"|urlescape }}')
+ assert tmpl.render() == 'Hello%2C%20world%21'
+ tmpl = env.from_string('{{ o|urlescape }}')
+ assert tmpl.render(o=u"Hello, world\u203d") == "Hello%2C%20world%E2%80%BD"
+ assert tmpl.render(o=(("f", 1),)) == "f=1"
+ assert tmpl.render(o=(('f', 1), ("z", 2))) == "f=1&amp;z=2"
+ assert tmpl.render(o=((u"\u203d", 1),)) == "%E2%80%BD=1"
+ assert tmpl.render(o={u"\u203d": 1}) == "%E2%80%BD=1"
def suite():
suite = unittest.TestSuite()