diff options
Diffstat (limited to 'django/template')
-rw-r--r-- | django/template/__init__.py | 41 | ||||
-rw-r--r-- | django/template/context.py | 2 | ||||
-rw-r--r-- | django/template/defaultfilters.py | 6 | ||||
-rw-r--r-- | django/template/defaulttags.py | 61 |
4 files changed, 72 insertions, 38 deletions
diff --git a/django/template/__init__.py b/django/template/__init__.py index b526863fbf..08f433fec9 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -56,9 +56,10 @@ times with multiple contexts) """ import re from inspect import getargspec -from django.utils.functional import curry from django.conf import settings from django.template.context import Context, RequestContext, ContextPopException +from django.utils.functional import curry +from django.utils.text import smart_split __all__ = ('Template', 'Context', 'RequestContext', 'compile_string') @@ -74,6 +75,8 @@ BLOCK_TAG_START = '{%' BLOCK_TAG_END = '%}' VARIABLE_TAG_START = '{{' VARIABLE_TAG_END = '}}' +SINGLE_BRACE_START = '{' +SINGLE_BRACE_END = '}' ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.' @@ -133,7 +136,7 @@ class StringOrigin(Origin): def reload(self): return self.source -class Template: +class Template(object): def __init__(self, template_string, origin=None): "Compilation stage" if settings.TEMPLATE_DEBUG and origin == None: @@ -157,22 +160,18 @@ def compile_string(template_string, origin): parser = parser_factory(lexer.tokenize()) return parser.parse() -class Token: +class Token(object): def __init__(self, token_type, contents): "The token_type must be TOKEN_TEXT, TOKEN_VAR or TOKEN_BLOCK" self.token_type, self.contents = token_type, contents def __str__(self): - return '<%s token: "%s...">' % ( - {TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block'}[self.token_type], - self.contents[:20].replace('\n', '') - ) + return '<%s token: "%s...">' % \ + ({TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block'}[self.token_type], + self.contents[:20].replace('\n', '')) - def __repr__(self): - return '<%s token: "%s">' % ( - {TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block'}[self.token_type], - self.contents[:].replace('\n', '') - ) + def split_contents(self): + return smart_split(self.contents) class Lexer(object): def __init__(self, template_string, origin): @@ -367,7 +366,6 @@ class DebugParser(Parser): if not hasattr(e, 'source'): e.source = token.source - def lexer_factory(*args, **kwargs): if settings.TEMPLATE_DEBUG: return DebugLexer(*args, **kwargs) @@ -380,8 +378,7 @@ def parser_factory(*args, **kwargs): else: return Parser(*args, **kwargs) - -class TokenParser: +class TokenParser(object): """ Subclass this and implement the top() method to parse a template line. When instantiating the parser, pass in the line from the Django template parser. @@ -544,7 +541,7 @@ class FilterExpression(object): upto = match.end() if upto != len(token): raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:] - self.var , self.filters = var, filters + self.var, self.filters = var, filters def resolve(self, context): try: @@ -564,7 +561,7 @@ class FilterExpression(object): def args_check(name, func, provided): provided = list(provided) plen = len(provided) - (args, varargs, varkw, defaults) = getargspec(func) + args, varargs, varkw, defaults = getargspec(func) # First argument is filter input. args.pop(0) if defaults: @@ -614,7 +611,7 @@ def resolve_variable(path, context): (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.') """ - if path[0] in '0123456789': + if path[0].isdigit(): number_type = '.' in path and float or int try: current = number_type(path) @@ -655,11 +652,11 @@ def resolve_variable(path, context): if getattr(e, 'silent_variable_failure', False): current = settings.TEMPLATE_STRING_IF_INVALID else: - raise + raise del bits[0] return current -class Node: +class Node(object): def render(self, context): "Return the node rendered as a string" pass @@ -820,7 +817,7 @@ class Library(object): return func def simple_tag(self,func): - (params, xx, xxx, defaults) = getargspec(func) + params, xx, xxx, defaults = getargspec(func) class SimpleNode(Node): def __init__(self, vars_to_resolve): @@ -837,7 +834,7 @@ class Library(object): def inclusion_tag(self, file_name, context_class=Context, takes_context=False): def dec(func): - (params, xx, xxx, defaults) = getargspec(func) + params, xx, xxx, defaults = getargspec(func) if takes_context: if params[0] == 'context': params = params[1:] diff --git a/django/template/context.py b/django/template/context.py index f50fb07598..44a97f95a8 100644 --- a/django/template/context.py +++ b/django/template/context.py @@ -7,7 +7,7 @@ class ContextPopException(Exception): "pop() has been called more times than push()" pass -class Context: +class Context(object): "A stack container for variable context" def __init__(self, dict_=None): dict_ = dict_ or {} diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index 03069121ee..453c34b0bd 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -330,6 +330,8 @@ def get_digit(value, arg): def date(value, arg=None): "Formats a date according to the given format" from django.utils.dateformat import format + if not value: + return '' if arg is None: arg = settings.DATE_FORMAT return format(value, arg) @@ -337,6 +339,8 @@ def date(value, arg=None): def time(value, arg=None): "Formats a time according to the given format" from django.utils.dateformat import time_format + if not value: + return '' if arg is None: arg = settings.TIME_FORMAT return time_format(value, arg) @@ -344,6 +348,8 @@ def time(value, arg=None): def timesince(value): 'Formats a date as the time since that date (i.e. "4 days, 6 hours")' from django.utils.timesince import timesince + if not value: + return '' return timesince(value) ################### diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 18f1b9ab30..8b52b70cda 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -1,7 +1,7 @@ "Default tags used by the template system, available to all templates." from django.template import Node, NodeList, Template, Context, resolve_variable -from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END +from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END from django.template import get_library, Library, InvalidTemplateLibrary from django.conf import settings import sys @@ -149,9 +149,10 @@ class IfEqualNode(Node): return self.nodelist_false.render(context) class IfNode(Node): - def __init__(self, bool_exprs, nodelist_true, nodelist_false): + def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type): self.bool_exprs = bool_exprs self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false + self.link_type = link_type def __repr__(self): return "<If node>" @@ -171,14 +172,28 @@ class IfNode(Node): return nodes def render(self, context): - for ifnot, bool_expr in self.bool_exprs: - try: - value = bool_expr.resolve(context) - except VariableDoesNotExist: - value = None - if (value and not ifnot) or (ifnot and not value): - return self.nodelist_true.render(context) - return self.nodelist_false.render(context) + if self.link_type == IfNode.LinkTypes.or_: + for ifnot, bool_expr in self.bool_exprs: + try: + value = bool_expr.resolve(context) + except VariableDoesNotExist: + value = None + if (value and not ifnot) or (ifnot and not value): + return self.nodelist_true.render(context) + return self.nodelist_false.render(context) + else: + for ifnot, bool_expr in self.bool_exprs: + try: + value = bool_expr.resolve(context) + except VariableDoesNotExist: + value = None + if not ((value and not ifnot) or (ifnot and not value)): + return self.nodelist_false.render(context) + return self.nodelist_true.render(context) + + class LinkTypes: + and_ = 0, + or_ = 1 class RegroupNode(Node): def __init__(self, target, expression, var_name): @@ -260,7 +275,10 @@ class TemplateTagNode(Node): mapping = {'openblock': BLOCK_TAG_START, 'closeblock': BLOCK_TAG_END, 'openvariable': VARIABLE_TAG_START, - 'closevariable': VARIABLE_TAG_END} + 'closevariable': VARIABLE_TAG_END, + 'openbrace': SINGLE_BRACE_START, + 'closebrace': SINGLE_BRACE_END, + } def __init__(self, tagtype): self.tagtype = tagtype @@ -487,7 +505,7 @@ def do_ifequal(parser, token, negate): ... {% endifnotequal %} """ - bits = token.contents.split() + bits = list(token.split_contents()) if len(bits) != 3: raise TemplateSyntaxError, "%r takes two arguments" % bits[0] end_tag = 'end' + bits[0] @@ -561,11 +579,22 @@ def do_if(parser, token): if not bits: raise TemplateSyntaxError, "'if' statement requires at least one argument" # bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d'] - boolpairs = ' '.join(bits).split(' or ') + bitstr = ' '.join(bits) + boolpairs = bitstr.split(' and ') boolvars = [] + if len(boolpairs) == 1: + link_type = IfNode.LinkTypes.or_ + boolpairs = bitstr.split(' or ') + else: + link_type = IfNode.LinkTypes.and_ + if ' or ' in bitstr: + raise TemplateSyntaxError, "'if' tags can't mix 'and' and 'or'" for boolpair in boolpairs: if ' ' in boolpair: - not_, boolvar = boolpair.split() + try: + not_, boolvar = boolpair.split() + except ValueError: + raise TemplateSyntaxError, "'if' statement improperly formatted" if not_ != 'not': raise TemplateSyntaxError, "Expected 'not' in if statement" boolvars.append((True, parser.compile_filter(boolvar))) @@ -578,7 +607,7 @@ def do_if(parser, token): parser.delete_first_token() else: nodelist_false = NodeList() - return IfNode(boolvars, nodelist_true, nodelist_false) + return IfNode(boolvars, nodelist_true, nodelist_false, link_type) do_if = register.tag("if", do_if) #@register.tag @@ -783,6 +812,8 @@ def templatetag(parser, token): ``closeblock`` ``%}`` ``openvariable`` ``{{`` ``closevariable`` ``}}`` + ``openbrace`` ``{`` + ``closebrace`` ``}`` ================== ======= """ bits = token.contents.split() |