summaryrefslogtreecommitdiff
path: root/django/template
diff options
context:
space:
mode:
Diffstat (limited to 'django/template')
-rw-r--r--django/template/__init__.py41
-rw-r--r--django/template/context.py2
-rw-r--r--django/template/defaultfilters.py6
-rw-r--r--django/template/defaulttags.py61
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()