Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge pull request #751 from bmispelon/ticket-6271

  • Loading branch information...
commit 5278776a882ce8ba60fdd9a82dda4d8bac513976 2 parents 8d17114 + 069280a
Aymeric Augustin authored
23  django/template/defaulttags.py
@@ -496,6 +496,7 @@ def autoescape(parser, token):
496 496
     """
497 497
     Force autoescape behavior for this block.
498 498
     """
  499
+    # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments
499 500
     args = token.contents.split()
500 501
     if len(args) != 2:
501 502
         raise TemplateSyntaxError("'autoescape' tag requires exactly one argument.")
@@ -645,6 +646,7 @@ def do_filter(parser, token):
645 646
     Instead, use the ``autoescape`` tag to manage autoescaping for blocks of
646 647
     template code.
647 648
     """
  649
+    # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments
648 650
     _, rest = token.contents.split(None, 1)
649 651
     filter_expr = parser.compile_filter("var|%s" % (rest))
650 652
     for func, unused in filter_expr.filters:
@@ -972,7 +974,7 @@ def ifchanged(parser, token):
972 974
                 {% endifchanged %}
973 975
             {% endfor %}
974 976
     """
975  
-    bits = token.contents.split()
  977
+    bits = token.split_contents()
976 978
     nodelist_true = parser.parse(('else', 'endifchanged'))
977 979
     token = parser.next_token()
978 980
     if token.contents == 'else':
@@ -1029,6 +1031,7 @@ def load(parser, token):
1029 1031
         {% load byline from news %}
1030 1032
 
1031 1033
     """
  1034
+    # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments
1032 1035
     bits = token.contents.split()
1033 1036
     if len(bits) >= 4 and bits[-2] == "from":
1034 1037
         try:
@@ -1127,17 +1130,16 @@ def regroup(parser, token):
1127 1130
         {% regroup people|dictsort:"gender" by gender as grouped %}
1128 1131
 
1129 1132
     """
1130  
-    firstbits = token.contents.split(None, 3)
1131  
-    if len(firstbits) != 4:
  1133
+    bits = token.split_contents()
  1134
+    if len(bits) != 6:
1132 1135
         raise TemplateSyntaxError("'regroup' tag takes five arguments")
1133  
-    target = parser.compile_filter(firstbits[1])
1134  
-    if firstbits[2] != 'by':
  1136
+    target = parser.compile_filter(bits[1])
  1137
+    if bits[2] != 'by':
1135 1138
         raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'")
1136  
-    lastbits_reversed = firstbits[3][::-1].split(None, 2)
1137  
-    if lastbits_reversed[1][::-1] != 'as':
  1139
+    if bits[4] != 'as':
1138 1140
         raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must"
1139 1141
                                   " be 'as'")
1140  
-    var_name = lastbits_reversed[0][::-1]
  1142
+    var_name = bits[5]
1141 1143
     # RegroupNode will take each item in 'target', put it in the context under
1142 1144
     # 'var_name', evaluate 'var_name'.'expression' in the current context, and
1143 1145
     # group by the resulting value. After all items are processed, it will
@@ -1146,7 +1148,7 @@ def regroup(parser, token):
1146 1148
     # doesn't provide a context-aware equivalent of Python's getattr.
1147 1149
     expression = parser.compile_filter(var_name +
1148 1150
                                        VARIABLE_ATTRIBUTE_SEPARATOR +
1149  
-                                       lastbits_reversed[2][::-1])
  1151
+                                       bits[3])
1150 1152
     return RegroupNode(target, expression, var_name)
1151 1153
 
1152 1154
 @register.tag
@@ -1202,6 +1204,7 @@ def templatetag(parser, token):
1202 1204
         ``closecomment``    ``#}``
1203 1205
         ==================  =======
1204 1206
     """
  1207
+    # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments
1205 1208
     bits = token.contents.split()
1206 1209
     if len(bits) != 2:
1207 1210
         raise TemplateSyntaxError("'templatetag' statement takes one argument")
@@ -1343,7 +1346,7 @@ def widthratio(parser, token):
1343 1346
     the image in the above example will be 88 pixels wide
1344 1347
     (because 175/200 = .875; .875 * 100 = 87.5 which is rounded up to 88).
1345 1348
     """
1346  
-    bits = token.contents.split()
  1349
+    bits = token.split_contents()
1347 1350
     if len(bits) != 4:
1348 1351
         raise TemplateSyntaxError("widthratio takes three arguments")
1349 1352
     tag, this_value_expr, max_value_expr, max_width = bits
