summaryrefslogtreecommitdiff
path: root/django/template
diff options
context:
space:
mode:
authorJustin Bronn <jbronn@gmail.com>2008-01-02 16:57:53 +0000
committerJustin Bronn <jbronn@gmail.com>2008-01-02 16:57:53 +0000
commit7e322b5908bf5ec255c90c615d1b45b358ecee5f (patch)
tree3390dc70c5b4c4a8ff583308bd904eb749f97c04 /django/template
parentef0f46f1d0b9e41c811d3d6b833f1652ad46aec3 (diff)
gis: Merged revisions 6920-6989 via svnmerge from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6990 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/template')
-rw-r--r--django/template/__init__.py171
-rw-r--r--django/template/context.py6
-rw-r--r--django/template/debug.py97
-rw-r--r--django/template/defaultfilters.py6
-rw-r--r--django/template/defaulttags.py62
5 files changed, 166 insertions, 176 deletions
diff --git a/django/template/__init__.py b/django/template/__init__.py
index 9b1868cfce..750c48988d 100644
--- a/django/template/__init__.py
+++ b/django/template/__init__.py
@@ -154,15 +154,12 @@ class StringOrigin(Origin):
class Template(object):
def __init__(self, template_string, origin=None, name='<Unknown Template>'):
- "Compilation stage"
try:
template_string = smart_unicode(template_string)
except UnicodeDecodeError:
raise TemplateEncodingError("Templates can only be constructed from unicode or UTF-8 strings.")
- if settings.TEMPLATE_DEBUG and origin == None:
+ if settings.TEMPLATE_DEBUG and origin is None:
origin = StringOrigin(template_string)
- # Could do some crazy stack-frame stuff to record where this string
- # came from...
self.nodelist = compile_string(template_string, origin)
self.name = name
@@ -177,13 +174,18 @@ class Template(object):
def compile_string(template_string, origin):
"Compiles template_string into NodeList ready for rendering"
- lexer = lexer_factory(template_string, origin)
- parser = parser_factory(lexer.tokenize())
+ if settings.TEMPLATE_DEBUG:
+ from debug import DebugLexer, DebugParser
+ lexer_class, parser_class = DebugLexer, DebugParser
+ else:
+ lexer_class, parser_class = Lexer, Parser
+ lexer = lexer_class(template_string, origin)
+ parser = parser_class(lexer.tokenize())
return parser.parse()
class Token(object):
def __init__(self, token_type, contents):
- "The token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT"
+ # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or TOKEN_COMMENT.
self.token_type, self.contents = token_type, contents
def __str__(self):
@@ -200,7 +202,7 @@ class Lexer(object):
self.origin = origin
def tokenize(self):
- "Return a list of tokens from a given template_string"
+ "Return a list of tokens from a given template_string."
in_tag = False
result = []
for bit in tag_re.split(self.template_string):
@@ -226,30 +228,6 @@ class Lexer(object):
token = Token(TOKEN_TEXT, token_string)
return token
-class DebugLexer(Lexer):
- def __init__(self, template_string, origin):
- super(DebugLexer, self).__init__(template_string, origin)
-
- def tokenize(self):
- "Return a list of tokens from a given template_string"
- result, upto = [], 0
- for match in tag_re.finditer(self.template_string):
- start, end = match.span()
- if start > upto:
- result.append(self.create_token(self.template_string[upto:start], (upto, start), False))
- upto = start
- result.append(self.create_token(self.template_string[start:end], (start, end), True))
- upto = end
- last_bit = self.template_string[upto:]
- if last_bit:
- result.append(self.create_token(last_bit, (upto, upto + len(last_bit)), False))
- return result
-
- def create_token(self, token_string, source, in_tag):
- token = super(DebugLexer, self).create_token(token_string, in_tag)
- token.source = self.origin, source
- return token
-
class Parser(object):
def __init__(self, tokens):
self.tokens = tokens
@@ -319,17 +297,17 @@ class Parser(object):
def exit_command(self):
pass
- def error(self, token, msg ):
+ def error(self, token, msg):
return TemplateSyntaxError(msg)
def empty_variable(self, token):
- raise self.error( token, "Empty variable tag")
+ raise self.error(token, "Empty variable tag")
def empty_block_tag(self, token):
- raise self.error( token, "Empty block tag")
+ raise self.error(token, "Empty block tag")
def invalid_block_tag(self, token, command):
- raise self.error( token, "Invalid block tag: '%s'" % command)
+ raise self.error(token, "Invalid block tag: '%s'" % command)
def unclosed_block_tag(self, parse_until):
raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until))
@@ -358,57 +336,7 @@ class Parser(object):
if filter_name in self.filters:
return self.filters[filter_name]
else:
- raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name
-
-class DebugParser(Parser):
- def __init__(self, lexer):
- super(DebugParser, self).__init__(lexer)
- self.command_stack = []
-
- def enter_command(self, command, token):
- self.command_stack.append( (command, token.source) )
-
- def exit_command(self):
- self.command_stack.pop()
-
- def error(self, token, msg):
- return self.source_error(token.source, msg)
-
- def source_error(self, source,msg):
- e = TemplateSyntaxError(msg)
- e.source = source
- return e
-
- def create_nodelist(self):
- return DebugNodeList()
-
- def create_variable_node(self, contents):
- return DebugVariableNode(contents)
-
- def extend_nodelist(self, nodelist, node, token):
- node.source = token.source
- super(DebugParser, self).extend_nodelist(nodelist, node, token)
-
- def unclosed_block_tag(self, parse_until):
- command, source = self.command_stack.pop()
- msg = "Unclosed tag '%s'. Looking for one of: %s " % (command, ', '.join(parse_until))
- raise self.source_error( source, msg)
-
- def compile_function_error(self, token, e):
- if not hasattr(e, 'source'):
- e.source = token.source
-
-def lexer_factory(*args, **kwargs):
- if settings.TEMPLATE_DEBUG:
- return DebugLexer(*args, **kwargs)
- else:
- return Lexer(*args, **kwargs)
-
-def parser_factory(*args, **kwargs):
- if settings.TEMPLATE_DEBUG:
- return DebugParser(*args, **kwargs)
- else:
- return Parser(*args, **kwargs)
+ raise TemplateSyntaxError("Invalid filter: '%s'" % filter_name)
class TokenParser(object):
"""
@@ -426,7 +354,7 @@ class TokenParser(object):
def top(self):
"Overload this method to do the actual parsing and return the result."
- raise NotImplemented
+ raise NotImplementedError()
def more(self):
"Returns True if there is more stuff in the tag."
@@ -435,7 +363,7 @@ class TokenParser(object):
def back(self):
"Undoes the last microparser. Use this for lookahead and backtracking."
if not len(self.backout):
- raise TemplateSyntaxError, "back called without some previous parsing"
+ raise TemplateSyntaxError("back called without some previous parsing")
self.pointer = self.backout.pop()
def tag(self):
@@ -443,7 +371,7 @@ class TokenParser(object):
subject = self.subject
i = self.pointer
if i >= len(subject):
- raise TemplateSyntaxError, "expected another tag, found end of string: %s" % subject
+ raise TemplateSyntaxError("expected another tag, found end of string: %s" % subject)
p = i
while i < len(subject) and subject[i] not in (' ', '\t'):
i += 1
@@ -459,14 +387,14 @@ class TokenParser(object):
subject = self.subject
i = self.pointer
if i >= len(subject):
- raise TemplateSyntaxError, "Searching for value. Expected another value but found end of string: %s" % subject
+ raise TemplateSyntaxError("Searching for value. Expected another value but found end of string: %s" % subject)
if subject[i] in ('"', "'"):
p = i
i += 1
while i < len(subject) and subject[i] != subject[p]:
i += 1
if i >= len(subject):
- raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % (i, subject)
+ raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))
i += 1
res = subject[p:i]
while i < len(subject) and subject[i] in (' ', '\t'):
@@ -483,7 +411,7 @@ class TokenParser(object):
while i < len(subject) and subject[i] != c:
i += 1
if i >= len(subject):
- raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % (i, subject)
+ raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject))
i += 1
s = subject[p:i]
while i < len(subject) and subject[i] in (' ', '\t'):
@@ -542,8 +470,8 @@ class FilterExpression(object):
for match in matches:
start = match.start()
if upto != start:
- raise TemplateSyntaxError, "Could not parse some characters: %s|%s|%s" % \
- (token[:upto], token[upto:start], token[start:])
+ raise TemplateSyntaxError("Could not parse some characters: %s|%s|%s" % \
+ (token[:upto], token[upto:start], token[start:]))
if var == None:
var, constant, i18n_constant = match.group("var", "constant", "i18n_constant")
if i18n_constant:
@@ -552,9 +480,9 @@ class FilterExpression(object):
var = '"%s"' % constant.replace(r'\"', '"')
upto = match.end()
if var == None:
- raise TemplateSyntaxError, "Could not find variable at start of %s" % token
+ raise TemplateSyntaxError("Could not find variable at start of %s" % token)
elif var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_':
- raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % var
+ raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % var)
else:
filter_name = match.group("filter_name")
args = []
@@ -570,7 +498,7 @@ class FilterExpression(object):
filters.append( (filter_func,args))
upto = match.end()
if upto != len(token):
- raise TemplateSyntaxError, "Could not parse the remainder: '%s' from '%s'" % (token[upto:], token)
+ raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token))
self.filters = filters
self.var = Variable(var)
@@ -627,7 +555,7 @@ class FilterExpression(object):
provided.pop(0)
except IndexError:
# Not enough
- raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
+ raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen))
# Defaults can be overridden.
defaults = defaults and list(defaults) or []
@@ -636,7 +564,7 @@ class FilterExpression(object):
defaults.pop(0)
except IndexError:
# Too many.
- raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
+ raise TemplateSyntaxError("%s requires %d arguments, %d provided" % (name, len(nondefs), plen))
return True
args_check = staticmethod(args_check)
@@ -816,22 +744,6 @@ class NodeList(list):
def render_node(self, node, context):
return node.render(context)
-class DebugNodeList(NodeList):
- def render_node(self, node, context):
- try:
- result = node.render(context)
- except TemplateSyntaxError, e:
- if not hasattr(e, 'source'):
- e.source = node.source
- raise
- except Exception, e:
- from sys import exc_info
- wrapped = TemplateSyntaxError('Caught an exception while rendering: %s' % e)
- wrapped.source = node.source
- wrapped.exc_info = exc_info()
- raise wrapped
- return result
-
class TextNode(Node):
def __init__(self, s):
self.s = s
@@ -861,21 +773,6 @@ class VariableNode(Node):
else:
return force_unicode(output)
-class DebugVariableNode(VariableNode):
- def render(self, context):
- try:
- output = force_unicode(self.filter_expression.resolve(context))
- except TemplateSyntaxError, e:
- if not hasattr(e, 'source'):
- e.source = self.source
- raise
- except UnicodeDecodeError:
- return ''
- if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
- return escape(output)
- else:
- return output
-
def generic_tag_compiler(params, defaults, name, node_class, parser, token):
"Returns a template.Node subclass."
bits = token.split_contents()[1:]
@@ -887,7 +784,7 @@ def generic_tag_compiler(params, defaults, name, node_class, parser, token):
message = "%s takes %s arguments" % (name, bmin)
else:
message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
- raise TemplateSyntaxError, message
+ raise TemplateSyntaxError(message)
return node_class(bits)
class Library(object):
@@ -913,7 +810,7 @@ class Library(object):
self.tags[name] = compile_function
return compile_function
else:
- raise InvalidTemplateLibrary, "Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function)
+ raise InvalidTemplateLibrary("Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function))
def tag_function(self,func):
self.tags[getattr(func, "_decorated_function", func).__name__] = func
@@ -937,7 +834,7 @@ class Library(object):
self.filters[name] = filter_func
return filter_func
else:
- raise InvalidTemplateLibrary, "Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func)
+ raise InvalidTemplateLibrary("Unsupported arguments to Library.filter: (%r, %r)", (name, filter_func))
def filter_function(self, func):
self.filters[getattr(func, "_decorated_function", func).__name__] = func
@@ -966,7 +863,7 @@ class Library(object):
if params[0] == 'context':
params = params[1:]
else:
- raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
+ raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'")
class InclusionNode(Node):
def __init__(self, vars_to_resolve):
@@ -1003,12 +900,12 @@ def get_library(module_name):
try:
mod = __import__(module_name, {}, {}, [''])
except ImportError, e:
- raise InvalidTemplateLibrary, "Could not load template library from %s, %s" % (module_name, e)
+ raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e))
try:
lib = mod.register
libraries[module_name] = lib
except AttributeError:
- raise InvalidTemplateLibrary, "Template library %s does not have a variable named 'register'" % module_name
+ raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name)
return lib
def add_to_builtins(module_name):
diff --git a/django/template/context.py b/django/template/context.py
index 0e41a26618..6ba53f340b 100644
--- a/django/template/context.py
+++ b/django/template/context.py
@@ -9,7 +9,6 @@ class ContextPopException(Exception):
class Context(object):
"A stack container for variable context"
-
def __init__(self, dict_=None, autoescape=True):
dict_ = dict_ or {}
self.dicts = [dict_]
@@ -78,11 +77,11 @@ def get_standard_processors():
try:
mod = __import__(module, {}, {}, [attr])
except ImportError, e:
- raise ImproperlyConfigured, 'Error importing request processor module %s: "%s"' % (module, e)
+ raise ImproperlyConfigured('Error importing request processor module %s: "%s"' % (module, e))
try:
func = getattr(mod, attr)
except AttributeError:
- raise ImproperlyConfigured, 'Module "%s" does not define a "%s" callable request processor' % (module, attr)
+ raise ImproperlyConfigured('Module "%s" does not define a "%s" callable request processor' % (module, attr))
processors.append(func)
_standard_context_processors = tuple(processors)
return _standard_context_processors
@@ -102,4 +101,3 @@ class RequestContext(Context):
processors = tuple(processors)
for processor in get_standard_processors() + processors:
self.update(processor(request))
-
diff --git a/django/template/debug.py b/django/template/debug.py
new file mode 100644
index 0000000000..008059c28f
--- /dev/null
+++ b/django/template/debug.py
@@ -0,0 +1,97 @@
+from django.template import Lexer, Parser, tag_re, NodeList, VariableNode, TemplateSyntaxError
+from django.utils.encoding import force_unicode
+from django.utils.html import escape
+from django.utils.safestring import SafeData, EscapeData
+
+class DebugLexer(Lexer):
+ def __init__(self, template_string, origin):
+ super(DebugLexer, self).__init__(template_string, origin)
+
+ def tokenize(self):
+ "Return a list of tokens from a given template_string"
+ result, upto = [], 0
+ for match in tag_re.finditer(self.template_string):
+ start, end = match.span()
+ if start > upto:
+ result.append(self.create_token(self.template_string[upto:start], (upto, start), False))
+ upto = start
+ result.append(self.create_token(self.template_string[start:end], (start, end), True))
+ upto = end
+ last_bit = self.template_string[upto:]
+ if last_bit:
+ result.append(self.create_token(last_bit, (upto, upto + len(last_bit)), False))
+ return result
+
+ def create_token(self, token_string, source, in_tag):
+ token = super(DebugLexer, self).create_token(token_string, in_tag)
+ token.source = self.origin, source
+ return token
+
+class DebugParser(Parser):
+ def __init__(self, lexer):
+ super(DebugParser, self).__init__(lexer)
+ self.command_stack = []
+
+ def enter_command(self, command, token):
+ self.command_stack.append( (command, token.source) )
+
+ def exit_command(self):
+ self.command_stack.pop()
+
+ def error(self, token, msg):
+ return self.source_error(token.source, msg)
+
+ def source_error(self, source,msg):
+ e = TemplateSyntaxError(msg)
+ e.source = source
+ return e
+
+ def create_nodelist(self):
+ return DebugNodeList()
+
+ def create_variable_node(self, contents):
+ return DebugVariableNode(contents)
+
+ def extend_nodelist(self, nodelist, node, token):
+ node.source = token.source
+ super(DebugParser, self).extend_nodelist(nodelist, node, token)
+
+ def unclosed_block_tag(self, parse_until):
+ command, source = self.command_stack.pop()
+ msg = "Unclosed tag '%s'. Looking for one of: %s " % (command, ', '.join(parse_until))
+ raise self.source_error(source, msg)
+
+ def compile_function_error(self, token, e):
+ if not hasattr(e, 'source'):
+ e.source = token.source
+
+class DebugNodeList(NodeList):
+ def render_node(self, node, context):
+ try:
+ result = node.render(context)
+ except TemplateSyntaxError, e:
+ if not hasattr(e, 'source'):
+ e.source = node.source
+ raise
+ except Exception, e:
+ from sys import exc_info
+ wrapped = TemplateSyntaxError('Caught an exception while rendering: %s' % e)
+ wrapped.source = node.source
+ wrapped.exc_info = exc_info()
+ raise wrapped
+ return result
+
+class DebugVariableNode(VariableNode):
+ def render(self, context):
+ try:
+ output = force_unicode(self.filter_expression.resolve(context))
+ except TemplateSyntaxError, e:
+ if not hasattr(e, 'source'):
+ e.source = self.source
+ raise
+ except UnicodeDecodeError:
+ return ''
+ if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData):
+ return escape(output)
+ else:
+ return output
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index 2f211dadb5..5cee0ab244 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -254,7 +254,7 @@ urlize.is_safe=True
urlize.needs_autoescape = True
urlize = stringfilter(urlize)
-def urlizetrunc(value, limit):
+def urlizetrunc(value, limit, autoescape=None):
"""
Converts URLs into clickable links, truncating URLs to the given character
limit, and adding 'rel=nofollow' attribute to discourage spamming.
@@ -262,8 +262,10 @@ def urlizetrunc(value, limit):
Argument: Length to truncate URLs to.
"""
from django.utils.html import urlize
- return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True))
+ return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True,
+ autoescape=autoescape))
urlizetrunc.is_safe = True
+urlizetrunc.needs_autoescape = True
urlizetrunc = stringfilter(urlizetrunc)
def wordcount(value):
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index d91e30bbb5..726a37012b 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -84,19 +84,16 @@ class FirstOfNode(Node):
return u''
class ForNode(Node):
- def __init__(self, loopvars, sequence, reversed, nodelist_loop):
+ def __init__(self, loopvars, sequence, is_reversed, nodelist_loop):
self.loopvars, self.sequence = loopvars, sequence
- self.reversed = reversed
+ self.is_reversed = is_reversed
self.nodelist_loop = nodelist_loop
def __repr__(self):
- if self.reversed:
- reversed = ' reversed'
- else:
- reversed = ''
+ reversed_text = self.is_reversed and ' reversed' or ''
return "<For Node: for %s in %s, tail_len: %d%s>" % \
(', '.join(self.loopvars), self.sequence, len(self.nodelist_loop),
- reversed)
+ reversed_text)
def __iter__(self):
for node in self.nodelist_loop:
@@ -125,22 +122,23 @@ class ForNode(Node):
if not hasattr(values, '__len__'):
values = list(values)
len_values = len(values)
- if self.reversed:
+ if self.is_reversed:
values = reversed(values)
unpack = len(self.loopvars) > 1
+ # Create a forloop value in the context. We'll update counters on each
+ # iteration just below.
+ loop_dict = context['forloop'] = {'parentloop': parentloop}
for i, item in enumerate(values):
- context['forloop'] = {
- # Shortcuts for current loop iteration number.
- 'counter0': i,
- 'counter': i+1,
- # Reverse counter iteration numbers.
- 'revcounter': len_values - i,
- 'revcounter0': len_values - i - 1,
- # Boolean values designating first and last times through loop.
- 'first': (i == 0),
- 'last': (i == len_values - 1),
- 'parentloop': parentloop,
- }
+ # Shortcuts for current loop iteration number.
+ loop_dict['counter0'] = i
+ loop_dict['counter'] = i+1
+ # Reverse counter iteration numbers.
+ loop_dict['revcounter'] = len_values - i
+ loop_dict['revcounter0'] = len_values - i - 1
+ # Boolean values designating first and last times through loop.
+ loop_dict['first'] = (i == 0)
+ loop_dict['last'] = (i == len_values - 1)
+
if unpack:
# If there are multiple loop variables, unpack the item into
# them.
@@ -619,8 +617,8 @@ def do_for(parser, token):
raise TemplateSyntaxError("'for' statements should have at least four"
" words: %s" % token.contents)
- reversed = bits[-1] == 'reversed'
- in_index = reversed and -3 or -2
+ is_reversed = bits[-1] == 'reversed'
+ in_index = is_reversed and -3 or -2
if bits[in_index] != 'in':
raise TemplateSyntaxError("'for' statements should use the format"
" 'for x in y': %s" % token.contents)
@@ -634,7 +632,7 @@ def do_for(parser, token):
sequence = parser.compile_filter(bits[in_index+1])
nodelist_loop = parser.parse(('endfor',))
parser.delete_first_token()
- return ForNode(loopvars, sequence, reversed, nodelist_loop)
+ return ForNode(loopvars, sequence, is_reversed, nodelist_loop)
do_for = register.tag("for", do_for)
def do_ifequal(parser, token, negate):
@@ -814,7 +812,7 @@ def ssi(parser, token):
Outputs the contents of a given file into the page.
Like a simple "include" tag, the ``ssi`` tag includes the contents
- of another file -- which must be specified using an absolute page --
+ of another file -- which must be specified using an absolute path --
in the current page::
{% ssi /home/html/ljworld.com/includes/right_generic.html %}
@@ -926,20 +924,18 @@ def regroup(parser, token):
{% regroup people|dictsort:"gender" by gender as grouped %}
"""
- firstbits = token.contents.split(None, 3)
- if len(firstbits) != 4:
+ bits = token.contents.split()
+ if len(bits) != 6:
raise TemplateSyntaxError, "'regroup' tag takes five arguments"
- target = parser.compile_filter(firstbits[1])
- if firstbits[2] != 'by':
+ target = parser.compile_filter(bits[1])
+ if bits[2] != 'by':
raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'")
- lastbits_reversed = firstbits[3][::-1].split(None, 2)
- if lastbits_reversed[1][::-1] != 'as':
+ if bits[4] != 'as':
raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must"
" be 'as'")
- expression = parser.compile_filter(lastbits_reversed[2][::-1])
-
- var_name = lastbits_reversed[0][::-1]
+ expression = parser.compile_filter(bits[3])
+ var_name = bits[5]
return RegroupNode(target, expression, var_name)
regroup = register.tag(regroup)