From 7f354d91de72a48d0c5ebf77593ea7069ecfe052 Mon Sep 17 00:00:00 2001 From: Joseph Kocherhans Date: Fri, 13 Jan 2006 17:36:03 +0000 Subject: [PATCH] magic-removal: Removed django.core.template... it has been moved to django.template. git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1946 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/template/__init__.py | 914 ------------------ django/core/template/defaultfilters.py | 487 ---------- django/core/template/defaulttags.py | 783 --------------- django/core/template/loader.py | 111 --- django/core/template/loader_tags.py | 172 ---- django/core/template/loaders/__init__.py | 0 .../core/template/loaders/app_directories.py | 41 - django/core/template/loaders/eggs.py | 25 - django/core/template/loaders/filesystem.py | 25 - 9 files changed, 2558 deletions(-) delete mode 100644 django/core/template/__init__.py delete mode 100644 django/core/template/defaultfilters.py delete mode 100644 django/core/template/defaulttags.py delete mode 100644 django/core/template/loader.py delete mode 100644 django/core/template/loader_tags.py delete mode 100644 django/core/template/loaders/__init__.py delete mode 100644 django/core/template/loaders/app_directories.py delete mode 100644 django/core/template/loaders/eggs.py delete mode 100644 django/core/template/loaders/filesystem.py 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('' % 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 -
        -
      • Lawrence
      • -
      • Topeka
      • -
      -
    • -
    • Illinois
    • -
    -
  • - """ - def _helper(value, tabs): - indent = '\t' * tabs - if value[1]: - return '%s
  • %s\n%s
      \n%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