1  django/template/loader_tags.py
@@ -174,6 +174,7 @@ def do_block(parser, token):
174 174
     """
175 175
     Define a block that can be overridden by child templates.
176 176
     """
  177
+    # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments
177 178
     bits = token.contents.split()
178 179
     if len(bits) != 2:
179 180
         raise TemplateSyntaxError("'%s' tag takes only one argument" % bits[0])
14  django/templatetags/cache.py
... ...
@@ -1,8 +1,7 @@
1 1
 from __future__ import unicode_literals
2 2
 
3 3
 import hashlib
4  
-from django.template import Library, Node, TemplateSyntaxError, Variable, VariableDoesNotExist
5  
-from django.template import resolve_variable
  4
+from django.template import Library, Node, TemplateSyntaxError, VariableDoesNotExist
6 5
 from django.core.cache import cache
7 6
 from django.utils.encoding import force_bytes
8 7
 from django.utils.http import urlquote
@@ -12,7 +11,7 @@
12 11
 class CacheNode(Node):
13 12
     def __init__(self, nodelist, expire_time_var, fragment_name, vary_on):
14 13
         self.nodelist = nodelist
15  
-        self.expire_time_var = Variable(expire_time_var)
  14
+        self.expire_time_var = expire_time_var
16 15
         self.fragment_name = fragment_name
17 16
         self.vary_on = vary_on
18 17
 
@@ -26,7 +25,7 @@ def render(self, context):
26 25
         except (ValueError, TypeError):
27 26
             raise TemplateSyntaxError('"cache" tag got a non-integer timeout value: %r' % expire_time)
28 27
         # Build a key for this fragment and all vary-on's.
29  
-        key = ':'.join([urlquote(resolve_variable(var, context)) for var in self.vary_on])
  28
+        key = ':'.join([urlquote(var.resolve(context)) for var in self.vary_on])
30 29
         args = hashlib.md5(force_bytes(key))
31 30
         cache_key = 'template.cache.%s.%s' % (self.fragment_name, args.hexdigest())
32 31
         value = cache.get(cache_key)
@@ -59,7 +58,10 @@ def do_cache(parser, token):
59 58
     """
60 59
     nodelist = parser.parse(('endcache',))
61 60
     parser.delete_first_token()
62  
-    tokens = token.contents.split()
  61
+    tokens = token.split_contents()
63 62
     if len(tokens) < 3:
64 63
         raise TemplateSyntaxError("'%r' tag requires at least 2 arguments." % tokens[0])
65  
-    return CacheNode(nodelist, tokens[1], tokens[2], tokens[3:])
  64
+    return CacheNode(nodelist,
  65
+        parser.compile_filter(tokens[1]),
  66
+        parser.compile_filter(tokens[2]),
  67
+        [parser.compile_filter(token) for token in tokens[3:]])
15  django/templatetags/i18n.py
@@ -24,7 +24,7 @@ def render(self, context):
24 24
 
25 25
 class GetLanguageInfoNode(Node):
26 26
     def __init__(self, lang_code, variable):
27  
-        self.lang_code = Variable(lang_code)
  27
+        self.lang_code = lang_code
28 28
         self.variable = variable
29 29
 
30 30
     def render(self, context):
@@ -35,7 +35,7 @@ def render(self, context):
35 35
 
36 36
 class GetLanguageInfoListNode(Node):
37 37
     def __init__(self, languages, variable):
38  
-        self.languages = Variable(languages)
  38
+        self.languages = languages
39 39
         self.variable = variable
40 40
 
41 41
     def get_language_info(self, language):
@@ -185,6 +185,7 @@ def do_get_available_languages(parser, token):
185 185
     your setting file (or the default settings) and
186 186
     put it into the named variable.
187 187
     """
  188
+    # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments
188 189
     args = token.contents.split()
189 190
     if len(args) != 3 or args[1] != 'as':
190 191
         raise TemplateSyntaxError("'get_available_languages' requires 'as variable' (got %r)" % args)
@@ -204,10 +205,10 @@ def do_get_language_info(parser, token):
204 205
         {{ l.name_local }}
205 206
         {{ l.bidi|yesno:"bi-directional,uni-directional" }}
206 207
     """
207  
-    args = token.contents.split()
  208
+    args = token.split_contents()
208 209
     if len(args) != 5 or args[1] != 'for' or args[3] != 'as':
209 210
         raise TemplateSyntaxError("'%s' requires 'for string as variable' (got %r)" % (args[0], args[1:]))
210  
-    return GetLanguageInfoNode(args[2], args[4])
  211
+    return GetLanguageInfoNode(parser.compile_filter(args[2]), args[4])
211 212
 
212 213
 @register.tag("get_language_info_list")
213 214
 def do_get_language_info_list(parser, token):
@@ -227,10 +228,10 @@ def do_get_language_info_list(parser, token):
227 228
           {{ l.bidi|yesno:"bi-directional,uni-directional" }}
228 229
         {% endfor %}
229 230
     """
