Style and import fixes.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6641 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Gary Wilson Jr 2007-11-04 02:05:56 +00:00
parent 0e59bf42cf
commit 4e8864a882
2 changed files with 172 additions and 144 deletions

View File

@ -1,11 +1,12 @@
"Default variable filters" """Default variable filters."""
import re
import random as random_module
from django.template import Variable, Library from django.template import Variable, Library
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext, ungettext from django.utils.translation import ugettext, ungettext
from django.utils.encoding import force_unicode, smart_str, iri_to_uri from django.utils.encoding import force_unicode, iri_to_uri
import re
import random as random_module
register = Library() register = Library()
@ -36,17 +37,17 @@ def stringfilter(func):
def addslashes(value): def addslashes(value):
"Adds slashes - useful for passing strings to JavaScript, for example." """Adds slashes - useful for passing strings to JavaScript, for example."""
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
addslashes = stringfilter(addslashes) addslashes = stringfilter(addslashes)
def capfirst(value): def capfirst(value):
"Capitalizes the first character of the value" """Capitalizes the first character of the value."""
return value and value[0].upper() + value[1:] return value and value[0].upper() + value[1:]
capfirst = stringfilter(capfirst) capfirst = stringfilter(capfirst)
def fix_ampersands(value): def fix_ampersands(value):
"Replaces ampersands with ``&`` entities" """Replaces ampersands with ``&`` entities."""
from django.utils.html import fix_ampersands from django.utils.html import fix_ampersands
return fix_ampersands(value) return fix_ampersands(value)
fix_ampersands = stringfilter(fix_ampersands) fix_ampersands = stringfilter(fix_ampersands)
@ -86,15 +87,16 @@ def floatformat(text, arg=-1):
return formatstr % f return formatstr % f
def iriencode(value): def iriencode(value):
"Escapes an IRI value for use in a URL" """Escapes an IRI value for use in a URL."""
return force_unicode(iri_to_uri(value)) return force_unicode(iri_to_uri(value))
iriencode = stringfilter(iriencode) iriencode = stringfilter(iriencode)
def linenumbers(value): def linenumbers(value):
"Displays text with line numbers" """Displays text with line numbers."""
from django.utils.html import escape from django.utils.html import escape
lines = value.split(u'\n') lines = value.split(u'\n')
# Find the maximum width of the line count, for use with zero padding string format command # Find the maximum width of the line count, for use with zero padding
# string format command.
width = unicode(len(unicode(len(lines)))) width = unicode(len(unicode(len(lines))))
for i, line in enumerate(lines): for i, line in enumerate(lines):
lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line)) lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line))
@ -102,22 +104,24 @@ def linenumbers(value):
linenumbers = stringfilter(linenumbers) linenumbers = stringfilter(linenumbers)
def lower(value): def lower(value):
"Converts a string into all lowercase" """Converts a string into all lowercase."""
return value.lower() return value.lower()
lower = stringfilter(lower) lower = stringfilter(lower)
def make_list(value): def make_list(value):
""" """
Returns the value turned into a list. For an integer, it's a list of Returns the value turned into a list.
digits. For a string, it's a list of characters.
For an integer, it's a list of digits.
For a string, it's a list of characters.
""" """
return list(value) return list(value)
make_list = stringfilter(make_list) make_list = stringfilter(make_list)
def slugify(value): def slugify(value):
""" """
Normalizes string, converts to lowercase, removes non-alpha chars and Normalizes string, converts to lowercase, removes non-alpha characters,
converts spaces to hyphens. and converts spaces to hyphens.
""" """
import unicodedata import unicodedata
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
@ -127,7 +131,8 @@ slugify = stringfilter(slugify)
def stringformat(value, arg): def stringformat(value, arg):
""" """
Formats the variable according to the argument, a string formatting specifier. Formats the variable according to the arg, a string formatting specifier.
This specifier uses Python string formating syntax, with the exception that This specifier uses Python string formating syntax, with the exception that
the leading "%" is dropped. the leading "%" is dropped.
@ -140,29 +145,29 @@ def stringformat(value, arg):
return u"" return u""
def title(value): def title(value):
"Converts a string into titlecase" """Converts a string into titlecase."""
return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
title = stringfilter(title) title = stringfilter(title)
def truncatewords(value, arg): def truncatewords(value, arg):
""" """
Truncates a string after a certain number of words Truncates a string after a certain number of words.
Argument: Number of words to truncate after Argument: Number of words to truncate after.
""" """
from django.utils.text import truncate_words from django.utils.text import truncate_words
try: try:
length = int(arg) length = int(arg)
except ValueError: # invalid literal for int() except ValueError: # Invalid literal for int().
return value # Fail silently. return value # Fail silently.
return truncate_words(value, length) return truncate_words(value, length)
truncatewords = stringfilter(truncatewords) truncatewords = stringfilter(truncatewords)
def truncatewords_html(value, arg): def truncatewords_html(value, arg):
""" """
Truncates HTML after a certain number of words Truncates HTML after a certain number of words.
Argument: Number of words to truncate after Argument: Number of words to truncate after.
""" """
from django.utils.text import truncate_html_words from django.utils.text import truncate_html_words
try: try:
@ -173,26 +178,26 @@ def truncatewords_html(value, arg):
truncatewords_html = stringfilter(truncatewords_html) truncatewords_html = stringfilter(truncatewords_html)
def upper(value): def upper(value):
"Converts a string into all uppercase" """Converts a string into all uppercase."""
return value.upper() return value.upper()
upper = stringfilter(upper) upper = stringfilter(upper)
def urlencode(value): def urlencode(value):
"Escapes a value for use in a URL" """Escapes a value for use in a URL."""
from django.utils.http import urlquote from django.utils.http import urlquote
return urlquote(value) return urlquote(value)
urlencode = stringfilter(urlencode) urlencode = stringfilter(urlencode)
def urlize(value): def urlize(value):
"Converts URLs in plain text into clickable links" """Converts URLs in plain text into clickable links."""
from django.utils.html import urlize from django.utils.html import urlize
return urlize(value, nofollow=True) return urlize(value, nofollow=True)
urlize = stringfilter(urlize) urlize = stringfilter(urlize)
def urlizetrunc(value, limit): def urlizetrunc(value, limit):
""" """
Converts URLs into clickable links, truncating URLs to the given character limit, Converts URLs into clickable links, truncating URLs to the given character
and adding 'rel=nofollow' attribute to discourage spamming. limit, and adding 'rel=nofollow' attribute to discourage spamming.
Argument: Length to truncate URLs to. Argument: Length to truncate URLs to.
""" """
@ -201,13 +206,13 @@ def urlizetrunc(value, limit):
urlizetrunc = stringfilter(urlizetrunc) urlizetrunc = stringfilter(urlizetrunc)
def wordcount(value): def wordcount(value):
"Returns the number of words" """Returns the number of words."""
return len(value.split()) return len(value.split())
wordcount = stringfilter(wordcount) wordcount = stringfilter(wordcount)
def wordwrap(value, arg): def wordwrap(value, arg):
""" """
Wraps words at specified line length Wraps words at specified line length.
Argument: number of characters to wrap the text at. Argument: number of characters to wrap the text at.
""" """
@ -217,29 +222,29 @@ wordwrap = stringfilter(wordwrap)
def ljust(value, arg): def ljust(value, arg):
""" """
Left-aligns the value in a field of a given width Left-aligns the value in a field of a given width.
Argument: field size Argument: field size.
""" """
return value.ljust(int(arg)) return value.ljust(int(arg))
ljust = stringfilter(ljust) ljust = stringfilter(ljust)
def rjust(value, arg): def rjust(value, arg):
""" """
Right-aligns the value in a field of a given width Right-aligns the value in a field of a given width.
Argument: field size Argument: field size.
""" """
return value.rjust(int(arg)) return value.rjust(int(arg))
rjust = stringfilter(rjust) rjust = stringfilter(rjust)
def center(value, arg): def center(value, arg):
"Centers the value in a field of a given width" """Centers the value in a field of a given width."""
return value.center(int(arg)) return value.center(int(arg))
center = stringfilter(center) center = stringfilter(center)
def cut(value, arg): def cut(value, arg):
"Removes all values of arg from the given string" """Removes all values of arg from the given string."""
return value.replace(arg, u'') return value.replace(arg, u'')
cut = stringfilter(cut) cut = stringfilter(cut)
@ -272,7 +277,7 @@ def linebreaksbr(value):
linebreaksbr = stringfilter(linebreaksbr) linebreaksbr = stringfilter(linebreaksbr)
def removetags(value, tags): def removetags(value, tags):
"Removes a space separated list of [X]HTML tags from the output" """Removes a space separated list of [X]HTML tags from the output."""
tags = [re.escape(tag) for tag in tags.split()] tags = [re.escape(tag) for tag in tags.split()]
tags_re = u'(%s)' % u'|'.join(tags) tags_re = u'(%s)' % u'|'.join(tags)
starttag_re = re.compile(ur'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U) starttag_re = re.compile(ur'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U)
@ -283,7 +288,7 @@ def removetags(value, tags):
removetags = stringfilter(removetags) removetags = stringfilter(removetags)
def striptags(value): def striptags(value):
"Strips all [X]HTML tags" """Strips all [X]HTML tags."""
from django.utils.html import strip_tags from django.utils.html import strip_tags
return strip_tags(value) return strip_tags(value)
striptags = stringfilter(striptags) striptags = stringfilter(striptags)
@ -314,29 +319,29 @@ def dictsortreversed(value, arg):
return [item[1] for item in decorated] return [item[1] for item in decorated]
def first(value): def first(value):
"Returns the first item in a list" """Returns the first item in a list."""
try: try:
return value[0] return value[0]
except IndexError: except IndexError:
return u'' return u''
def join(value, arg): def join(value, arg):
"Joins a list with a string, like Python's ``str.join(list)``" """Joins a list with a string, like Python's ``str.join(list)``."""
try: try:
return arg.join(map(force_unicode, value)) return arg.join(map(force_unicode, value))
except AttributeError: # fail silently but nicely except AttributeError: # fail silently but nicely
return value return value
def length(value): def length(value):
"Returns the length of the value - useful for lists" """Returns the length of the value - useful for lists."""
return len(value) return len(value)
def length_is(value, arg): def length_is(value, arg):
"Returns a boolean of whether the value's length is the argument" """Returns a boolean of whether the value's length is the argument."""
return len(value) == int(arg) return len(value) == int(arg)
def random(value): def random(value):
"Returns a random item from the list" """Returns a random item from the list."""
return random_module.choice(value) return random_module.choice(value)
def slice_(value, arg): def slice_(value, arg):
@ -416,7 +421,7 @@ def unordered_list(value):
sublist = '' sublist = ''
sublist_item = None sublist_item = None
if isinstance(title, (list, tuple)): if isinstance(title, (list, tuple)):
sublist_item = title sublist_item = title
title = '' title = ''
elif i < list_length - 1: elif i < list_length - 1:
next_item = list_[i+1] next_item = list_[i+1]
@ -424,7 +429,7 @@ def unordered_list(value):
# The next item is a sub-list. # The next item is a sub-list.
sublist_item = next_item sublist_item = next_item
# We've processed the next item now too. # We've processed the next item now too.
i += 1 i += 1
if sublist_item: if sublist_item:
sublist = _helper(sublist_item, tabs+1) sublist = _helper(sublist_item, tabs+1)
sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist, sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist,
@ -433,7 +438,7 @@ def unordered_list(value):
sublist)) sublist))
i += 1 i += 1
return '\n'.join(output) return '\n'.join(output)
value, converted = convert_old_style_list(value) value, converted = convert_old_style_list(value)
return _helper(value) return _helper(value)
################### ###################
@ -441,7 +446,7 @@ def unordered_list(value):
################### ###################
def add(value, arg): def add(value, arg):
"Adds the arg to the value" """Adds the arg to the value."""
return int(value) + int(arg) return int(value) + int(arg)
def get_digit(value, arg): def get_digit(value, arg):
@ -468,7 +473,7 @@ def get_digit(value, arg):
################### ###################
def date(value, arg=None): def date(value, arg=None):
"Formats a date according to the given format" """Formats a date according to the given format."""
from django.utils.dateformat import format from django.utils.dateformat import format
if not value: if not value:
return u'' return u''
@ -477,7 +482,7 @@ def date(value, arg=None):
return format(value, arg) return format(value, arg)
def time(value, arg=None): def time(value, arg=None):
"Formats a time according to the given format" """Formats a time according to the given format."""
from django.utils.dateformat import time_format from django.utils.dateformat import time_format
if value in (None, u''): if value in (None, u''):
return u'' return u''
@ -486,7 +491,7 @@ def time(value, arg=None):
return time_format(value, arg) return time_format(value, arg)
def timesince(value, arg=None): def timesince(value, arg=None):
'Formats a date as the time since that date (i.e. "4 days, 6 hours")' """Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
from django.utils.timesince import timesince from django.utils.timesince import timesince
if not value: if not value:
return u'' return u''
@ -495,7 +500,7 @@ def timesince(value, arg=None):
return timesince(value) return timesince(value)
def timeuntil(value, arg=None): def timeuntil(value, arg=None):
'Formats a date as the time until that date (i.e. "4 days, 6 hours")' """Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
from django.utils.timesince import timesince from django.utils.timesince import timesince
from datetime import datetime from datetime import datetime
if not value: if not value:
@ -509,17 +514,17 @@ def timeuntil(value, arg=None):
################### ###################
def default(value, arg): def default(value, arg):
"If value is unavailable, use given default" """If value is unavailable, use given default."""
return value or arg return value or arg
def default_if_none(value, arg): def default_if_none(value, arg):
"If value is None, use given default" """If value is None, use given default."""
if value is None: if value is None:
return arg return arg
return value return value
def divisibleby(value, arg): def divisibleby(value, arg):
"Returns true if the value is devisible by the argument" """Returns True if the value is devisible by the argument."""
return int(value) % int(arg) == 0 return int(value) % int(arg) == 0
def yesno(value, arg=None): def yesno(value, arg=None):
@ -544,7 +549,8 @@ def yesno(value, arg=None):
return value # Invalid arg. return value # Invalid arg.
try: try:
yes, no, maybe = bits yes, no, maybe = bits
except ValueError: # unpack list of wrong size (no "maybe" value provided) except ValueError:
# Unpack list of wrong size (no "maybe" value provided).
yes, no, maybe = bits[0], bits[1], bits[1] yes, no, maybe = bits[0], bits[1], bits[1]
if value is None: if value is None:
return maybe return maybe
@ -558,8 +564,8 @@ def yesno(value, arg=None):
def filesizeformat(bytes): def filesizeformat(bytes):
""" """
Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102 Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
bytes, etc). 102 bytes, etc).
""" """
try: try:
bytes = float(bytes) bytes = float(bytes)
@ -591,23 +597,23 @@ def pluralize(value, arg=u's'):
try: try:
if int(value) != 1: if int(value) != 1:
return plural_suffix return plural_suffix
except ValueError: # invalid string that's not a number except ValueError: # Invalid string that's not a number.
pass pass
except TypeError: # value isn't a string or a number; maybe it's a list? except TypeError: # Value isn't a string or a number; maybe it's a list?
try: try:
if len(value) != 1: if len(value) != 1:
return plural_suffix return plural_suffix
except TypeError: # len() of unsized object except TypeError: # len() of unsized object.
pass pass
return singular_suffix return singular_suffix
def phone2numeric(value): def phone2numeric(value):
"Takes a phone number and converts it in to its numerical equivalent" """Takes a phone number and converts it in to its numerical equivalent."""
from django.utils.text import phone2numeric from django.utils.text import phone2numeric
return phone2numeric(value) return phone2numeric(value)
def pprint(value): def pprint(value):
"A wrapper around pprint.pprint -- for debugging, really" """A wrapper around pprint.pprint -- for debugging, really."""
from pprint import pformat from pprint import pformat
try: try:
return pformat(value) return pformat(value)

