diff --git a/django/core/template/__init__.py b/django/core/template/__init__.py
deleted file mode 100644
index bc814e0f5f..0000000000
--- a/django/core/template/__init__.py
+++ /dev/null
@@ -1,914 +0,0 @@
-"""
-This is the Django template system.
-
-How it works:
-
-The Lexer.tokenize() function converts a template string (i.e., a string containing
-markup with custom template tags) to tokens, which can be either plain text
-(TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK).
-
-The Parser() class takes a list of tokens in its constructor, and its parse()
-method returns a compiled template -- which is, under the hood, a list of
-Node objects.
-
-Each Node is responsible for creating some sort of output -- e.g. simple text
-(TextNode), variable values in a given context (VariableNode), results of basic
-logic (IfNode), results of looping (ForNode), or anything else. The core Node
-types are TextNode, VariableNode, IfNode and ForNode, but plugin modules can
-define their own custom node types.
-
-Each Node has a render() method, which takes a Context and returns a string of
-the rendered node. For example, the render() method of a Variable Node returns
-the variable's value as a string. The render() method of an IfNode returns the
-rendered output of whatever was inside the loop, recursively.
-
-The Template class is a convenient wrapper that takes care of template
-compilation and rendering.
-
-Usage:
-
-The only thing you should ever use directly in this file is the Template class.
-Create a compiled template object with a template_string, then call render()
-with a context. In the compilation stage, the TemplateSyntaxError exception
-will be raised if the template doesn't have proper syntax.
-
-Sample code:
-
->>> import template
->>> s = '''
-...
-... {% if test %}
-...
{{ varvalue }}
-... {% endif %}
-...
-... '''
->>> t = template.Template(s)
-
-(t is now a compiled template, and its render() method can be called multiple
-times with multiple contexts)
-
->>> c = template.Context({'test':True, 'varvalue': 'Hello'})
->>> t.render(c)
-'\n\n\n Hello
\n\n\n'
->>> c = template.Context({'test':False, 'varvalue': 'Hello'})
->>> t.render(c)
-'\n\n\n\n'
-"""
-import re
-from inspect import getargspec
-from django.utils.functional import curry
-from django.conf.settings import DEFAULT_CHARSET
-from django.conf import settings
-
-__all__ = ('Template','Context','compile_string')
-
-TOKEN_TEXT = 0
-TOKEN_VAR = 1
-TOKEN_BLOCK = 2
-
-# template syntax constants
-FILTER_SEPARATOR = '|'
-FILTER_ARGUMENT_SEPARATOR = ':'
-VARIABLE_ATTRIBUTE_SEPARATOR = '.'
-BLOCK_TAG_START = '{%'
-BLOCK_TAG_END = '%}'
-VARIABLE_TAG_START = '{{'
-VARIABLE_TAG_END = '}}'
-
-ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.'
-
-# what to report as the origin for templates that come from non-loader sources
-# (e.g. strings)
-UNKNOWN_SOURCE="<unknown source>"
-
-# match a variable or block tag and capture the entire tag, including start/end delimiters
-tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
- re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END)))
-
-# global dictionary of libraries that have been loaded using get_library
-libraries = {}
-# global list of libraries to load by default for a new parser
-builtins = []
-
-class TemplateSyntaxError(Exception):
- pass
-
-class ContextPopException(Exception):
- "pop() has been called more times than push()"
- pass
-
-class TemplateDoesNotExist(Exception):
- pass
-
-class VariableDoesNotExist(Exception):
- pass
-
-class InvalidTemplateLibrary(Exception):
- pass
-
-class Origin(object):
- def __init__(self, name):
- self.name = name
-
- def reload(self):
- raise NotImplementedError
-
- def __str__(self):
- return self.name
-
-class StringOrigin(Origin):
- def __init__(self, source):
- super(StringOrigin, self).__init__(UNKNOWN_SOURCE)
- self.source = source
-
- def reload(self):
- return self.source
-
-class Template:
- def __init__(self, template_string, origin=None):
- "Compilation stage"
- if settings.TEMPLATE_DEBUG and origin == 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)
-
- def __iter__(self):
- for node in self.nodelist:
- for subnode in node:
- yield subnode
-
- def render(self, context):
- "Display stage -- can be called many times"
- return self.nodelist.render(context)
-
-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())
- return parser.parse()
-
-class Context:
- "A stack container for variable context"
- def __init__(self, dict=None):
- dict = dict or {}
- self.dicts = [dict]
-
- def __repr__(self):
- return repr(self.dicts)
-
- def __iter__(self):
- for d in self.dicts:
- yield d
-
- def push(self):
- self.dicts = [{}] + self.dicts
-
- def pop(self):
- if len(self.dicts) == 1:
- raise ContextPopException
- del self.dicts[0]
-
- def __setitem__(self, key, value):
- "Set a variable in the current context"
- self.dicts[0][key] = value
-
- def __getitem__(self, key):
- "Get a variable's value, starting at the current context and going upward"
- for dict in self.dicts:
- if dict.has_key(key):
- return dict[key]
- return ''
-
- def __delitem__(self, key):
- "Delete a variable from the current context"
- del self.dicts[0][key]
-
- def has_key(self, key):
- for dict in self.dicts:
- if dict.has_key(key):
- return True
- return False
-
- def get(self, key, otherwise):
- for dict in self.dicts:
- if dict.has_key(key):
- return dict[key]
- return otherwise
-
- def update(self, other_dict):
- "Like dict.update(). Pushes an entire dictionary's keys and values onto the context."
- self.dicts = [other_dict] + self.dicts
-
-class Token:
- 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', '')
- )
-
- def __repr__(self):
- return '<%s token: "%s">' % (
- {TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block'}[self.token_type],
- self.contents[:].replace('\n', '')
- )
-
-class Lexer(object):
- def __init__(self, template_string, origin):
- self.template_string = template_string
- self.origin = origin
-
- def tokenize(self):
- "Return a list of tokens from a given template_string"
- # remove all empty strings, because the regex has a tendency to add them
- bits = filter(None, tag_re.split(self.template_string))
- return map(self.create_token, bits)
-
- def create_token(self,token_string):
- "Convert the given token string into a new Token object and return it"
- if token_string.startswith(VARIABLE_TAG_START):
- token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip())
- elif token_string.startswith(BLOCK_TAG_START):
- token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip())
- else:
- 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"
- token_tups, upto = [], 0
- for match in tag_re.finditer(self.template_string):
- start, end = match.span()
- if start > upto:
- token_tups.append( (self.template_string[upto:start], (upto, start)) )
- upto = start
- token_tups.append( (self.template_string[start:end], (start,end)) )
- upto = end
- last_bit = self.template_string[upto:]
- if last_bit:
- token_tups.append( (last_bit, (upto, upto + len(last_bit))) )
- return [self.create_token(tok, (self.origin, loc)) for tok, loc in token_tups]
-
- def create_token(self, token_string, source):
- token = super(DebugLexer, self).create_token(token_string)
- token.source = source
- return token
-
-class Parser(object):
- def __init__(self, tokens):
- self.tokens = tokens
- self.tags = {}
- self.filters = {}
- for lib in builtins:
- self.add_library(lib)
-
- def parse(self, parse_until=[]):
- nodelist = self.create_nodelist()
- while self.tokens:
- token = self.next_token()
- if token.token_type == TOKEN_TEXT:
- self.extend_nodelist(nodelist, TextNode(token.contents), token)
- elif token.token_type == TOKEN_VAR:
- if not token.contents:
- self.empty_variable(token)
- filter_expression = self.compile_filter(token.contents)
- var_node = self.create_variable_node(filter_expression)
- self.extend_nodelist(nodelist, var_node,token)
- elif token.token_type == TOKEN_BLOCK:
- if token.contents in parse_until:
- # put token back on token list so calling code knows why it terminated
- self.prepend_token(token)
- return nodelist
- try:
- command = token.contents.split()[0]
- except IndexError:
- self.empty_block_tag(token)
- # execute callback function for this tag and append resulting node
- self.enter_command(command, token)
- try:
- compile_func = self.tags[command]
- except KeyError:
- self.invalid_block_tag(token, command)
- try:
- compiled_result = compile_func(self, token)
- except TemplateSyntaxError, e:
- if not self.compile_function_error(token, e):
- raise
- self.extend_nodelist(nodelist, compiled_result, token)
- self.exit_command()
- if parse_until:
- self.unclosed_block_tag(parse_until)
- return nodelist
-
- def create_variable_node(self, filter_expression):
- return VariableNode(filter_expression)
-
- def create_nodelist(self):
- return NodeList()
-
- def extend_nodelist(self, nodelist, node, token):
- nodelist.append(node)
-
- def enter_command(self, command, token):
- pass
-
- def exit_command(self):
- pass
-
- def error(self, token, msg ):
- return TemplateSyntaxError(msg)
-
- def empty_variable(self, token):
- raise self.error( token, "Empty variable tag")
-
- def empty_block_tag(self, token):
- raise self.error( token, "Empty block tag")
-
- def invalid_block_tag(self, token, 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))
-
- def compile_function_error(self, token, e):
- pass
-
- def next_token(self):
- return self.tokens.pop(0)
-
- def prepend_token(self, token):
- self.tokens.insert(0, token)
-
- def delete_first_token(self):
- del self.tokens[0]
-
- def add_library(self, lib):
- self.tags.update(lib.tags)
- self.filters.update(lib.filters)
-
- def compile_filter(self,token):
- "Convenient wrapper for FilterExpression"
- return FilterExpression(token, self)
-
- def find_filter(self, filter_name):
- if self.filters.has_key(filter_name):
- 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)
-
-
-class TokenParser:
- """
- 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.
-
- The parser's "tagname" instance-variable stores the name of the tag that
- the filter was called with.
- """
- def __init__(self, subject):
- self.subject = subject
- self.pointer = 0
- self.backout = []
- self.tagname = self.tag()
-
- def top(self):
- "Overload this method to do the actual parsing and return the result."
- raise NotImplemented
-
- def more(self):
- "Returns True if there is more stuff in the tag."
- return self.pointer < len(self.subject)
-
- 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"
- self.pointer = self.backout.pop()
-
- def tag(self):
- "A microparser that just returns the next tag from the line."
- subject = self.subject
- i = self.pointer
- if i >= len(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
- s = subject[p:i]
- while i < len(subject) and subject[i] in (' ', '\t'):
- i += 1
- self.backout.append(self.pointer)
- self.pointer = i
- return s
-
- def value(self):
- "A microparser that parses for a value: some string constant or variable name."
- 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
- 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" % subject
- i += 1
- res = subject[p:i]
- while i < len(subject) and subject[i] in (' ', '\t'):
- i += 1
- self.backout.append(self.pointer)
- self.pointer = i
- return res
- else:
- p = i
- while i < len(subject) and subject[i] not in (' ', '\t'):
- if subject[i] in ('"', "'"):
- c = subject[i]
- i += 1
- 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" % subject
- i += 1
- s = subject[p:i]
- while i < len(subject) and subject[i] in (' ', '\t'):
- i += 1
- self.backout.append(self.pointer)
- self.pointer = i
- return s
-
-
-
-
-filter_raw_string = r"""
-^%(i18n_open)s"(?P%(str)s)"%(i18n_close)s|
-^"(?P%(str)s)"|
-^(?P[%(var_chars)s]+)|
- (?:%(filter_sep)s
- (?P\w+)
- (?:%(arg_sep)s
- (?:
- %(i18n_open)s"(?P%(str)s)"%(i18n_close)s|
- "(?P%(str)s)"|
- (?P[%(var_chars)s]+)
- )
- )?
- )""" % {
- 'str': r"""[^"\\]*(?:\\.[^"\\]*)*""",
- 'var_chars': "A-Za-z0-9\_\." ,
- 'filter_sep': re.escape(FILTER_SEPARATOR),
- 'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR),
- 'i18n_open' : re.escape("_("),
- 'i18n_close' : re.escape(")"),
- }
-
-filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")
-filter_re = re.compile(filter_raw_string)
-
-class FilterExpression(object):
- """
- Parses a variable token and its optional filters (all as a single string),
- and return a list of tuples of the filter name and arguments.
- Sample:
- >>> token = 'variable|default:"Default value"|date:"Y-m-d"'
- >>> p = FilterParser(token)
- >>> p.filters
- [('default', 'Default value'), ('date', 'Y-m-d')]
- >>> p.var
- 'variable'
-
- This class should never be instantiated outside of the
- get_filters_from_token helper function.
- """
- def __init__(self, token, parser):
- self.token = token
- matches = filter_re.finditer(token)
- var = None
- filters = []
- upto = 0
- 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:])
- if var == None:
- var, constant, i18n_constant = match.group("var", "constant", "i18n_constant")
- if i18n_constant:
- var = '"%s"' % _(i18n_constant)
- elif constant:
- var = '"%s"' % constant
- upto = match.end()
- if var == None:
- 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
- else:
- filter_name = match.group("filter_name")
- args = []
- constant_arg, i18n_arg, var_arg = match.group("constant_arg", "i18n_arg", "var_arg")
- if i18n_arg:
- args.append((False, _(i18n_arg.replace('\\', ''))))
- elif constant_arg:
- args.append((False, constant_arg.replace('\\', '')))
- elif var_arg:
- args.append((True, var_arg))
- filter_func = parser.find_filter(filter_name)
- self.args_check(filter_name,filter_func, args)
- filters.append( (filter_func,args))
- upto = match.end()
- if upto != len(token):
- raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
- self.var , self.filters = var, filters
-
- def resolve(self, context):
- try:
- obj = resolve_variable(self.var, context)
- except VariableDoesNotExist:
- obj = ''
- for func, args in self.filters:
- arg_vals = []
- for lookup, arg in args:
- if not lookup:
- arg_vals.append(arg)
- else:
- arg_vals.append(resolve_variable(arg, context))
- obj = func(obj, *arg_vals)
- return obj
-
- def args_check(name, func, provided):
- provided = list(provided)
- plen = len(provided)
- (args, varargs, varkw, defaults) = getargspec(func)
- # First argument is filter input.
- args.pop(0)
- if defaults:
- nondefs = args[:-len(defaults)]
- else:
- nondefs = args
- # Args without defaults must be provided.
- try:
- for arg in nondefs:
- provided.pop(0)
- except IndexError:
- # Not enough
- raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
-
- # Defaults can be overridden.
- defaults = defaults and list(defaults) or []
- try:
- for parg in provided:
- defaults.pop(0)
- except IndexError:
- # Too many.
- raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
-
- return True
- args_check = staticmethod(args_check)
-
- def __str__(self):
- return self.token
-
-def resolve_variable(path, context):
- """
- Returns the resolved variable, which may contain attribute syntax, within
- the given context. The variable may be a hard-coded string (if it begins
- and ends with single or double quote marks).
-
- >>> c = {'article': {'section':'News'}}
- >>> resolve_variable('article.section', c)
- 'News'
- >>> resolve_variable('article', c)
- {'section': 'News'}
- >>> class AClass: pass
- >>> c = AClass()
- >>> c.article = AClass()
- >>> c.article.section = 'News'
- >>> resolve_variable('article.section', c)
- 'News'
-
- (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
- """
- if path[0] in ('"', "'") and path[0] == path[-1]:
- current = path[1:-1]
- else:
- current = context
- bits = path.split(VARIABLE_ATTRIBUTE_SEPARATOR)
- while bits:
- try: # dictionary lookup
- current = current[bits[0]]
- except (TypeError, AttributeError, KeyError):
- try: # attribute lookup
- current = getattr(current, bits[0])
- if callable(current):
- if getattr(current, 'alters_data', False):
- current = ''
- else:
- try: # method call (assuming no args required)
- current = current()
- except TypeError: # arguments *were* required
- # GOTCHA: This will also catch any TypeError
- # raised in the function itself.
- current = '' # invalid method call
- except Exception, e:
- if getattr(e, 'silent_variable_failure', False):
- current = ''
- else:
- raise
- except (TypeError, AttributeError):
- try: # list-index lookup
- current = current[int(bits[0])]
- except (IndexError, ValueError, KeyError):
- raise VariableDoesNotExist, "Failed lookup for key [%s] in %r" % (bits[0], current) # missing attribute
- del bits[0]
- return current
-
-class Node:
- def render(self, context):
- "Return the node rendered as a string"
- pass
-
- def __iter__(self):
- yield self
-
- def get_nodes_by_type(self, nodetype):
- "Return a list of all nodes (within this node and its nodelist) of the given type"
- nodes = []
- if isinstance(self, nodetype):
- nodes.append(self)
- if hasattr(self, 'nodelist'):
- nodes.extend(self.nodelist.get_nodes_by_type(nodetype))
- return nodes
-
-class NodeList(list):
- def render(self, context):
- bits = []
- for node in self:
- if isinstance(node, Node):
- bits.append(self.render_node(node, context))
- else:
- bits.append(node)
- return ''.join(bits)
-
- def get_nodes_by_type(self, nodetype):
- "Return a list of all nodes of the given type"
- nodes = []
- for node in self:
- nodes.extend(node.get_nodes_by_type(nodetype))
- return nodes
-
- 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:
- from sys import exc_info
- wrapped = TemplateSyntaxError('Caught an exception while rendering.')
- wrapped.source = node.source
- wrapped.exc_info = exc_info()
- raise wrapped
- return result
-
-class TextNode(Node):
- def __init__(self, s):
- self.s = s
-
- def __repr__(self):
- return "" % self.s[:25]
-
- def render(self, context):
- return self.s
-
-class VariableNode(Node):
- def __init__(self, filter_expression):
- self.filter_expression = filter_expression
-
- def __repr__(self):
- return "" % self.filter_expression
-
- def encode_output(self, output):
- # Check type so that we don't run str() on a Unicode object
- if not isinstance(output, basestring):
- return str(output)
- elif isinstance(output, unicode):
- return output.encode(DEFAULT_CHARSET)
- else:
- return output
-
- def render(self, context):
- output = self.filter_expression.resolve(context)
- return self.encode_output(output)
-
-class DebugVariableNode(VariableNode):
- def render(self, context):
- try:
- output = self.filter_expression.resolve(context)
- except TemplateSyntaxError, e:
- if not hasattr(e, 'source'):
- e.source = self.source
- raise
- return self.encode_output(output)
-
-def generic_tag_compiler(params, defaults, name, node_class, parser, token):
- "Returns a template.Node subclass."
- bits = token.contents.split()[1:]
- bmax = len(params)
- def_len = defaults and len(defaults) or 0
- bmin = bmax - def_len
- if(len(bits) < bmin or len(bits) > bmax):
- if bmin == bmax:
- message = "%s takes %s arguments" % (name, bmin)
- else:
- message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
- raise TemplateSyntaxError, message
- return node_class(bits)
-
-class Library(object):
- def __init__(self):
- self.filters = {}
- self.tags = {}
-
- def tag(self, name=None, compile_function=None):
- if name == None and compile_function == None:
- # @register.tag()
- return self.tag_function
- elif name != None and compile_function == None:
- if(callable(name)):
- # @register.tag
- return self.tag_function(name)
- else:
- # @register.tag('somename') or @register.tag(name='somename')
- def dec(func):
- return self.tag(name, func)
- return dec
- elif name != None and compile_function != None:
- # register.tag('somename', somefunc)
- self.tags[name] = compile_function
- return compile_function
- else:
- raise InvalidTemplateLibrary, "Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function)
-
- def tag_function(self,func):
- self.tags[func.__name__] = func
- return func
-
- def filter(self, name=None, filter_func=None):
- if name == None and filter_func == None:
- # @register.filter()
- return self.filter_function
- elif filter_func == None:
- if(callable(name)):
- # @register.filter
- return self.filter_function(name)
- else:
- # @register.filter('somename') or @register.filter(name='somename')
- def dec(func):
- return self.filter(name, func)
- return dec
- elif name != None and filter_func != None:
- # register.filter('somename', somefunc)
- self.filters[name] = filter_func
- return filter_func
- else:
- raise InvalidTemplateLibrary, "Unsupported arguments to Library.filter: (%r, %r, %r)", (name, compile_function, has_arg)
-
- def filter_function(self, func):
- self.filters[func.__name__] = func
- return func
-
- def simple_tag(self,func):
- (params, xx, xxx, defaults) = getargspec(func)
-
- class SimpleNode(Node):
- def __init__(self, vars_to_resolve):
- self.vars_to_resolve = vars_to_resolve
-
- def render(self, context):
- resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
- return func(*resolved_vars)
-
- compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, SimpleNode)
- compile_func.__doc__ = func.__doc__
- self.tag(func.__name__, compile_func)
- return func
-
- def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
- def dec(func):
- (params, xx, xxx, defaults) = getargspec(func)
- if takes_context:
- 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'"
-
- class InclusionNode(Node):
- def __init__(self, vars_to_resolve):
- self.vars_to_resolve = vars_to_resolve
-
- def render(self, context):
- resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
- if takes_context:
- args = [context] + resolved_vars
- else:
- args = resolved_vars
-
- dict = func(*args)
-
- if not getattr(self, 'nodelist', False):
- from django.core.template_loader import get_template
- t = get_template(file_name)
- self.nodelist = t.nodelist
- return self.nodelist.render(context_class(dict))
-
- compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, InclusionNode)
- compile_func.__doc__ = func.__doc__
- self.tag(func.__name__, compile_func)
- return func
- return dec
-
-def get_library(module_name):
- lib = libraries.get(module_name, None)
- if not lib:
- try:
- mod = __import__(module_name, '', '', [''])
- except ImportError, 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
- return lib
-
-def add_to_builtins(module_name):
- builtins.append(get_library(module_name))
-
-add_to_builtins('django.core.template.defaulttags')
-add_to_builtins('django.core.template.defaultfilters')
diff --git a/django/core/template/defaultfilters.py b/django/core/template/defaultfilters.py
deleted file mode 100644
index dd5fbef25e..0000000000
--- a/django/core/template/defaultfilters.py
+++ /dev/null
@@ -1,487 +0,0 @@
-"Default variable filters"
-
-from django.core.template import resolve_variable, Library
-from django.conf.settings import DATE_FORMAT, TIME_FORMAT
-from django.utils.translation import gettext
-import re
-import random as random_module
-
-register = Library()
-
-###################
-# STRINGS #
-###################
-
-
-def addslashes(value):
- "Adds slashes - useful for passing strings to JavaScript, for example."
- return value.replace('"', '\\"').replace("'", "\\'")
-
-def capfirst(value):
- "Capitalizes the first character of the value"
- value = str(value)
- return value and value[0].upper() + value[1:]
-
-def fix_ampersands(value):
- "Replaces ampersands with ``&`` entities"
- from django.utils.html import fix_ampersands
- return fix_ampersands(value)
-
-def floatformat(text):
- """
- Displays a floating point number as 34.2 (with one decimal place) -- but
- only if there's a point to be displayed
- """
- try:
- f = float(text)
- except ValueError:
- return ''
- m = f - int(f)
- if m:
- return '%.1f' % f
- else:
- return '%d' % int(f)
-
-def linenumbers(value):
- "Displays text with line numbers"
- from django.utils.html import escape
- lines = value.split('\n')
- # Find the maximum width of the line count, for use with zero padding string format command
- width = str(len(str(len(lines))))
- for i, line in enumerate(lines):
- lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
- return '\n'.join(lines)
-
-def lower(value):
- "Converts a string into all lowercase"
- return value.lower()
-
-def make_list(value):
- """
- Returns the value turned into a list. For an integer, it's a list of
- digits. For a string, it's a list of characters.
- """
- return list(str(value))
-
-def slugify(value):
- "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
- value = re.sub('[^\w\s-]', '', value).strip().lower()
- return re.sub('\s+', '-', value)
-
-def stringformat(value, arg):
- """
- Formats the variable according to the argument, a string formatting specifier.
- This specifier uses Python string formating syntax, with the exception that
- the leading "%" is dropped.
-
- See http://docs.python.org/lib/typesseq-strings.html for documentation
- of Python string formatting
- """
- try:
- return ("%" + arg) % value
- except (ValueError, TypeError):
- return ""
-
-def title(value):
- "Converts a string into titlecase"
- return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
-
-def truncatewords(value, arg):
- """
- Truncates a string after a certain number of words
-
- Argument: Number of words to truncate after
- """
- from django.utils.text import truncate_words
- try:
- length = int(arg)
- except ValueError: # invalid literal for int()
- return value # Fail silently.
- if not isinstance(value, basestring):
- value = str(value)
- return truncate_words(value, length)
-
-def upper(value):
- "Converts a string into all uppercase"
- return value.upper()
-
-def urlencode(value):
- "Escapes a value for use in a URL"
- import urllib
- return urllib.quote(value)
-
-def urlize(value):
- "Converts URLs in plain text into clickable links"
- from django.utils.html import urlize
- return urlize(value, nofollow=True)
-
-def urlizetrunc(value, limit):
- """
- Converts URLs into clickable links, truncating URLs to the given character limit,
- and adding 'rel=nofollow' attribute to discourage spamming.
-
- Argument: Length to truncate URLs to.
- """
- from django.utils.html import urlize
- return urlize(value, trim_url_limit=int(limit), nofollow=True)
-
-def wordcount(value):
- "Returns the number of words"
- return len(value.split())
-
-def wordwrap(value, arg):
- """
- Wraps words at specified line length
-
- Argument: number of words to wrap the text at.
- """
- from django.utils.text import wrap
- return wrap(str(value), int(arg))
-
-def ljust(value, arg):
- """
- Left-aligns the value in a field of a given width
-
- Argument: field size
- """
- return str(value).ljust(int(arg))
-
-def rjust(value, arg):
- """
- Right-aligns the value in a field of a given width
-
- Argument: field size
- """
- return str(value).rjust(int(arg))
-
-def center(value, arg):
- "Centers the value in a field of a given width"
- return str(value).center(int(arg))
-
-def cut(value, arg):
- "Removes all values of arg from the given string"
- return value.replace(arg, '')
-
-###################
-# HTML STRINGS #
-###################
-
-def escape(value):
- "Escapes a string's HTML"
- from django.utils.html import escape
- return escape(value)
-
-def linebreaks(value):
- "Converts newlines into and
s"
- from django.utils.html import linebreaks
- return linebreaks(value)
-
-def linebreaksbr(value):
- "Converts newlines into
s"
- return value.replace('\n', '
')
-
-def removetags(value, tags):
- "Removes a space separated list of [X]HTML tags from the output"
- tags = [re.escape(tag) for tag in tags.split()]
- tags_re = '(%s)' % '|'.join(tags)
- starttag_re = re.compile(r'<%s(/?>|(\s+[^>]*>))' % tags_re)
- endtag_re = re.compile('%s>' % tags_re)
- value = starttag_re.sub('', value)
- value = endtag_re.sub('', value)
- return value
-
-def striptags(value):
- "Strips all [X]HTML tags"
- from django.utils.html import strip_tags
- if not isinstance(value, basestring):
- value = str(value)
- return strip_tags(value)
-
-###################
-# LISTS #
-###################
-
-def dictsort(value, arg):
- """
- Takes a list of dicts, returns that list sorted by the property given in
- the argument.
- """
- decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
- decorated.sort()
- return [item[1] for item in decorated]
-
-def dictsortreversed(value, arg):
- """
- Takes a list of dicts, returns that list sorted in reverse order by the
- property given in the argument.
- """
- decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
- decorated.sort()
- decorated.reverse()
- return [item[1] for item in decorated]
-
-def first(value):
- "Returns the first item in a list"
- try:
- return value[0]
- except IndexError:
- return ''
-
-def join(value, arg):
- "Joins a list with a string, like Python's ``str.join(list)``"
- try:
- return arg.join(map(str, value))
- except AttributeError: # fail silently but nicely
- return value
-
-def length(value):
- "Returns the length of the value - useful for lists"
- return len(value)
-
-def length_is(value, arg):
- "Returns a boolean of whether the value's length is the argument"
- return len(value) == int(arg)
-
-def random(value):
- "Returns a random item from the list"
- return random_module.choice(value)
-
-def slice_(value, arg):
- """
- Returns a slice of the list.
-
- Uses the same syntax as Python's list slicing; see
- http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
- for an introduction.
- """
- try:
- bits = []
- for x in arg.split(':'):
- if len(x) == 0:
- bits.append(None)
- else:
- bits.append(int(x))
- return value[slice(*bits)]
-
- except (ValueError, TypeError):
- return value # Fail silently.
-
-def unordered_list(value):
- """
- Recursively takes a self-nested list and returns an HTML unordered list --
- WITHOUT opening and closing
tags.
-
- The list is assumed to be in the proper format. For example, if ``var`` contains
- ``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``,
- then ``{{ var|unordered_list }}`` would return::
-
- - States
-
- - Kansas
-
-
- - Illinois
-
-
- """
- def _helper(value, tabs):
- indent = '\t' * tabs
- if value[1]:
- return '%s- %s\n%s\n%s
' % (indent, value[0], indent,
- '\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent)
- else:
- return '%s- %s
' % (indent, value[0])
- return _helper(value, 1)
-
-###################
-# INTEGERS #
-###################
-
-def add(value, arg):
- "Adds the arg to the value"
- return int(value) + int(arg)
-
-def get_digit(value, arg):
- """
- Given a whole number, returns the requested digit of it, where 1 is the
- right-most digit, 2 is the second-right-most digit, etc. Returns the
- original value for invalid input (if input or argument is not an integer,
- or if argument is less than 1). Otherwise, output is always an integer.
- """
- try:
- arg = int(arg)
- value = int(value)
- except ValueError:
- return value # Fail silently for an invalid argument
- if arg < 1:
- return value
- try:
- return int(str(value)[-arg])
- except IndexError:
- return 0
-
-###################
-# DATES #
-###################
-
-def date(value, arg=DATE_FORMAT):
- "Formats a date according to the given format"
- from django.utils.dateformat import format
- return format(value, arg)
-
-def time(value, arg=TIME_FORMAT):
- "Formats a time according to the given format"
- from django.utils.dateformat import time_format
- return time_format(value, arg)
-
-def timesince(value):
- 'Formats a date as the time since that date (i.e. "4 days, 6 hours")'
- from django.utils.timesince import timesince
- return timesince(value)
-
-###################
-# LOGIC #
-###################
-
-def default(value, arg):
- "If value is unavailable, use given default"
- return value or arg
-
-def default_if_none(value, arg):
- "If value is None, use given default"
- if value is None:
- return arg
- return value
-
-def divisibleby(value, arg):
- "Returns true if the value is devisible by the argument"
- return int(value) % int(arg) == 0
-
-def yesno(value, arg=None):
- """
- Given a string mapping values for true, false and (optionally) None,
- returns one of those strings accoding to the value:
-
- ========== ====================== ==================================
- Value Argument Outputs
- ========== ====================== ==================================
- ``True`` ``"yeah,no,maybe"`` ``yeah``
- ``False`` ``"yeah,no,maybe"`` ``no``
- ``None`` ``"yeah,no,maybe"`` ``maybe``
- ``None`` ``"yeah,no"`` ``"no"`` (converts None to False
- if no mapping for None is given.
- ========== ====================== ==================================
- """
- if arg is None:
- arg = gettext('yes,no,maybe')
- bits = arg.split(',')
- if len(bits) < 2:
- return value # Invalid arg.
- try:
- yes, no, maybe = bits
- except ValueError: # unpack list of wrong size (no "maybe" value provided)
- yes, no, maybe = bits[0], bits[1], bits[1]
- if value is None:
- return maybe
- if value:
- return yes
- return no
-
-###################
-# MISC #
-###################
-
-def filesizeformat(bytes):
- """
- Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
- bytes, etc).
- """
- bytes = float(bytes)
- if bytes < 1024:
- return "%d byte%s" % (bytes, bytes != 1 and 's' or '')
- if bytes < 1024 * 1024:
- return "%.1f KB" % (bytes / 1024)
- if bytes < 1024 * 1024 * 1024:
- return "%.1f MB" % (bytes / (1024 * 1024))
- return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
-
-def pluralize(value):
- "Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'"
- try:
- if int(value) != 1:
- return 's'
- except ValueError: # invalid string that's not a number
- pass
- except TypeError: # value isn't a string or a number; maybe it's a list?
- try:
- if len(value) != 1:
- return 's'
- except TypeError: # len() of unsized object
- pass
- return ''
-
-def phone2numeric(value):
- "Takes a phone number and converts it in to its numerical equivalent"
- from django.utils.text import phone2numeric
- return phone2numeric(value)
-
-def pprint(value):
- "A wrapper around pprint.pprint -- for debugging, really"
- from pprint import pformat
- try:
- return pformat(value)
- except Exception, e:
- return "Error in formatting:%s" % e
-
-# Syntax: register.filter(name of filter, callback)
-register.filter(add)
-register.filter(addslashes)
-register.filter(capfirst)
-register.filter(center)
-register.filter(cut)
-register.filter(date)
-register.filter(default)
-register.filter(default_if_none)
-register.filter(dictsort)
-register.filter(dictsortreversed)
-register.filter(divisibleby)
-register.filter(escape)
-register.filter(filesizeformat)
-register.filter(first)
-register.filter(fix_ampersands)
-register.filter(floatformat)
-register.filter(get_digit)
-register.filter(join)
-register.filter(length)
-register.filter(length_is)
-register.filter(linebreaks)
-register.filter(linebreaksbr)
-register.filter(linenumbers)
-register.filter(ljust)
-register.filter(lower)
-register.filter(make_list)
-register.filter(phone2numeric)
-register.filter(pluralize)
-register.filter(pprint)
-register.filter(removetags)
-register.filter(random)
-register.filter(rjust)
-register.filter('slice', slice_)
-register.filter(slugify)
-register.filter(stringformat)
-register.filter(striptags)
-register.filter(time)
-register.filter(timesince)
-register.filter(title)
-register.filter(truncatewords)
-register.filter(unordered_list)
-register.filter(upper)
-register.filter(urlencode)
-register.filter(urlize)
-register.filter(urlizetrunc)
-register.filter(wordcount)
-register.filter(wordwrap)
-register.filter(yesno)
diff --git a/django/core/template/defaulttags.py b/django/core/template/defaulttags.py
deleted file mode 100644
index c11a67838a..0000000000
--- a/django/core/template/defaulttags.py
+++ /dev/null
@@ -1,783 +0,0 @@
-"Default tags used by the template system, available to all templates."
-
-from django.core.template import Node, NodeList, Template, Context, resolve_variable
-from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END
-from django.core.template import get_library, Library, InvalidTemplateLibrary
-import sys
-
-register = Library()
-
-class CommentNode(Node):
- def render(self, context):
- return ''
-
-class CycleNode(Node):
- def __init__(self, cyclevars):
- self.cyclevars = cyclevars
- self.cyclevars_len = len(cyclevars)
- self.counter = -1
-
- def render(self, context):
- self.counter += 1
- return self.cyclevars[self.counter % self.cyclevars_len]
-
-class DebugNode(Node):
- def render(self, context):
- from pprint import pformat
- output = [pformat(val) for val in context]
- output.append('\n\n')
- output.append(pformat(sys.modules))
- return ''.join(output)
-
-class FilterNode(Node):
- def __init__(self, filter_expr, nodelist):
- self.filter_expr, self.nodelist = filter_expr, nodelist
-
- def render(self, context):
- output = self.nodelist.render(context)
- # apply filters
- return self.filter_expr.resolve(Context({'var': output}))
-
-class FirstOfNode(Node):
- def __init__(self, vars):
- self.vars = vars
-
- def render(self, context):
- for var in self.vars:
- value = resolve_variable(var, context)
- if value:
- return str(value)
- return ''
-
-class ForNode(Node):
- def __init__(self, loopvar, sequence, reversed, nodelist_loop):
- self.loopvar, self.sequence = loopvar, sequence
- self.reversed = reversed
- self.nodelist_loop = nodelist_loop
-
- def __repr__(self):
- if self.reversed:
- reversed = ' reversed'
- else:
- reversed = ''
- return "" % \
- (self.loopvar, self.sequence, len(self.nodelist_loop), reversed)
-
- def __iter__(self):
- for node in self.nodelist_loop:
- yield node
-
- def get_nodes_by_type(self, nodetype):
- nodes = []
- if isinstance(self, nodetype):
- nodes.append(self)
- nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))
- return nodes
-
- def render(self, context):
- nodelist = NodeList()
- if context.has_key('forloop'):
- parentloop = context['forloop']
- else:
- parentloop = {}
- context.push()
- try:
- values = self.sequence.resolve(context)
- except VariableDoesNotExist:
- values = []
- if values is None:
- values = []
- len_values = len(values)
- if self.reversed:
- # From http://www.python.org/doc/current/tut/node11.html
- def reverse(data):
- for index in range(len(data)-1, -1, -1):
- yield data[index]
- values = reverse(values)
- 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,
- }
- context[self.loopvar] = item
- for node in self.nodelist_loop:
- nodelist.append(node.render(context))
- context.pop()
- return nodelist.render(context)
-
-class IfChangedNode(Node):
- def __init__(self, nodelist):
- self.nodelist = nodelist
- self._last_seen = None
-
- def render(self, context):
- content = self.nodelist.render(context)
- if content != self._last_seen:
- firstloop = (self._last_seen == None)
- self._last_seen = content
- context.push()
- context['ifchanged'] = {'firstloop': firstloop}
- content = self.nodelist.render(context)
- context.pop()
- return content
- else:
- return ''
-
-class IfEqualNode(Node):
- def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
- self.var1, self.var2 = var1, var2
- self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
- self.negate = negate
-
- def __repr__(self):
- return ""
-
- def render(self, context):
- val1 = resolve_variable(self.var1, context)
- val2 = resolve_variable(self.var2, context)
- if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
- return self.nodelist_true.render(context)
- return self.nodelist_false.render(context)
-
-class IfNode(Node):
- def __init__(self, bool_exprs, nodelist_true, nodelist_false):
- self.bool_exprs = bool_exprs
- self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
-
- def __repr__(self):
- return ""
-
- def __iter__(self):
- for node in self.nodelist_true:
- yield node
- for node in self.nodelist_false:
- yield node
-
- def get_nodes_by_type(self, nodetype):
- nodes = []
- if isinstance(self, nodetype):
- nodes.append(self)
- nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
- nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
- 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)
-
-class RegroupNode(Node):
- def __init__(self, target, expression, var_name):
- self.target, self.expression = target, expression
- self.var_name = var_name
-
- def render(self, context):
- obj_list = self.target.resolve(context)
- if obj_list == '': # target_var wasn't found in context; fail silently
- context[self.var_name] = []
- return ''
- output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
- for obj in obj_list:
- grouper = self.expression.resolve(Context({'var': obj}))
- # TODO: Is this a sensible way to determine equality?
- if output and repr(output[-1]['grouper']) == repr(grouper):
- output[-1]['list'].append(obj)
- else:
- output.append({'grouper': grouper, 'list': [obj]})
- context[self.var_name] = output
- return ''
-
-def include_is_allowed(filepath):
- from django.conf.settings import ALLOWED_INCLUDE_ROOTS
- for root in ALLOWED_INCLUDE_ROOTS:
- if filepath.startswith(root):
- return True
- return False
-
-class SsiNode(Node):
- def __init__(self, filepath, parsed):
- self.filepath, self.parsed = filepath, parsed
-
- def render(self, context):
- from django.conf.settings import DEBUG
- if not include_is_allowed(self.filepath):
- if DEBUG:
- return "[Didn't have permission to include file]"
- else:
- return '' # Fail silently for invalid includes.
- try:
- fp = open(self.filepath, 'r')
- output = fp.read()
- fp.close()
- except IOError:
- output = ''
- if self.parsed:
- try:
- t = Template(output)
- return t.render(context)
- except TemplateSyntaxError, e:
- if DEBUG:
- return "[Included template had syntax error: %s]" % e
- else:
- return '' # Fail silently for invalid included templates.
- return output
-
-class LoadNode(Node):
- def render(self, context):
- return ''
-
-class NowNode(Node):
- def __init__(self, format_string):
- self.format_string = format_string
-
- def render(self, context):
- from datetime import datetime
- from django.utils.dateformat import DateFormat
- df = DateFormat(datetime.now())
- return df.format(self.format_string)
-
-class TemplateTagNode(Node):
- mapping = {'openblock': BLOCK_TAG_START,
- 'closeblock': BLOCK_TAG_END,
- 'openvariable': VARIABLE_TAG_START,
- 'closevariable': VARIABLE_TAG_END}
-
- def __init__(self, tagtype):
- self.tagtype = tagtype
-
- def render(self, context):
- return self.mapping.get(self.tagtype, '')
-
-class WidthRatioNode(Node):
- def __init__(self, val_expr, max_expr, max_width):
- self.val_expr = val_expr
- self.max_expr = max_expr
- self.max_width = max_width
-
- def render(self, context):
- try:
- value = self.val_expr.resolve(context)
- maxvalue = self.max_expr.resolve(context)
- except VariableDoesNotExist:
- return ''
- try:
- value = float(value)
- maxvalue = float(maxvalue)
- ratio = (value / maxvalue) * int(self.max_width)
- except (ValueError, ZeroDivisionError):
- return ''
- return str(int(round(ratio)))
-
-#@register.tag
-def comment(parser, token):
- """
- Ignore everything between ``{% comment %}`` and ``{% endcomment %}``
- """
- nodelist = parser.parse(('endcomment',))
- parser.delete_first_token()
- return CommentNode()
-comment = register.tag(comment)
-
-#@register.tag
-def cycle(parser, token):
- """
- Cycle among the given strings each time this tag is encountered
-
- Within a loop, cycles among the given strings each time through
- the loop::
-
- {% for o in some_list %}
-
- ...
-
- {% endfor %}
-
- Outside of a loop, give the values a unique name the first time you call
- it, then use that name each sucessive time through::
-
- ...
- ...
- ...
-
- You can use any number of values, seperated by commas. Make sure not to
- put spaces between the values -- only commas.
- """
-
- # Note: This returns the exact same node on each {% cycle name %} call; that
- # is, the node object returned from {% cycle a,b,c as name %} and the one
- # returned from {% cycle name %} are the exact same object. This shouldn't
- # cause problems (heh), but if it does, now you know.
- #
- # Ugly hack warning: this stuffs the named template dict into parser so
- # that names are only unique within each template (as opposed to using
- # a global variable, which would make cycle names have to be unique across
- # *all* templates.
-
- args = token.contents.split()
- if len(args) < 2:
- raise TemplateSyntaxError("'Cycle' statement requires at least two arguments")
-
- elif len(args) == 2 and "," in args[1]:
- # {% cycle a,b,c %}
- cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks
- return CycleNode(cyclevars)
- # {% cycle name %}
-
- elif len(args) == 2:
- name = args[1]
- if not parser._namedCycleNodes.has_key(name):
- raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
- return parser._namedCycleNodes[name]
-
- elif len(args) == 4:
- # {% cycle a,b,c as name %}
- if args[2] != 'as':
- raise TemplateSyntaxError("Second 'cycle' argument must be 'as'")
- cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks
- name = args[3]
- node = CycleNode(cyclevars)
-
- if not hasattr(parser, '_namedCycleNodes'):
- parser._namedCycleNodes = {}
-
- parser._namedCycleNodes[name] = node
- return node
-
- else:
- raise TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args)
-cycle = register.tag(cycle)
-
-def debug(parser, token):
- return DebugNode()
-debug = register.tag(debug)
-
-#@register.tag(name="filter")
-def do_filter(parser, token):
- """
- Filter the contents of the blog through variable filters.
-
- Filters can also be piped through each other, and they can have
- arguments -- just like in variable syntax.
-
- Sample usage::
-
- {% filter escape|lower %}
- This text will be HTML-escaped, and will appear in lowercase.
- {% endfilter %}
- """
- _, rest = token.contents.split(None, 1)
- filter_expr = parser.compile_filter("var|%s" % (rest))
- nodelist = parser.parse(('endfilter',))
- parser.delete_first_token()
- return FilterNode(filter_expr, nodelist)
-filter = register.tag("filter", do_filter)
-
-#@register.tag
-def firstof(parser, token):
- """
- Outputs the first variable passed that is not False.
-
- Outputs nothing if all the passed variables are False.
-
- Sample usage::
-
- {% firstof var1 var2 var3 %}
-
- This is equivalent to::
-
- {% if var1 %}
- {{ var1 }}
- {% else %}{% if var2 %}
- {{ var2 }}
- {% else %}{% if var3 %}
- {{ var3 }}
- {% endif %}{% endif %}{% endif %}
-
- but obviously much cleaner!
- """
- bits = token.contents.split()[1:]
- if len(bits) < 1:
- raise TemplateSyntaxError, "'firstof' statement requires at least one argument"
- return FirstOfNode(bits)
-firstof = register.tag(firstof)
-
-#@register.tag(name="for")
-def do_for(parser, token):
- """
- Loop over each item in an array.
-
- For example, to display a list of athletes given ``athlete_list``::
-
-
- {% for athlete in athlete_list %}
- - {{ athlete.name }}
- {% endfor %}
-
-
- You can also loop over a list in reverse by using
- ``{% for obj in list reversed %}``.
-
- The for loop sets a number of variables available within the loop:
-
- ========================== ================================================
- Variable Description
- ========================== ================================================
- ``forloop.counter`` The current iteration of the loop (1-indexed)
- ``forloop.counter0`` The current iteration of the loop (0-indexed)
- ``forloop.revcounter`` The number of iterations from the end of the
- loop (1-indexed)
- ``forloop.revcounter0`` The number of iterations from the end of the
- loop (0-indexed)
- ``forloop.first`` True if this is the first time through the loop
- ``forloop.last`` True if this is the last time through the loop
- ``forloop.parentloop`` For nested loops, this is the loop "above" the
- current one
- ========================== ================================================
-
- """
- bits = token.contents.split()
- if len(bits) == 5 and bits[4] != 'reversed':
- raise TemplateSyntaxError, "'for' statements with five words should end in 'reversed': %s" % token.contents
- if len(bits) not in (4, 5):
- raise TemplateSyntaxError, "'for' statements should have either four or five words: %s" % token.contents
- if bits[2] != 'in':
- raise TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents
- loopvar = bits[1]
- sequence = parser.compile_filter(bits[3])
- reversed = (len(bits) == 5)
- nodelist_loop = parser.parse(('endfor',))
- parser.delete_first_token()
- return ForNode(loopvar, sequence, reversed, nodelist_loop)
-do_for = register.tag("for", do_for)
-
-def do_ifequal(parser, token, negate):
- """
- Output the contents of the block if the two arguments equal/don't equal each other.
-
- Examples::
-
- {% ifequal user.id comment.user_id %}
- ...
- {% endifequal %}
-
- {% ifnotequal user.id comment.user_id %}
- ...
- {% else %}
- ...
- {% endifnotequal %}
- """
- bits = token.contents.split()
- if len(bits) != 3:
- raise TemplateSyntaxError, "%r takes two arguments" % bits[0]
- end_tag = 'end' + bits[0]
- nodelist_true = parser.parse(('else', end_tag))
- token = parser.next_token()
- if token.contents == 'else':
- nodelist_false = parser.parse((end_tag,))
- parser.delete_first_token()
- else:
- nodelist_false = NodeList()
- return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate)
-
-#@register.tag
-def ifequal(parser, token):
- return do_ifequal(parser, token, False)
-ifequal = register.tag(ifequal)
-
-#@register.tag
-def ifnotequal(parser, token):
- return do_ifequal(parser, token, True)
-ifnotequal = register.tag(ifnotequal)
-
-#@register.tag(name="if")
-def do_if(parser, token):
- """
- 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
- of the block are output:
-
- ::
-
- {% if althlete_list %}
- Number of athletes: {{ althete_list|count }}
- {% else %}
- No athletes.
- {% endif %}
-
- In the above, if ``athlete_list`` is not empty, the number of athletes will
- be displayed by the ``{{ athlete_list|count }}`` variable.
-
- As you can see, the ``if`` tag can take an option ``{% else %}`` clause that
- will be displayed if the test fails.
-
- ``if`` tags may use ``or`` or ``not`` to test a number of variables or to
- negate a given variable::
-
- {% if not athlete_list %}
- There are no athletes.
- {% endif %}
-
- {% if athlete_list or coach_list %}
- There are some athletes or some coaches.
- {% endif %}
-
- {% if not athlete_list or coach_list %}
- There are no athletes, or there are some coaches.
- {% endif %}
-
- For simplicity, ``if`` tags do not allow ``and`` clauses. Use nested ``if``
- tags instead::
-
- {% if athlete_list %}
- {% if coach_list %}
- Number of athletes: {{ athlete_list|count }}.
- Number of coaches: {{ coach_list|count }}.
- {% endif %}
- {% endif %}
- """
- bits = token.contents.split()
- del bits[0]
- 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 ')
- boolvars = []
- for boolpair in boolpairs:
- if ' ' in boolpair:
- not_, boolvar = boolpair.split()
- if not_ != 'not':
- raise TemplateSyntaxError, "Expected 'not' in if statement"
- boolvars.append((True, parser.compile_filter(boolvar)))
- else:
- boolvars.append((False, parser.compile_filter(boolpair)))
- nodelist_true = parser.parse(('else', 'endif'))
- token = parser.next_token()
- if token.contents == 'else':
- nodelist_false = parser.parse(('endif',))
- parser.delete_first_token()
- else:
- nodelist_false = NodeList()
- return IfNode(boolvars, nodelist_true, nodelist_false)
-do_if = register.tag("if", do_if)
-
-#@register.tag
-def ifchanged(parser, token):
- """
- Check if a value has changed from the last iteration of a loop.
-
- The 'ifchanged' block tag is used within a loop. It checks its own rendered
- contents against its previous state and only displays its content if the
- value has changed::
-
- Archive for {{ year }}
-
- {% for date in days %}
- {% ifchanged %}{{ date|date:"F" }}
{% endifchanged %}
- {{ date|date:"j" }}
- {% endfor %}
- """
- bits = token.contents.split()
- if len(bits) != 1:
- raise TemplateSyntaxError, "'ifchanged' tag takes no arguments"
- nodelist = parser.parse(('endifchanged',))
- parser.delete_first_token()
- return IfChangedNode(nodelist)
-ifchanged = register.tag(ifchanged)
-
-#@register.tag
-def ssi(parser, token):
- """
- Output 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 --
- in the current page::
-
- {% ssi /home/html/ljworld.com/includes/right_generic.html %}
-
- If the optional "parsed" parameter is given, the contents of the included
- file are evaluated as template code, with the current context::
-
- {% ssi /home/html/ljworld.com/includes/right_generic.html parsed %}
- """
- bits = token.contents.split()
- parsed = False
- if len(bits) not in (2, 3):
- raise TemplateSyntaxError, "'ssi' tag takes one argument: the path to the file to be included"
- if len(bits) == 3:
- if bits[2] == 'parsed':
- parsed = True
- else:
- raise TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0]
- return SsiNode(bits[1], parsed)
-ssi = register.tag(ssi)
-
-#@register.tag
-def load(parser, token):
- """
- Load a custom template tag set.
-
- For example, to load the template tags in ``django/templatetags/news/photos.py``::
-
- {% load news.photos %}
- """
- bits = token.contents.split()
- for taglib in bits[1:]:
- # add the library to the parser
- try:
- lib = get_library("django.templatetags.%s" % taglib.split('.')[-1])
- parser.add_library(lib)
- except InvalidTemplateLibrary, e:
- raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e)
- return LoadNode()
-load = register.tag(load)
-
-#@register.tag
-def now(parser, token):
- """
- Display the date, formatted according to the given string.
-
- Uses the same format as PHP's ``date()`` function; see http://php.net/date
- for all the possible values.
-
- Sample usage::
-
- It is {% now "jS F Y H:i" %}
- """
- bits = token.contents.split('"')
- if len(bits) != 3:
- raise TemplateSyntaxError, "'now' statement takes one argument"
- format_string = bits[1]
- return NowNode(format_string)
-now = register.tag(now)
-
-#@register.tag
-def regroup(parser, token):
- """
- Regroup a list of alike objects by a common attribute.
-
- This complex tag is best illustrated by use of an example: say that
- ``people`` is a list of ``Person`` objects that have ``first_name``,
- ``last_name``, and ``gender`` attributes, and you'd like to display a list
- that looks like:
-
- * Male:
- * George Bush
- * Bill Clinton
- * Female:
- * Margaret Thatcher
- * Colendeeza Rice
- * Unknown:
- * Pat Smith
-
- The following snippet of template code would accomplish this dubious task::
-
- {% regroup people by gender as grouped %}
-
- {% for group in grouped %}
- - {{ group.grouper }}
-
- {% for item in group.list %}
- - {{ item }}
- {% endfor %}
-
- {% endfor %}
-
-
- As you can see, ``{% regroup %}`` populates a variable with a list of
- objects with ``grouper`` and ``list`` attributes. ``grouper`` contains the
- item that was grouped by; ``list`` contains the list of objects that share
- that ``grouper``. In this case, ``grouper`` would be ``Male``, ``Female``
- and ``Unknown``, and ``list`` is the list of people with those genders.
-
- 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
- people was not sorted by gender, you'd need to make sure it is sorted before
- using it, i.e.::
-
- {% regroup people|dictsort:"gender" by gender as grouped %}
-
- """
- firstbits = token.contents.split(None, 3)
- if len(firstbits) != 4:
- raise TemplateSyntaxError, "'regroup' tag takes five arguments"
- target = parser.compile_filter(firstbits[1])
- if firstbits[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':
- raise TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'"
-
- expression = parser.compile_filter('var.%s' % lastbits_reversed[2][::-1])
-
- var_name = lastbits_reversed[0][::-1]
- return RegroupNode(target, expression, var_name)
-regroup = register.tag(regroup)
-
-#@register.tag
-def templatetag(parser, token):
- """
- Output one of the bits used to compose template tags.
-
- 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 argument tells which template bit to output:
-
- ================== =======
- Argument Outputs
- ================== =======
- ``openblock`` ``{%``
- ``closeblock`` ``%}``
- ``openvariable`` ``{{``
- ``closevariable`` ``}}``
- ================== =======
- """
- bits = token.contents.split()
- if len(bits) != 2:
- raise TemplateSyntaxError, "'templatetag' statement takes one argument"
- tag = bits[1]
- if not TemplateTagNode.mapping.has_key(tag):
- raise TemplateSyntaxError, "Invalid templatetag argument: '%s'. Must be one of: %s" % \
- (tag, TemplateTagNode.mapping.keys())
- return TemplateTagNode(tag)
-templatetag = register.tag(templatetag)
-
-#@register.tag
-def widthratio(parser, token):
- """
- For creating bar charts and such, this tag calculates the ratio of a given
- value to a maximum value, and then applies that ratio to a constant.
-
- For example::
-
-
-
- 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 *
- 100 = 87.5 which is rounded up to 88).
- """
- bits = token.contents.split()
- if len(bits) != 4:
- raise TemplateSyntaxError("widthratio takes three arguments")
- tag, this_value_expr, max_value_expr, max_width = bits
- try:
- max_width = int(max_width)
- except ValueError:
- raise TemplateSyntaxError("widthratio final argument must be an integer")
- return WidthRatioNode(parser.compile_filter(this_value_expr),
- parser.compile_filter(max_value_expr), max_width)
-widthratio = register.tag(widthratio)
diff --git a/django/core/template/loader.py b/django/core/template/loader.py
deleted file mode 100644
index d009c3358a..0000000000
--- a/django/core/template/loader.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# Wrapper for loading templates from storage of some sort (e.g. filesystem, database).
-#
-# This uses the TEMPLATE_LOADERS setting, which is a list of loaders to use.
-# Each loader is expected to have this interface:
-#
-# callable(name, dirs=[])
-#
-# name is the template name.
-# dirs is an optional list of directories to search instead of TEMPLATE_DIRS.
-#
-# The loader should return a tuple of (template_source, path). The path returned
-# might be shown to the user for debugging purposes, so it should identify where
-# the template was loaded from.
-#
-# Each loader should have an "is_usable" attribute set. This is a boolean that
-# specifies whether the loader can be used in this Python installation. Each
-# loader is responsible for setting this when it's initialized.
-#
-# For example, the eggs loader (which is capable of loading templates from
-# Python eggs) sets is_usable to False if the "pkg_resources" module isn't
-# installed, because pkg_resources is necessary to read eggs.
-
-from django.core.exceptions import ImproperlyConfigured
-from django.core.template import Origin, StringOrigin, Template, Context, TemplateDoesNotExist, add_to_builtins
-from django.conf.settings import TEMPLATE_LOADERS
-from django.conf import settings
-
-template_source_loaders = []
-for path in TEMPLATE_LOADERS:
- i = path.rfind('.')
- module, attr = path[:i], path[i+1:]
- try:
- mod = __import__(module, globals(), locals(), [attr])
- except ImportError, e:
- raise ImproperlyConfigured, 'Error importing template source loader %s: "%s"' % (module, e)
- try:
- func = getattr(mod, attr)
- except AttributeError:
- raise ImproperlyConfigured, 'Module "%s" does not define a "%s" callable template source loader' % (module, attr)
- if not func.is_usable:
- import warnings
- warnings.warn("Your TEMPLATE_LOADERS setting includes %r, but your Python installation doesn't support that type of template loading. Consider removing that line from TEMPLATE_LOADERS." % path)
- else:
- template_source_loaders.append(func)
-
-class LoaderOrigin(Origin):
- def __init__(self, display_name, loader, name, dirs):
- super(LoaderOrigin, self).__init__(display_name)
- self.loader, self.loadname, self.dirs = loader, name, dirs
-
- def reload(self):
- return self.loader(self.loadname, self.dirs)[0]
-
-def make_origin(display_name, loader, name, dirs):
- if settings.TEMPLATE_DEBUG:
- return LoaderOrigin(display_name, loader, name, dirs)
- else:
- return None
-
-def find_template_source(name, dirs=None):
- for loader in template_source_loaders:
- try:
- source, display_name = loader(name, dirs)
- return (source, make_origin(display_name, loader, name, dirs))
- except TemplateDoesNotExist:
- pass
- raise TemplateDoesNotExist, name
-
-def get_template(template_name):
- """
- Returns a compiled Template object for the given template name,
- handling template inheritance recursively.
- """
- return get_template_from_string(*find_template_source(template_name))
-
-def get_template_from_string(source, origin=None ):
- """
- Returns a compiled Template object for the given template code,
- handling template inheritance recursively.
- """
- return Template(source, origin)
-
-def render_to_string(template_name, dictionary=None, context_instance=None):
- """
- Loads the given template_name and renders it with the given dictionary as
- context. The template_name may be a string to load a single template using
- get_template, or it may be a tuple to use select_template to find one of
- the templates in the list. Returns a string.
- """
- dictionary = dictionary or {}
- if isinstance(template_name, (list, tuple)):
- t = select_template(template_name)
- else:
- t = get_template(template_name)
- if context_instance:
- context_instance.update(dictionary)
- else:
- context_instance = Context(dictionary)
- return t.render(context_instance)
-
-def select_template(template_name_list):
- "Given a list of template names, returns the first that can be loaded."
- for template_name in template_name_list:
- try:
- return get_template(template_name)
- except TemplateDoesNotExist:
- continue
- # If we get here, none of the templates could be loaded
- raise TemplateDoesNotExist, ', '.join(template_name_list)
-
-add_to_builtins('django.core.template.loader_tags')
diff --git a/django/core/template/loader_tags.py b/django/core/template/loader_tags.py
deleted file mode 100644
index 9c9b991acf..0000000000
--- a/django/core/template/loader_tags.py
+++ /dev/null
@@ -1,172 +0,0 @@
-from django.core.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable
-from django.core.template import Library, Context, Node
-from django.core.template.loader import get_template, get_template_from_string, find_template_source
-
-register = Library()
-
-class ExtendsError(Exception):
- pass
-
-class BlockNode(Node):
- def __init__(self, name, nodelist, parent=None):
- self.name, self.nodelist, self.parent = name, nodelist, parent
-
- def __repr__(self):
- return "" % (self.name, self.nodelist)
-
- def render(self, context):
- context.push()
- # Save context in case of block.super().
- self.context = context
- context['block'] = self
- result = self.nodelist.render(context)
- context.pop()
- return result
-
- def super(self):
- if self.parent:
- return self.parent.render(self.context)
- return ''
-
- def add_parent(self, nodelist):
- if self.parent:
- self.parent.add_parent(nodelist)
- else:
- self.parent = BlockNode(self.name, nodelist)
-
-class ExtendsNode(Node):
- def __init__(self, nodelist, parent_name, parent_name_expr, template_dirs=None):
- self.nodelist = nodelist
- self.parent_name, self.parent_name_expr = parent_name, parent_name_expr
- self.template_dirs = template_dirs
-
- def get_parent(self, context):
- if self.parent_name_expr:
- self.parent_name = self.parent_name_expr.resolve(context)
- parent = self.parent_name
- if not parent:
- error_msg = "Invalid template name in 'extends' tag: %r." % parent
- if self.parent_name_expr:
- error_msg += " Got this from the %r variable." % self.parent_name_expr #TODO nice repr.
- raise TemplateSyntaxError, error_msg
- try:
- return get_template_from_string(*find_template_source(parent, self.template_dirs))
- except TemplateDoesNotExist:
- raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
-
- def render(self, context):
- compiled_parent = self.get_parent(context)
- parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode)
- parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
- for block_node in self.nodelist.get_nodes_by_type(BlockNode):
- # Check for a BlockNode with this node's name, and replace it if found.
- try:
- parent_block = parent_blocks[block_node.name]
- except KeyError:
- # This BlockNode wasn't found in the parent template, but the
- # parent block might be defined in the parent's *parent*, so we
- # add this BlockNode to the parent's ExtendsNode nodelist, so
- # it'll be checked when the parent node's render() is called.
- if parent_is_child:
- compiled_parent.nodelist[0].nodelist.append(block_node)
- else:
- # Keep any existing parents and add a new one. Used by BlockNode.
- parent_block.parent = block_node.parent
- parent_block.add_parent(parent_block.nodelist)
- parent_block.nodelist = block_node.nodelist
- return compiled_parent.render(context)
-
-class ConstantIncludeNode(Node):
- def __init__(self, template_path):
- try:
- t = get_template(template_path)
- self.template = t
- except:
- from django.conf.settings import TEMPLATE_DEBUG
- if TEMPLATE_DEBUG:
- raise
- self.template = None
-
- def render(self, context):
- if self.template:
- return self.template.render(context)
- else:
- return ''
-
-class IncludeNode(Node):
- def __init__(self, template_name):
- self.template_name = template_name
-
- def render(self, context):
- try:
- template_name = resolve_variable(self.template_name, context)
- t = get_template(template_name)
- return t.render(context)
- except TemplateSyntaxError, e:
- if TEMPLATE_DEBUG:
- raise
- return ''
- except:
- return '' # Fail silently for invalid included templates.
-
-def do_block(parser, token):
- """
- Define a block that can be overridden by child templates.
- """
- bits = token.contents.split()
- if len(bits) != 2:
- raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
- block_name = bits[1]
- # Keep track of the names of BlockNodes found in this template, so we can
- # check for duplication.
- try:
- if block_name in parser.__loaded_blocks:
- raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
- parser.__loaded_blocks.append(block_name)
- except AttributeError: # parser._loaded_blocks isn't a list yet
- parser.__loaded_blocks = [block_name]
- nodelist = parser.parse(('endblock',))
- parser.delete_first_token()
- return BlockNode(block_name, nodelist)
-
-def do_extends(parser, token):
- """
- Signal that this template extends a parent template.
-
- This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
- uses the literal value "base" as the name of the parent template to extend,
- or ``{% extends variable %}`` uses the value of ``variable`` as the name
- of the parent template to extend.
- """
- bits = token.contents.split()
- if len(bits) != 2:
- raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
- parent_name, parent_name_expr = None, None
- if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
- parent_name = bits[1][1:-1]
- else:
- parent_name_expr = parser.compile_filter(bits[1])
- nodelist = parser.parse()
- if nodelist.get_nodes_by_type(ExtendsNode):
- raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
- return ExtendsNode(nodelist, parent_name, parent_name_expr)
-
-def do_include(parser, token):
- """
- Loads a template and renders it with the current context.
-
- Example::
-
- {% include "foo/some_include" %}
- """
- bits = token.contents.split()
- if len(bits) != 2:
- raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
- path = bits[1]
- if path[0] in ('"', "'") and path[-1] == path[0]:
- return ConstantIncludeNode(path[1:-1])
- return IncludeNode(bits[1])
-
-register.tag('block', do_block)
-register.tag('extends', do_extends)
-register.tag('include', do_include)
diff --git a/django/core/template/loaders/__init__.py b/django/core/template/loaders/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/django/core/template/loaders/app_directories.py b/django/core/template/loaders/app_directories.py
deleted file mode 100644
index 390e47852e..0000000000
--- a/django/core/template/loaders/app_directories.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Wrapper for loading templates from "template" directories in installed app packages.
-
-from django.conf.settings import INSTALLED_APPS, TEMPLATE_FILE_EXTENSION
-from django.core.exceptions import ImproperlyConfigured
-from django.core.template import TemplateDoesNotExist
-import os
-
-# At compile time, cache the directories to search.
-app_template_dirs = []
-for app in INSTALLED_APPS:
- i = app.rfind('.')
- if i == -1:
- m, a = app, None
- else:
- m, a = app[:i], app[i+1:]
- try:
- if a is None:
- mod = __import__(m, '', '', [])
- else:
- mod = getattr(__import__(m, '', '', [a]), a)
- except ImportError, e:
- raise ImproperlyConfigured, 'ImportError %s: %s' % (app, e.args[0])
- template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
- if os.path.isdir(template_dir):
- app_template_dirs.append(template_dir)
-
-# It won't change, so convert it to a tuple to save memory.
-app_template_dirs = tuple(app_template_dirs)
-
-def get_template_sources(template_name, template_dirs=None):
- for template_dir in app_template_dirs:
- yield os.path.join(template_dir, template_name) + TEMPLATE_FILE_EXTENSION
-
-def load_template_source(template_name, template_dirs=None):
- for filepath in get_template_sources(template_name, template_dirs):
- try:
- return (open(filepath).read(), filepath)
- except IOError:
- pass
- raise TemplateDoesNotExist, template_name
-load_template_source.is_usable = True
diff --git a/django/core/template/loaders/eggs.py b/django/core/template/loaders/eggs.py
deleted file mode 100644
index 5d48326dce..0000000000
--- a/django/core/template/loaders/eggs.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Wrapper for loading templates from eggs via pkg_resources.resource_string.
-
-try:
- from pkg_resources import resource_string
-except ImportError:
- resource_string = None
-
-from django.core.template import TemplateDoesNotExist
-from django.conf.settings import INSTALLED_APPS, TEMPLATE_FILE_EXTENSION
-
-def load_template_source(template_name, template_dirs=None):
- """
- Loads templates from Python eggs via pkg_resource.resource_string.
-
- For every installed app, it tries to get the resource (app, template_name).
- """
- if resource_string is not None:
- pkg_name = 'templates/' + template_name + TEMPLATE_FILE_EXTENSION
- for app in INSTALLED_APPS:
- try:
- return (resource_string(app, pkg_name), 'egg:%s:%s ' % (app, pkg_name))
- except:
- pass
- raise TemplateDoesNotExist, template_name
-load_template_source.is_usable = resource_string is not None
diff --git a/django/core/template/loaders/filesystem.py b/django/core/template/loaders/filesystem.py
deleted file mode 100644
index 23ce6cd9e4..0000000000
--- a/django/core/template/loaders/filesystem.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Wrapper for loading templates from the filesystem.
-
-from django.conf.settings import TEMPLATE_DIRS, TEMPLATE_FILE_EXTENSION
-from django.core.template import TemplateDoesNotExist
-import os
-
-def get_template_sources(template_name, template_dirs=None):
- if not template_dirs:
- template_dirs = TEMPLATE_DIRS
- for template_dir in template_dirs:
- yield os.path.join(template_dir, template_name) + TEMPLATE_FILE_EXTENSION
-
-def load_template_source(template_name, template_dirs=None):
- tried = []
- for filepath in get_template_sources(template_name, template_dirs):
- try:
- return (open(filepath).read(), filepath)
- except IOError:
- tried.append(filepath)
- if template_dirs:
- error_msg = "Tried %s" % tried
- else:
- error_msg = "Your TEMPLATE_DIRS setting is empty. Change it to point to at least one template directory."
- raise TemplateDoesNotExist, error_msg
-load_template_source.is_usable = True