From 1d4c6389c946128da73c6b3355f5200868e64112 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 7 Jan 2012 17:46:40 +0100 Subject: [PATCH] Reworked implementation of the urlescape filter, made it Python3 compatible, documented it. --- CHANGES | 3 +++ jinja2/filters.py | 35 ++++++++++++++++++----------------- jinja2/testsuite/filters.py | 4 +++- jinja2/utils.py | 19 +++++++++++++++++++ 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/CHANGES b/CHANGES index 7f3388d..8838a21 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,9 @@ Version 2.7 advertised. - Fixed filesizeformat. - Added a non-silent option for babel extraction. +- Added `urlescape` filter that automatically quotes values for + URL safe usage with utf-8 as only supported encoding. If applications + want to change this encoding they can override the filter. Version 2.6 ----------- diff --git a/jinja2/filters.py b/jinja2/filters.py index 74f3113..bbe5a8c 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -10,11 +10,11 @@ """ import re import math -import urllib from random import choice from operator import itemgetter from itertools import imap, groupby -from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode +from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \ + unicode_urlescape from jinja2.runtime import Undefined from jinja2.exceptions import FilterArgumentError @@ -72,22 +72,23 @@ def do_forceescape(value): def do_urlescape(value): - """Escape strings for use in URLs (uses UTF-8 encoding).""" - def utf8(o): - return unicode(o).encode('utf8') + """Escape strings for use in URLs (uses UTF-8 encoding). It accepts both + dictionaries and regular strings as well as pairwise iterables. - 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) + .. versionadded:: 2.7 + """ + itemiter = None + if isinstance(value, dict): + itemiter = value.iteritems() + elif not isinstance(value, basestring): + try: + itemiter = iter(value) + except TypeError: + pass + if itemiter is None: + return unicode_urlescape(value) + return u'&'.join(unicode_urlescape(k) + '=' + + unicode_urlescape(v) for k, v in itemiter) @evalcontextfilter diff --git a/jinja2/testsuite/filters.py b/jinja2/testsuite/filters.py index 59795be..15451e5 100644 --- a/jinja2/testsuite/filters.py +++ b/jinja2/testsuite/filters.py @@ -366,7 +366,7 @@ class FilterTestCase(JinjaTestCase): assert tmpl.render() == '
foo
' tmpl = env.from_string('{{ "
foo
" }}') assert tmpl.render() == '<div>foo</div>' - + def test_urlescape(self): env = Environment(autoescape=True) tmpl = env.from_string('{{ "Hello, world!"|urlescape }}') @@ -377,6 +377,8 @@ class FilterTestCase(JinjaTestCase): assert tmpl.render(o=(('f', 1), ("z", 2))) == "f=1&z=2" assert tmpl.render(o=((u"\u203d", 1),)) == "%E2%80%BD=1" assert tmpl.render(o={u"\u203d": 1}) == "%E2%80%BD=1" + assert tmpl.render(o={0: 1}) == "0=1" + def suite(): suite = unittest.TestSuite() diff --git a/jinja2/utils.py b/jinja2/utils.py index 49e9e9a..1554a14 100644 --- a/jinja2/utils.py +++ b/jinja2/utils.py @@ -11,6 +11,10 @@ import re import sys import errno +try: + from urllib.parse import quote_from_bytes as url_quote +except ImportError: + from urllib import quote as url_quote try: from thread import allocate_lock except ImportError: @@ -349,6 +353,21 @@ def generate_lorem_ipsum(n=5, html=True, min=20, max=100): return Markup(u'\n'.join(u'

%s

' % escape(x) for x in result)) +def unicode_urlescape(obj, charset='utf-8'): + """URL escapes a single bytestring or unicode string with the + given charset if applicable to URL safe quoting under all rules + that need to be considered under all supported Python versions. + + If non strings are provided they are converted to their unicode + representation first. + """ + if not isinstance(obj, basestring): + obj = unicode(obj) + if isinstance(obj, unicode): + obj = obj.encode(charset) + return unicode(url_quote(obj)) + + class LRUCache(object): """A simple LRU Cache implementation.""" -- 2.26.2