View File

@ -1,6 +1,12 @@
"Default tags used by the template system, available to all templates." """Default tags used by the template system, available to all templates."""
import sys
import re
from itertools import cycle as itertools_cycle from itertools import cycle as itertools_cycle
try:
reversed
except NameError:
from django.utils.itercompat import reversed # Python 2.3 fallback
from django.template import Node, NodeList, Template, Context, Variable from django.template import Node, NodeList, Template, Context, Variable
from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_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, COMMENT_TAG_START, COMMENT_TAG_END
@ -8,13 +14,6 @@ from django.template import get_library, Library, InvalidTemplateLibrary
from django.conf import settings from django.conf import settings
from django.utils.encoding import smart_str, smart_unicode from django.utils.encoding import smart_str, smart_unicode
from django.utils.itercompat import groupby from django.utils.itercompat import groupby
import sys
import re
try:
reversed
except NameError:
from django.utils.itercompat import reversed # Python 2.3 fallback
register = Library() register = Library()
@ -48,7 +47,7 @@ class FilterNode(Node):
def render(self, context): def render(self, context):
output = self.nodelist.render(context) output = self.nodelist.render(context)
# apply filters # Apply filters.
context.update({'var': output}) context.update({'var': output})
filtered = self.filter_expr.resolve(context) filtered = self.filter_expr.resolve(context)
context.pop() context.pop()
@ -80,7 +79,8 @@ class ForNode(Node):
else: else:
reversed = '' reversed = ''
return "<For Node: for %s in %s, tail_len: %d%s>" % \ return "<For Node: for %s in %s, tail_len: %d%s>" % \
(', '.join( self.loopvars ), self.sequence, len(self.nodelist_loop), reversed) (', '.join(self.loopvars), self.sequence, len(self.nodelist_loop),
reversed)
def __iter__(self): def __iter__(self):
for node in self.nodelist_loop: for node in self.nodelist_loop:
@ -114,19 +114,20 @@ class ForNode(Node):
unpack = len(self.loopvars) > 1 unpack = len(self.loopvars) > 1
for i, item in enumerate(values): for i, item in enumerate(values):
context['forloop'] = { context['forloop'] = {
# shortcuts for current loop iteration number # Shortcuts for current loop iteration number.
'counter0': i, 'counter0': i,
'counter': i+1, 'counter': i+1,
# reverse counter iteration numbers # Reverse counter iteration numbers.
'revcounter': len_values - i, 'revcounter': len_values - i,
'revcounter0': len_values - i - 1, 'revcounter0': len_values - i - 1,
# boolean values designating first and last times through loop # Boolean values designating first and last times through loop.
'first': (i == 0), 'first': (i == 0),
'last': (i == len_values - 1), 'last': (i == len_values - 1),
'parentloop': parentloop, 'parentloop': parentloop,
} }
if unpack: if unpack:
# If there are multiple loop variables, unpack the item into them. # If there are multiple loop variables, unpack the item into
# them.
context.update(dict(zip(self.loopvars, item))) context.update(dict(zip(self.loopvars, item)))
else: else:
context[self.loopvars[0]] = item context[self.loopvars[0]] = item
@ -153,8 +154,8 @@ class IfChangedNode(Node):
self._last_seen = None self._last_seen = None
try: try:
if self._varlist: if self._varlist:
# Consider multiple parameters. # Consider multiple parameters. This automatically behaves
# This automatically behaves like a OR evaluation of the multiple variables. # like an OR evaluation of the multiple variables.
compare_to = [var.resolve(context) for var in self._varlist] compare_to = [var.resolve(context) for var in self._varlist]
else: else:
compare_to = self.nodelist.render(context) compare_to = self.nodelist.render(context)
@ -248,13 +249,17 @@ class RegroupNode(Node):
def render(self, context): def render(self, context):
obj_list = self.target.resolve(context, True) obj_list = self.target.resolve(context, True)
if obj_list == None: # target_var wasn't found in context; fail silently if obj_list == None:
# target variable wasn't found in context; fail silently.
context[self.var_name] = [] context[self.var_name] = []
return '' return ''
# List of dictionaries in the format # List of dictionaries in the format:
# {'grouper': 'key', 'list': [list of contents]}. # {'grouper': 'key', 'list': [list of contents]}.
context[self.var_name] = [{'grouper':key, 'list':list(val)} for key, val in context[self.var_name] = [
groupby(obj_list, lambda v, f=self.expression.resolve: f(v, True))] {'grouper': key, 'list': list(val)}
for key, val in
groupby(obj_list, lambda v, f=self.expression.resolve: f(v, True))
]
return '' return ''
def include_is_allowed(filepath): def include_is_allowed(filepath):
@ -338,13 +343,15 @@ class URLNode(Node):
def render(self, context): def render(self, context):
from django.core.urlresolvers import reverse, NoReverseMatch from django.core.urlresolvers import reverse, NoReverseMatch
args = [arg.resolve(context) for arg in self.args] args = [arg.resolve(context) for arg in self.args]
kwargs = dict([(smart_str(k,'ascii'), v.resolve(context)) for k, v in self.kwargs.items()]) kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
for k, v in self.kwargs.items()])
try: try:
return reverse(self.view_name, args=args, kwargs=kwargs) return reverse(self.view_name, args=args, kwargs=kwargs)
except NoReverseMatch: except NoReverseMatch:
try: try:
project_name = settings.SETTINGS_MODULE.split('.')[0] project_name = settings.SETTINGS_MODULE.split('.')[0]
return reverse(project_name + '.' + self.view_name, args=args, kwargs=kwargs) return reverse(project_name + '.' + self.view_name,
args=args, kwargs=kwargs)
except NoReverseMatch: except NoReverseMatch:
return '' return ''
@ -388,7 +395,7 @@ class WithNode(Node):
#@register.tag #@register.tag
def comment(parser, token): def comment(parser, token):
""" """
Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` Ignores everything between ``{% comment %}`` and ``{% endcomment %}``.
""" """
parser.skip_past('endcomment') parser.skip_past('endcomment')
return CommentNode() return CommentNode()
@ -397,7 +404,7 @@ comment = register.tag(comment)
#@register.tag #@register.tag
def cycle(parser, token): def cycle(parser, token):
""" """
Cycle among the given strings each time this tag is encountered Cycles among the given strings each time this tag is encountered.
Within a loop, cycles among the given strings each time through Within a loop, cycles among the given strings each time through
the loop:: the loop::
@ -416,14 +423,14 @@ def cycle(parser, token):
<tr class="{% cycle rowcolors %}">...</tr> <tr class="{% cycle rowcolors %}">...</tr>
You can use any number of values, seperated by spaces. Commas can also You can use any number of values, seperated by spaces. Commas can also
be used to separate values; if a comma is used, the cycle values are be used to separate values; if a comma is used, the cycle values are
interpreted as literal strings. interpreted as literal strings.
""" """
# Note: This returns the exact same node on each {% cycle name %} call; that # Note: This returns the exact same node on each {% cycle name %} call;
# is, the node object returned from {% cycle a b c as name %} and the one # that is, the node object returned from {% cycle a b c as name %} and the
# returned from {% cycle name %} are the exact same object. This shouldn't # one returned from {% cycle name %} are the exact same object. This
# cause problems (heh), but if it does, now you know. # shouldn't cause problems (heh), but if it does, now you know.
# #
# Ugly hack warning: this stuffs the named template dict into parser so # Ugly hack warning: this stuffs the named template dict into parser so
# that names are only unique within each template (as opposed to using # that names are only unique within each template (as opposed to using
@ -441,10 +448,11 @@ def cycle(parser, token):
args[1:2] = ['"%s"' % arg for arg in args[1].split(",")] args[1:2] = ['"%s"' % arg for arg in args[1].split(",")]
if len(args) == 2: if len(args) == 2:
# {% cycle foo %} case # {% cycle foo %} case.
name = args[1] name = args[1]
if not hasattr(parser, '_namedCycleNodes'): if not hasattr(parser, '_namedCycleNodes'):
raise TemplateSyntaxError("No named cycles in template: '%s' is not defined" % name) raise TemplateSyntaxError("No named cycles in template."
" '%s' is not defined" % name)
if not name in parser._namedCycleNodes: if not name in parser._namedCycleNodes:
raise TemplateSyntaxError("Named cycle '%s' does not exist" % name) raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
return parser._namedCycleNodes[name] return parser._namedCycleNodes[name]
@ -462,7 +470,8 @@ cycle = register.tag(cycle)
def debug(parser, token): def debug(parser, token):
""" """
Output a whole load of debugging information, including the current context and imported modules. Outputs a whole load of debugging information, including the current
context and imported modules.
Sample usage:: Sample usage::
@ -476,7 +485,7 @@ debug = register.tag(debug)
#@register.tag(name="filter") #@register.tag(name="filter")
def do_filter(parser, token): def do_filter(parser, token):
""" """
Filter the contents of the blog through variable filters. Filters the contents of the blog through variable filters.
Filters can also be piped through each other, and they can have Filters can also be piped through each other, and they can have
arguments -- just like in variable syntax. arguments -- just like in variable syntax.
@ -525,14 +534,15 @@ def firstof(parser, token):
""" """
bits = token.split_contents()[1:] bits = token.split_contents()[1:]
if len(bits) < 1: if len(bits) < 1:
raise TemplateSyntaxError, "'firstof' statement requires at least one argument" raise TemplateSyntaxError("'firstof' statement requires at least one"
" argument")
return FirstOfNode(bits) return FirstOfNode(bits)
firstof = register.tag(firstof) firstof = register.tag(firstof)
#@register.tag(name="for") #@register.tag(name="for")
def do_for(parser, token): def do_for(parser, token):
""" """
Loop over each item in an array. Loops over each item in an array.
For example, to display a list of athletes given ``athlete_list``:: For example, to display a list of athletes given ``athlete_list``::
@ -544,9 +554,9 @@ def do_for(parser, token):
You can loop over a list in reverse by using You can loop over a list in reverse by using
``{% for obj in list reversed %}``. ``{% for obj in list reversed %}``.
You can also unpack multiple values from a two-dimensional array:: You can also unpack multiple values from a two-dimensional array::
{% for key,value in dict.items %} {% for key,value in dict.items %}
{{ key }}: {{ value }} {{ key }}: {{ value }}
{% endfor %} {% endfor %}
@ -571,17 +581,20 @@ def do_for(parser, token):
""" """
bits = token.contents.split() bits = token.contents.split()
if len(bits) < 4: if len(bits) < 4:
raise TemplateSyntaxError, "'for' statements should have at least four words: %s" % token.contents raise TemplateSyntaxError("'for' statements should have at least four"
" words: %s" % token.contents)
reversed = bits[-1] == 'reversed' reversed = bits[-1] == 'reversed'
in_index = reversed and -3 or -2 in_index = reversed and -3 or -2
if bits[in_index] != 'in': if bits[in_index] != 'in':
raise TemplateSyntaxError, "'for' statements should use the format 'for x in y': %s" % token.contents raise TemplateSyntaxError("'for' statements should use the format"
" 'for x in y': %s" % token.contents)
loopvars = re.sub(r' *, *', ',', ' '.join(bits[1:in_index])).split(',') loopvars = re.sub(r' *, *', ',', ' '.join(bits[1:in_index])).split(',')
for var in loopvars: for var in loopvars:
if not var or ' ' in var: if not var or ' ' in var:
raise TemplateSyntaxError, "'for' tag received an invalid argument: %s" % token.contents raise TemplateSyntaxError("'for' tag received an invalid argument:"
" %s" % token.contents)
sequence = parser.compile_filter(bits[in_index+1]) sequence = parser.compile_filter(bits[in_index+1])
nodelist_loop = parser.parse(('endfor',)) nodelist_loop = parser.parse(('endfor',))
@ -606,7 +619,7 @@ def do_ifequal(parser, token, negate):
#@register.tag #@register.tag
def ifequal(parser, token): def ifequal(parser, token):
""" """
Output the contents of the block if the two arguments equal each other. Outputs the contents of the block if the two arguments equal each other.
Examples:: Examples::
@ -625,7 +638,10 @@ ifequal = register.tag(ifequal)
#@register.tag #@register.tag
def ifnotequal(parser, token): def ifnotequal(parser, token):
"""Output the contents of the block if the two arguments are not equal. See ifequal.""" """
Outputs the contents of the block if the two arguments are not equal.
See ifequal.
"""
return do_ifequal(parser, token, True) return do_ifequal(parser, token, True)
ifnotequal = register.tag(ifnotequal) ifnotequal = register.tag(ifnotequal)
@ -634,9 +650,7 @@ def do_if(parser, token):
""" """
The ``{% if %}`` tag evaluates a variable, and if that variable is "true" The ``{% if %}`` tag evaluates a variable, and if that variable is "true"
(i.e. exists, is not empty, and is not a false boolean value) the contents (i.e. exists, is not empty, and is not a false boolean value) the contents
of the block are output: of the block are output::
::
{% if athlete_list %} {% if athlete_list %}
Number of athletes: {{ athlete_list|count }} Number of athletes: {{ athlete_list|count }}
@ -647,8 +661,8 @@ def do_if(parser, token):
In the above, if ``athlete_list`` is not empty, the number of athletes will In the above, if ``athlete_list`` is not empty, the number of athletes will
be displayed by the ``{{ athlete_list|count }}`` variable. be displayed by the ``{{ athlete_list|count }}`` variable.
As you can see, the ``if`` tag can take an option ``{% else %}`` clause that As you can see, the ``if`` tag can take an option ``{% else %}`` clause
will be displayed if the test fails. that will be displayed if the test fails.
``if`` tags may use ``or``, ``and`` or ``not`` to test a number of ``if`` tags may use ``or``, ``and`` or ``not`` to test a number of
variables or to negate a given variable:: variables or to negate a given variable::
@ -673,9 +687,9 @@ def do_if(parser, token):
There are some athletes and absolutely no coaches. There are some athletes and absolutely no coaches.
{% endif %} {% endif %}
``if`` tags do not allow ``and`` and ``or`` clauses with the same ``if`` tags do not allow ``and`` and ``or`` clauses with the same tag,
tag, because the order of logic would be ambigous. For example, because the order of logic would be ambigous. For example, this is
this is invalid:: invalid::
{% if athlete_list and coach_list or cheerleader_list %} {% if athlete_list and coach_list or cheerleader_list %}
@ -691,8 +705,8 @@ def do_if(parser, token):
bits = token.contents.split() bits = token.contents.split()
del bits[0] del bits[0]
if not bits: if not bits:
raise TemplateSyntaxError, "'if' statement requires at least one argument" raise TemplateSyntaxError("'if' statement requires at least one argument")
# bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d'] # Bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d']
bitstr = ' '.join(bits) bitstr = ' '.join(bits)
boolpairs = bitstr.split(' and ') boolpairs = bitstr.split(' and ')
boolvars = [] boolvars = []
@ -727,13 +741,13 @@ do_if = register.tag("if", do_if)
#@register.tag #@register.tag
def ifchanged(parser, token): def ifchanged(parser, token):
""" """
Check if a value has changed from the last iteration of a loop. Checks if a value has changed from the last iteration of a loop.
The 'ifchanged' block tag is used within a loop. It has two possible uses. The 'ifchanged' block tag is used within a loop. It has two possible uses.
1. Checks its own rendered contents against its previous state and only 1. Checks its own rendered contents against its previous state and only
displays the content if it has changed. For example, this displays a list of displays the content if it has changed. For example, this displays a
days, only displaying the month if it changes:: list of days, only displaying the month if it changes::
<h1>Archive for {{ year }}</h1> <h1>Archive for {{ year }}</h1>
@ -742,9 +756,9 @@ def ifchanged(parser, token):
<a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a> <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>
{% endfor %} {% endfor %}
2. If given a variable, check whether that variable has changed. For example, the 2. If given a variable, check whether that variable has changed.
following shows the date every time it changes, but only shows the hour if both For example, the following shows the date every time it changes, but
the hour and the date have changed:: only shows the hour if both the hour and the date have changed::
{% for date in days %} {% for date in days %}
{% ifchanged date.date %} {{ date.date }} {% endifchanged %} {% ifchanged date.date %} {{ date.date }} {% endifchanged %}
@ -762,7 +776,7 @@ ifchanged = register.tag(ifchanged)
#@register.tag #@register.tag
def ssi(parser, token): def ssi(parser, token):
""" """
Output the contents of a given file into the page. Outputs the contents of a given file into the page.
Like a simple "include" tag, the ``ssi`` tag includes the contents 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 page --
@ -778,21 +792,24 @@ def ssi(parser, token):
bits = token.contents.split() bits = token.contents.split()
parsed = False parsed = False
if len(bits) not in (2, 3): if len(bits) not in (2, 3):
raise TemplateSyntaxError, "'ssi' tag takes one argument: the path to the file to be included" raise TemplateSyntaxError("'ssi' tag takes one argument: the path to"
" the file to be included")
if len(bits) == 3: if len(bits) == 3:
if bits[2] == 'parsed': if bits[2] == 'parsed':
parsed = True parsed = True
else: else:
raise TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0] raise TemplateSyntaxError("Second (optional) argument to %s tag"
" must be 'parsed'" % bits[0])
return SsiNode(bits[1], parsed) return SsiNode(bits[1], parsed)
ssi = register.tag(ssi) ssi = register.tag(ssi)
#@register.tag #@register.tag
def load(parser, token): def load(parser, token):
""" """
Load a custom template tag set. Loads a custom template tag set.
For example, to load the template tags in ``django/templatetags/news/photos.py``:: For example, to load the template tags in
``django/templatetags/news/photos.py``::
{% load news.photos %} {% load news.photos %}
""" """
@ -803,14 +820,15 @@ def load(parser, token):
lib = get_library("django.templatetags.%s" % taglib) lib = get_library("django.templatetags.%s" % taglib)
parser.add_library(lib) parser.add_library(lib)
except InvalidTemplateLibrary, e: except InvalidTemplateLibrary, e:
raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e) raise TemplateSyntaxError("'%s' is not a valid tag library: %s" %
(taglib, e))
return LoadNode() return LoadNode()
load = register.tag(load) load = register.tag(load)
#@register.tag #@register.tag
def now(parser, token): def now(parser, token):
""" """
Display the date, formatted according to the given string. Displays the date, formatted according to the given string.
Uses the same format as PHP's ``date()`` function; see http://php.net/date Uses the same format as PHP's ``date()`` function; see http://php.net/date
for all the possible values. for all the possible values.
@ -829,7 +847,7 @@ now = register.tag(now)
#@register.tag #@register.tag
def regroup(parser, token): def regroup(parser, token):
""" """
Regroup a list of alike objects by a common attribute. Regroups a list of alike objects by a common attribute.
This complex tag is best illustrated by use of an example: say that This complex tag is best illustrated by use of an example: say that
``people`` is a list of ``Person`` objects that have ``first_name``, ``people`` is a list of ``Person`` objects that have ``first_name``,
@ -867,8 +885,8 @@ def regroup(parser, token):
Note that `{% regroup %}`` does not work when the list to be grouped is not Note that `{% regroup %}`` does not work when the list to be grouped is not
sorted by the key you are grouping by! This means that if your list of sorted by the key you are grouping by! This means that if your list of
people was not sorted by gender, you'd need to make sure it is sorted before people was not sorted by gender, you'd need to make sure it is sorted
using it, i.e.:: before using it, i.e.::
{% regroup people|dictsort:"gender" by gender as grouped %} {% regroup people|dictsort:"gender" by gender as grouped %}
@ -878,10 +896,11 @@ def regroup(parser, token):
raise TemplateSyntaxError, "'regroup' tag takes five arguments" raise TemplateSyntaxError, "'regroup' tag takes five arguments"
target = parser.compile_filter(firstbits[1]) target = parser.compile_filter(firstbits[1])
if firstbits[2] != 'by': if firstbits[2] != 'by':
raise TemplateSyntaxError, "second argument to 'regroup' tag must be 'by'" raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'")
lastbits_reversed = firstbits[3][::-1].split(None, 2) lastbits_reversed = firstbits[3][::-1].split(None, 2)
if lastbits_reversed[1][::-1] != 'as': if lastbits_reversed[1][::-1] != 'as':
raise TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'" raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must"
" be 'as'")
expression = parser.compile_filter(lastbits_reversed[2][::-1]) expression = parser.compile_filter(lastbits_reversed[2][::-1])
@ -891,8 +910,7 @@ regroup = register.tag(regroup)
def spaceless(parser, token): def spaceless(parser, token):
""" """
Removes whitespace between HTML tags. This includes tab Removes whitespace between HTML tags, including tab and newline characters.
characters and newlines.
Example usage:: Example usage::
@ -906,8 +924,8 @@ def spaceless(parser, token):
<p><a href="foo/">Foo</a></p> <p><a href="foo/">Foo</a></p>
Only space between *tags* is normalized -- not space between tags and text. In Only space between *tags* is normalized -- not space between tags and text.
this example, the space around ``Hello`` won't be stripped:: In this example, the space around ``Hello`` won't be stripped::
{% spaceless %} {% spaceless %}
<strong> <strong>
@ -923,7 +941,7 @@ spaceless = register.tag(spaceless)
#@register.tag #@register.tag
def templatetag(parser, token): def templatetag(parser, token):
""" """
Output one of the bits used to compose template tags. Outputs one of the bits used to compose template tags.
Since the template system has no concept of "escaping", to display one of Since the template system has no concept of "escaping", to display one of
the bits used in template tags, you must use the ``{% templatetag %}`` tag. the bits used in template tags, you must use the ``{% templatetag %}`` tag.
@ -948,8 +966,9 @@ def templatetag(parser, token):
raise TemplateSyntaxError, "'templatetag' statement takes one argument" raise TemplateSyntaxError, "'templatetag' statement takes one argument"
tag = bits[1] tag = bits[1]
if tag not in TemplateTagNode.mapping: if tag not in TemplateTagNode.mapping:
raise TemplateSyntaxError, "Invalid templatetag argument: '%s'. Must be one of: %s" % \ raise TemplateSyntaxError("Invalid templatetag argument: '%s'."
(tag, TemplateTagNode.mapping.keys()) " Must be one of: %s" %
(tag, TemplateTagNode.mapping.keys()))
return TemplateTagNode(tag) return TemplateTagNode(tag)
templatetag = register.tag(templatetag) templatetag = register.tag(templatetag)
@ -957,7 +976,8 @@ def url(parser, token):
""" """
Returns an absolute URL matching given view with its parameters. Returns an absolute URL matching given view with its parameters.
This is a way to define links that aren't tied to a particular URL configuration:: This is a way to define links that aren't tied to a particular URL
configuration::
{% url path.to.some_view arg1,arg2,name1=value1 %} {% url path.to.some_view arg1,arg2,name1=value1 %}
@ -985,7 +1005,8 @@ def url(parser, token):
""" """
bits = token.contents.split(' ', 2) bits = token.contents.split(' ', 2)
if len(bits) < 2: if len(bits) < 2:
raise TemplateSyntaxError, "'%s' takes at least one argument (path to a view)" % bits[0] raise TemplateSyntaxError("'%s' takes at least one argument"
" (path to a view)" % bits[0])
args = [] args = []
kwargs = {} kwargs = {}
if len(bits) > 2: if len(bits) > 2:
@ -1010,8 +1031,8 @@ def widthratio(parser, token):
<img src='bar.gif' height='10' width='{% widthratio this_value max_value 100 %}' /> <img src='bar.gif' height='10' width='{% widthratio this_value max_value 100 %}' />
Above, if ``this_value`` is 175 and ``max_value`` is 200, the the image in Above, if ``this_value`` is 175 and ``max_value`` is 200, the the image in
the above example will be 88 pixels wide (because 175/200 = .875; .875 * the above example will be 88 pixels wide (because 175/200 = .875;
100 = 87.5 which is rounded up to 88). .875 * 100 = 87.5 which is rounded up to 88).
""" """
bits = token.contents.split() bits = token.contents.split()
if len(bits) != 4: if len(bits) != 4:
@ -1028,7 +1049,7 @@ widthratio = register.tag(widthratio)
#@register.tag #@register.tag
def do_with(parser, token): def do_with(parser, token):
""" """
Add a value to the context (inside of this block) for caching and easy Adds a value to the context (inside of this block) for caching and easy
access. access.
For example:: For example::
@ -1039,7 +1060,8 @@ def do_with(parser, token):
""" """
bits = list(token.split_contents()) bits = list(token.split_contents())
if len(bits) != 4 or bits[2] != "as": if len(bits) != 4 or bits[2] != "as":
raise TemplateSyntaxError, "%r expected format is 'value as name'" % bits[0] raise TemplateSyntaxError("%r expected format is 'value as name'" %
bits[0])
var = parser.compile_filter(bits[1]) var = parser.compile_filter(bits[1])
name = bits[3] name = bits[3]
nodelist = parser.parse(('endwith',)) nodelist = parser.parse(('endwith',))