230  
-    args = token.contents.split()
  231
+    args = token.split_contents()
231 232
     if len(args) != 5 or args[1] != 'for' or args[3] != 'as':
232 233
         raise TemplateSyntaxError("'%s' requires 'for sequence as variable' (got %r)" % (args[0], args[1:]))
233  
-    return GetLanguageInfoListNode(args[2], args[4])
  234
+    return GetLanguageInfoListNode(parser.compile_filter(args[2]), args[4])
234 235
 
235 236
 @register.filter
236 237
 def language_name(lang_code):
@@ -257,6 +258,7 @@ def do_get_current_language(parser, token):
257 258
     put it's value into the ``language`` context
258 259
     variable.
259 260
     """
  261
+    # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments
260 262
     args = token.contents.split()
261 263
     if len(args) != 3 or args[1] != 'as':
262 264
         raise TemplateSyntaxError("'get_current_language' requires 'as variable' (got %r)" % args)
@@ -275,6 +277,7 @@ def do_get_current_language_bidi(parser, token):
275 277
     put it's value into the ``bidi`` context variable.
276 278
     True indicates right-to-left layout, otherwise left-to-right
277 279
     """
  280
+    # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments
278 281
     args = token.contents.split()
279 282
     if len(args) != 3 or args[1] != 'as':
280 283
         raise TemplateSyntaxError("'get_current_language_bidi' requires 'as variable' (got %r)" % args)
1  django/templatetags/static.py
@@ -27,6 +27,7 @@ def handle_token(cls, parser, token, name):
27 27
         """
28 28
         Class method to parse prefix node and return a Node.
29 29
         """
  30
+        # token.split_contents() isn't useful here because tags using this method don't accept variable as arguments
30 31
         tokens = token.contents.split()
31 32
         if len(tokens) > 1 and tokens[1] != 'as':
32 33
             raise template.TemplateSyntaxError(
1  django/templatetags/tz.py
@@ -190,6 +190,7 @@ def get_current_timezone_tag(parser, token):
190 190
     This will fetch the currently active time zone and put its name
191 191
     into the ``TIME_ZONE`` context variable.
192 192
     """
  193
+    # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments
193 194
     args = token.contents.split()
194 195
     if len(args) != 3 or args[1] != 'as':
195 196
         raise TemplateSyntaxError("'get_current_timezone' requires "
7  tests/regressiontests/templates/templatetags/custom.py
@@ -12,6 +12,13 @@
12 12
 def trim(value, num):
13 13
     return value[:num]
14 14
 
  15
+@register.filter
  16
+def noop(value, param=None):
  17
+    """A noop filter that always return its first argument and does nothing with
  18
+    its second (optional) one.
  19
+    Useful for testing out whitespace in filter arguments (see #19882)."""
  20
+    return value
  21
+
15 22
 @register.simple_tag
16 23
 def no_params():
17 24
     """Expected no_params __doc__"""
25  tests/regressiontests/templates/tests.py
@@ -844,7 +844,7 @@ def get_template_tests(self):
844 844
             'for-tag-empty02': ("{% for val in values %}{{ val }}{% empty %}values array empty{% endfor %}", {"values": []}, "values array empty"),
845 845
             'for-tag-empty03': ("{% for val in values %}{{ val }}{% empty %}values array not found{% endfor %}", {}, "values array not found"),
846 846
             # Ticket 19882
847  
-            'for-tag-filter-ws': ("{% for x in ''|add:'a b c' %}{{ x }}{% endfor %}", {}, 'a b c'),
  847
+            'for-tag-filter-ws': ("{% load custom %}{% for x in s|noop:'x y' %}{{ x }}{% endfor %}", {'s': 'abc'}, 'abc'),
848 848
 
849 849
             ### IF TAG ################################################################
850 850
             'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),
@@ -1013,6 +1013,9 @@ def get_template_tests(self):
1013 1013
 
1014 1014
             'ifchanged-else04': ('{% for id in ids %}{% ifchanged %}***{{ id }}*{% else %}...{% endifchanged %}{{ forloop.counter }}{% endfor %}', {'ids': [1,1,2,2,2,3,4]}, '***1*1...2***2*3...4...5***3*6***4*7'),
1015 1015
 
  1016
+            # Test whitespace in filter arguments
  1017
+            'ifchanged-filter-ws': ('{% load custom %}{% for n in num %}{% ifchanged n|noop:"x y" %}..{% endifchanged %}{{ n }}{% endfor %}', {'num': (1,2,3)}, '..1..2..3'),
  1018
+
1016 1019
             ### IFEQUAL TAG ###########################################################
1017 1020
             'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""),
1018 1021
             'ifequal02': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 1}, "yes"),
