diff options
| author | Justin Bronn <jbronn@gmail.com> | 2008-01-02 16:57:53 +0000 |
|---|---|---|
| committer | Justin Bronn <jbronn@gmail.com> | 2008-01-02 16:57:53 +0000 |
| commit | 7e322b5908bf5ec255c90c615d1b45b358ecee5f (patch) | |
| tree | 3390dc70c5b4c4a8ff583308bd904eb749f97c04 /django/template | |
| parent | ef0f46f1d0b9e41c811d3d6b833f1652ad46aec3 (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__.py | 171 | ||||
| -rw-r--r-- | django/template/context.py | 6 | ||||
| -rw-r--r-- | django/template/debug.py | 97 | ||||
| -rw-r--r-- | django/template/defaultfilters.py | 6 | ||||
| -rw-r--r-- | django/template/defaulttags.py | 62 |
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) |