@@ -1353,6 +1356,10 @@ def get_template_tests(self):
1353 1356
             'i18n36': ('{% load i18n %}{% trans "Page not found" as page_not_found noop %}{{ page_not_found }}', {'LANGUAGE_CODE': 'de'}, "Page not found"),
1354 1357
             'i18n37': ('{% load i18n %}{% trans "Page not found" as page_not_found %}{% blocktrans %}Error: {{ page_not_found }}{% endblocktrans %}', {'LANGUAGE_CODE': 'de'}, "Error: Seite nicht gefunden"),
1355 1358
 
  1359
+            # Test whitespace in filter arguments
  1360
+            'i18n38': ('{% load i18n custom %}{% get_language_info for "de"|noop:"x y" as l %}{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}', {}, 'de: German/Deutsch bidi=False'),
  1361
+            'i18n38_2': ('{% load i18n custom %}{% get_language_info_list for langcodes|noop:"x y" as langs %}{% for l in langs %}{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}', {'langcodes': ['it', 'no']}, 'it: Italian/italiano bidi=False; no: Norwegian/norsk bidi=False; '),
  1362
+
1356 1363
             ### HANDLING OF TEMPLATE_STRING_IF_INVALID ###################################
1357 1364
 
1358 1365
             'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo','INVALID')),
@@ -1434,6 +1441,16 @@ def get_template_tests(self):
1434 1441
                                     {'foo': 'z', 'bar': ['a', 'd']}]},
1435 1442
                           'abc:xy,ad:z,'),
1436 1443
 
  1444
+            # Test syntax
  1445
+            'regroup05': ('{% regroup data by bar as %}', {},
  1446
+                           template.TemplateSyntaxError),
  1447
+            'regroup06': ('{% regroup data by bar thisaintright grouped %}', {},
  1448
+                           template.TemplateSyntaxError),
  1449
+            'regroup07': ('{% regroup data thisaintright bar as grouped %}', {},
  1450
+                           template.TemplateSyntaxError),
  1451
+            'regroup08': ('{% regroup data by bar as grouped toomanyargs %}', {},
  1452
+                           template.TemplateSyntaxError),
  1453
+
1437 1454
             ### SSI TAG ########################################################
1438 1455
 
1439 1456
             # Test normal behavior
@@ -1502,6 +1519,9 @@ def get_template_tests(self):
1502 1519
             'widthratio14a': ('{% widthratio a b c %}', {'a':0,'b':100,'c':'c'}, template.TemplateSyntaxError),
1503 1520
             'widthratio14b': ('{% widthratio a b c %}', {'a':0,'b':100,'c':None}, template.TemplateSyntaxError),
1504 1521
 
  1522
+            # Test whitespace in filter argument
  1523
+            'widthratio15': ('{% load custom %}{% widthratio a|noop:"x y" b 0 %}', {'a':50,'b':100}, '0'),
  1524
+
1505 1525
             ### WITH TAG ########################################################
1506 1526
             'with01': ('{% with key=dict.key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'),
1507 1527
             'legacywith01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'),
@@ -1603,6 +1623,9 @@ def get_template_tests(self):
1603 1623
             # Regression test for #11270.
1604 1624
             'cache17': ('{% load cache %}{% cache 10 long_cache_key poem %}Some Content{% endcache %}', {'poem': 'Oh freddled gruntbuggly/Thy micturations are to me/As plurdled gabbleblotchits/On a lurgid bee/That mordiously hath bitled out/Its earted jurtles/Into a rancid festering/Or else I shall rend thee in the gobberwarts with my blurglecruncheon/See if I dont.'}, 'Some Content'),
1605 1625
 
  1626
+            # Test whitespace in filter arguments
  1627
+            'cache18': ('{% load cache custom %}{% cache 2|noop:"x y" cache18 %}cache18{% endcache %}', {}, 'cache18'),
  1628
+
1606 1629
 
1607 1630
             ### AUTOESCAPE TAG ##############################################
1608 1631
             'autoescape-tag01': ("{% autoescape off %}hello{% endautoescape %}", {}, "hello"),

0 notes on commit 5278776

Please sign in to comment.
Something went wrong with that request. Please try again.