diff options
Diffstat (limited to 'lib/python2.7/site-packages/django/template')
15 files changed, 5010 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/django/template/__init__.py b/lib/python2.7/site-packages/django/template/__init__.py new file mode 100644 index 0000000..ca1bd49 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/__init__.py @@ -0,0 +1,80 @@ +""" +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: + +>>> from django import template +>>> s = u'<html>{% if test %}<h1>{{ varvalue }}</h1>{% endif %}</html>' +>>> 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) +u'<html><h1>Hello</h1></html>' +>>> c = template.Context({'test':False, 'varvalue': 'Hello'}) +>>> t.render(c) +u'<html></html>' +""" + +# Template lexing symbols +from django.template.base import (ALLOWED_VARIABLE_CHARS, BLOCK_TAG_END, + BLOCK_TAG_START, COMMENT_TAG_END, COMMENT_TAG_START, + FILTER_ARGUMENT_SEPARATOR, FILTER_SEPARATOR, SINGLE_BRACE_END, + SINGLE_BRACE_START, TOKEN_BLOCK, TOKEN_COMMENT, TOKEN_TEXT, TOKEN_VAR, + TRANSLATOR_COMMENT_MARK, UNKNOWN_SOURCE, VARIABLE_ATTRIBUTE_SEPARATOR, + VARIABLE_TAG_END, VARIABLE_TAG_START, filter_re, tag_re) + +# Exceptions +from django.template.base import (ContextPopException, InvalidTemplateLibrary, + TemplateDoesNotExist, TemplateEncodingError, TemplateSyntaxError, + VariableDoesNotExist) + +# Template parts +from django.template.base import (Context, FilterExpression, Lexer, Node, + NodeList, Parser, RequestContext, Origin, StringOrigin, Template, + TextNode, Token, TokenParser, Variable, VariableNode, constant_string, + filter_raw_string) + +# Compiling templates +from django.template.base import (compile_string, resolve_variable, + unescape_string_literal, generic_tag_compiler) + +# Library management +from django.template.base import (Library, add_to_builtins, builtins, + get_library, get_templatetags_modules, get_text_list, import_library, + libraries) + +__all__ = ('Template', 'Context', 'RequestContext', 'compile_string') diff --git a/lib/python2.7/site-packages/django/template/base.py b/lib/python2.7/site-packages/django/template/base.py new file mode 100644 index 0000000..26f8111 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/base.py @@ -0,0 +1,1335 @@ +from __future__ import absolute_import, unicode_literals + +import re +from functools import partial +from inspect import getargspec + +from django.conf import settings +from django.template.context import (BaseContext, Context, RequestContext, + ContextPopException) +from django.utils.importlib import import_module +from django.utils.itercompat import is_iterable +from django.utils.text import (smart_split, unescape_string_literal, + get_text_list) +from django.utils.encoding import force_str, force_text +from django.utils.translation import ugettext_lazy, pgettext_lazy +from django.utils.safestring import (SafeData, EscapeData, mark_safe, + mark_for_escaping) +from django.utils.formats import localize +from django.utils.html import escape +from django.utils.module_loading import module_has_submodule +from django.utils import six +from django.utils.timezone import template_localtime +from django.utils.encoding import python_2_unicode_compatible + + +TOKEN_TEXT = 0 +TOKEN_VAR = 1 +TOKEN_BLOCK = 2 +TOKEN_COMMENT = 3 +TOKEN_MAPPING = { + TOKEN_TEXT: 'Text', + TOKEN_VAR: 'Var', + TOKEN_BLOCK: 'Block', + TOKEN_COMMENT: 'Comment', +} + +# template syntax constants +FILTER_SEPARATOR = '|' +FILTER_ARGUMENT_SEPARATOR = ':' +VARIABLE_ATTRIBUTE_SEPARATOR = '.' +BLOCK_TAG_START = '{%' +BLOCK_TAG_END = '%}' +VARIABLE_TAG_START = '{{' +VARIABLE_TAG_END = '}}' +COMMENT_TAG_START = '{#' +COMMENT_TAG_END = '#}' +TRANSLATOR_COMMENT_MARK = 'Translators' +SINGLE_BRACE_START = '{' +SINGLE_BRACE_END = '}' + +ALLOWED_VARIABLE_CHARS = ('abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.') + +# 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|%s.*?%s)' % + (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), + re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END), + re.escape(COMMENT_TAG_START), re.escape(COMMENT_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 = [] + +# True if TEMPLATE_STRING_IF_INVALID contains a format string (%s). None means +# uninitialised. +invalid_var_format_string = None + +class TemplateSyntaxError(Exception): + pass + +class TemplateDoesNotExist(Exception): + pass + +class TemplateEncodingError(Exception): + pass + +@python_2_unicode_compatible +class VariableDoesNotExist(Exception): + + def __init__(self, msg, params=()): + self.msg = msg + self.params = params + + def __str__(self): + return self.msg % tuple([force_text(p, errors='replace') + for p in self.params]) + +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(object): + def __init__(self, template_string, origin=None, + name='<Unknown Template>'): + try: + template_string = force_text(template_string) + except UnicodeDecodeError: + raise TemplateEncodingError("Templates can only be constructed " + "from unicode or UTF-8 strings.") + if settings.TEMPLATE_DEBUG and origin is None: + origin = StringOrigin(template_string) + self.nodelist = compile_string(template_string, origin) + self.name = name + + def __iter__(self): + for node in self.nodelist: + for subnode in node: + yield subnode + + def _render(self, context): + return self.nodelist.render(context) + + def render(self, context): + "Display stage -- can be called many times" + context.render_context.push() + try: + return self._render(context) + finally: + context.render_context.pop() + +def compile_string(template_string, origin): + "Compiles template_string into NodeList ready for rendering" + if settings.TEMPLATE_DEBUG: + from django.template.debug import DebugLexer, DebugParser + lexer_class, parser_class = DebugLexer, DebugParser + else: + lexer_class, parser_class = Lexer, Parser + lexer = lexer_class(template_string, origin) + parser = parser_class(lexer.tokenize()) + return parser.parse() + +class Token(object): + def __init__(self, token_type, contents): + # token_type must be TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK or + # TOKEN_COMMENT. + self.token_type, self.contents = token_type, contents + self.lineno = None + + def __str__(self): + token_name = TOKEN_MAPPING[self.token_type] + return ('<%s token: "%s...">' % + (token_name, self.contents[:20].replace('\n', ''))) + + def split_contents(self): + split = [] + bits = iter(smart_split(self.contents)) + for bit in bits: + # Handle translation-marked template pieces + if bit.startswith('_("') or bit.startswith("_('"): + sentinal = bit[2] + ')' + trans_bit = [bit] + while not bit.endswith(sentinal): + bit = next(bits) + trans_bit.append(bit) + bit = ' '.join(trans_bit) + split.append(bit) + return split + +class Lexer(object): + def __init__(self, template_string, origin): + self.template_string = template_string + self.origin = origin + self.lineno = 1 + self.verbatim = False + + def tokenize(self): + """ + Return a list of tokens from a given template_string. + """ + in_tag = False + result = [] + for bit in tag_re.split(self.template_string): + if bit: + result.append(self.create_token(bit, in_tag)) + in_tag = not in_tag + return result + + def create_token(self, token_string, in_tag): + """ + Convert the given token string into a new Token object and return it. + If in_tag is True, we are processing something that matched a tag, + otherwise it should be treated as a literal string. + """ + if in_tag and token_string.startswith(BLOCK_TAG_START): + # The [2:-2] ranges below strip off *_TAG_START and *_TAG_END. + # We could do len(BLOCK_TAG_START) to be more "correct", but we've + # hard-coded the 2s here for performance. And it's not like + # the TAG_START values are going to change anytime, anyway. + block_content = token_string[2:-2].strip() + if self.verbatim and block_content == self.verbatim: + self.verbatim = False + if in_tag and not self.verbatim: + if token_string.startswith(VARIABLE_TAG_START): + token = Token(TOKEN_VAR, token_string[2:-2].strip()) + elif token_string.startswith(BLOCK_TAG_START): + if block_content[:9] in ('verbatim', 'verbatim '): + self.verbatim = 'end%s' % block_content + token = Token(TOKEN_BLOCK, block_content) + elif token_string.startswith(COMMENT_TAG_START): + content = '' + if token_string.find(TRANSLATOR_COMMENT_MARK): + content = token_string[2:-2].strip() + token = Token(TOKEN_COMMENT, content) + else: + token = Token(TOKEN_TEXT, token_string) + token.lineno = self.lineno + self.lineno += token_string.count('\n') + 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=None): + if parse_until is None: + parse_until = [] + nodelist = self.create_nodelist() + while self.tokens: + token = self.next_token() + # Use the raw values here for TOKEN_* for a tiny performance boost. + if token.token_type == 0: # TOKEN_TEXT + self.extend_nodelist(nodelist, TextNode(token.contents), token) + elif token.token_type == 1: # TOKEN_VAR + if not token.contents: + self.empty_variable(token) + try: + filter_expression = self.compile_filter(token.contents) + except TemplateSyntaxError as e: + if not self.compile_filter_error(token, e): + raise + var_node = self.create_variable_node(filter_expression) + self.extend_nodelist(nodelist, var_node, token) + elif token.token_type == 2: # TOKEN_BLOCK + try: + command = token.contents.split()[0] + except IndexError: + self.empty_block_tag(token) + if command in parse_until: + # put token back on token list so calling + # code knows why it terminated + self.prepend_token(token) + return nodelist + # 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, parse_until) + try: + compiled_result = compile_func(self, token) + except TemplateSyntaxError as 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 skip_past(self, endtag): + while self.tokens: + token = self.next_token() + if token.token_type == TOKEN_BLOCK and token.contents == endtag: + return + self.unclosed_block_tag([endtag]) + + def create_variable_node(self, filter_expression): + return VariableNode(filter_expression) + + def create_nodelist(self): + return NodeList() + + def extend_nodelist(self, nodelist, node, token): + if node.must_be_first and nodelist: + try: + if nodelist.contains_nontext: + raise AttributeError + except AttributeError: + raise TemplateSyntaxError("%r must be the first tag " + "in the template." % node) + if isinstance(nodelist, NodeList) and not isinstance(node, TextNode): + nodelist.contains_nontext = True + 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, parse_until=None): + if parse_until: + raise self.error(token, "Invalid block tag: '%s', expected %s" % + (command, get_text_list(["'%s'" % p for p in parse_until]))) + 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_filter_error(self, token, e): + pass + + 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 filter_name in self.filters: + return self.filters[filter_name] + else: + raise TemplateSyntaxError("Invalid filter: '%s'" % filter_name) + +class TokenParser(object): + """ + Subclass this and implement the top() method to parse a template line. + When instantiating the parser, pass in the line from the Django template + parser. + + 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 NotImplementedError() + + 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 + + def next_space_index(subject, i): + """ + Increment pointer until a real space (i.e. a space not within + quotes) is encountered + """ + 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" % + (i, subject)) + i += 1 + return i + + 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" % + (i, subject)) + i += 1 + + # Continue parsing until next "real" space, + # so that filters are also included + i = next_space_index(subject, i) + + 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 + i = next_space_index(subject, i) + 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 + +# This only matches constant *strings* (things in quotes or marked for +# translation). Numbers are treated as variables for implementation reasons +# (so that they retain their type when passed to filters). +constant_string = r""" +(?:%(i18n_open)s%(strdq)s%(i18n_close)s| +%(i18n_open)s%(strsq)s%(i18n_close)s| +%(strdq)s| +%(strsq)s) +""" % { + 'strdq': r'"[^"\\]*(?:\\.[^"\\]*)*"', # double-quoted string + 'strsq': r"'[^'\\]*(?:\\.[^'\\]*)*'", # single-quoted string + 'i18n_open': re.escape("_("), + 'i18n_close': re.escape(")"), + } +constant_string = constant_string.replace("\n", "") + +filter_raw_string = r""" +^(?P<constant>%(constant)s)| +^(?P<var>[%(var_chars)s]+|%(num)s)| + (?:\s*%(filter_sep)s\s* + (?P<filter_name>\w+) + (?:%(arg_sep)s + (?: + (?P<constant_arg>%(constant)s)| + (?P<var_arg>[%(var_chars)s]+|%(num)s) + ) + )? + )""" % { + 'constant': constant_string, + 'num': r'[-+\.]?\d[\d\.e]*', + 'var_chars': "\w\.", + 'filter_sep': re.escape(FILTER_SEPARATOR), + 'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR), + } + +filter_re = re.compile(filter_raw_string, re.UNICODE | re.VERBOSE) + +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 = Parser('') + >>> fe = FilterExpression(token, p) + >>> len(fe.filters) + 2 + >>> fe.var + <Variable: '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_obj = 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_obj is None: + var, constant = match.group("var", "constant") + if constant: + try: + var_obj = Variable(constant).resolve({}) + except VariableDoesNotExist: + var_obj = None + elif var is None: + raise TemplateSyntaxError("Could not find variable at " + "start of %s." % token) + else: + var_obj = Variable(var) + else: + filter_name = match.group("filter_name") + args = [] + constant_arg, var_arg = match.group("constant_arg", "var_arg") + if constant_arg: + args.append((False, Variable(constant_arg).resolve({}))) + elif var_arg: + args.append((True, Variable(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' " + "from '%s'" % (token[upto:], token)) + + self.filters = filters + self.var = var_obj + + def resolve(self, context, ignore_failures=False): + if isinstance(self.var, Variable): + try: + obj = self.var.resolve(context) + except VariableDoesNotExist: + if ignore_failures: + obj = None + else: + if settings.TEMPLATE_STRING_IF_INVALID: + global invalid_var_format_string + if invalid_var_format_string is None: + invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID + if invalid_var_format_string: + return settings.TEMPLATE_STRING_IF_INVALID % self.var + return settings.TEMPLATE_STRING_IF_INVALID + else: + obj = settings.TEMPLATE_STRING_IF_INVALID + else: + obj = self.var + for func, args in self.filters: + arg_vals = [] + for lookup, arg in args: + if not lookup: + arg_vals.append(mark_safe(arg)) + else: + arg_vals.append(arg.resolve(context)) + if getattr(func, 'expects_localtime', False): + obj = template_localtime(obj, context.use_tz) + if getattr(func, 'needs_autoescape', False): + new_obj = func(obj, autoescape=context.autoescape, *arg_vals) + else: + new_obj = func(obj, *arg_vals) + if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): + obj = mark_safe(new_obj) + elif isinstance(obj, EscapeData): + obj = mark_for_escaping(new_obj) + else: + obj = new_obj + return obj + + def args_check(name, func, provided): + provided = list(provided) + plen = len(provided) + # Check to see if a decorator is providing the real function. + func = getattr(func, '_decorated_function', func) + 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 = list(defaults) if defaults else [] + 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. + + Deprecated; use the Variable class instead. + """ + return Variable(path).resolve(context) + +class Variable(object): + """ + A template variable, resolvable against a given context. The variable may + be a hard-coded string (if it begins and ends with single or double quote + marks):: + + >>> c = {'article': {'section':u'News'}} + >>> Variable('article.section').resolve(c) + u'News' + >>> Variable('article').resolve(c) + {'section': u'News'} + >>> class AClass: pass + >>> c = AClass() + >>> c.article = AClass() + >>> c.article.section = u'News' + + (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.') + """ + + def __init__(self, var): + self.var = var + self.literal = None + self.lookups = None + self.translate = False + self.message_context = None + + try: + # First try to treat this variable as a number. + # + # Note that this could cause an OverflowError here that we're not + # catching. Since this should only happen at compile time, that's + # probably OK. + self.literal = float(var) + + # So it's a float... is it an int? If the original value contained a + # dot or an "e" then it was a float, not an int. + if '.' not in var and 'e' not in var.lower(): + self.literal = int(self.literal) + + # "2." is invalid + if var.endswith('.'): + raise ValueError + + except ValueError: + # A ValueError means that the variable isn't a number. + if var.startswith('_(') and var.endswith(')'): + # The result of the lookup should be translated at rendering + # time. + self.translate = True + var = var[2:-1] + # If it's wrapped with quotes (single or double), then + # we're also dealing with a literal. + try: + self.literal = mark_safe(unescape_string_literal(var)) + except ValueError: + # Otherwise we'll set self.lookups so that resolve() knows we're + # dealing with a bonafide variable + if var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_': + raise TemplateSyntaxError("Variables and attributes may " + "not begin with underscores: '%s'" % + var) + self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR)) + + def resolve(self, context): + """Resolve this variable against a given context.""" + if self.lookups is not None: + # We're dealing with a variable that needs to be resolved + value = self._resolve_lookup(context) + else: + # We're dealing with a literal, so it's already been "resolved" + value = self.literal + if self.translate: + if self.message_context: + return pgettext_lazy(self.message_context, value) + else: + return ugettext_lazy(value) + return value + + def __repr__(self): + return "<%s: %r>" % (self.__class__.__name__, self.var) + + def __str__(self): + return self.var + + def _resolve_lookup(self, context): + """ + Performs resolution of a real variable (i.e. not a literal) against the + given context. + + As indicated by the method's name, this method is an implementation + detail and shouldn't be called by external code. Use Variable.resolve() + instead. + """ + current = context + try: # catch-all for silent variable failures + for bit in self.lookups: + try: # dictionary lookup + current = current[bit] + except (TypeError, AttributeError, KeyError, ValueError): + try: # attribute lookup + # Don't return class attributes if the class is the context: + if isinstance(current, BaseContext) and getattr(type(current), bit): + raise AttributeError + current = getattr(current, bit) + except (TypeError, AttributeError): + try: # list-index lookup + current = current[int(bit)] + except (IndexError, # list index out of range + ValueError, # invalid literal for int() + KeyError, # current is a dict without `int(bit)` key + TypeError): # unsubscriptable object + raise VariableDoesNotExist("Failed lookup for key " + "[%s] in %r", + (bit, current)) # missing attribute + if callable(current): + if getattr(current, 'do_not_call_in_templates', False): + pass + elif getattr(current, 'alters_data', False): + current = settings.TEMPLATE_STRING_IF_INVALID + 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 = settings.TEMPLATE_STRING_IF_INVALID # invalid method call + except Exception as e: + if getattr(e, 'silent_variable_failure', False): + current = settings.TEMPLATE_STRING_IF_INVALID + else: + raise + + return current + +class Node(object): + # Set this to True for nodes that must be first in the template (although + # they can be preceded by text nodes. + must_be_first = False + child_nodelists = ('nodelist',) + + 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) + for attr in self.child_nodelists: + nodelist = getattr(self, attr, None) + if nodelist: + nodes.extend(nodelist.get_nodes_by_type(nodetype)) + return nodes + +class NodeList(list): + # Set to True the first time a non-TextNode is inserted by + # extend_nodelist(). + contains_nontext = False + + def render(self, context): + bits = [] + for node in self: + if isinstance(node, Node): + bit = self.render_node(node, context) + else: + bit = node + bits.append(force_text(bit)) + return mark_safe(''.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 TextNode(Node): + def __init__(self, s): + self.s = s + + def __repr__(self): + return force_str("<Text Node: '%s'>" % self.s[:25], 'ascii', + errors='replace') + + def render(self, context): + return self.s + +def render_value_in_context(value, context): + """ + Converts any value to a string to become part of a rendered template. This + means escaping, if required, and conversion to a unicode object. If value + is a string, it is expected to have already been translated. + """ + value = template_localtime(value, use_tz=context.use_tz) + value = localize(value, use_l10n=context.use_l10n) + value = force_text(value) + if ((context.autoescape and not isinstance(value, SafeData)) or + isinstance(value, EscapeData)): + return escape(value) + else: + return value + +class VariableNode(Node): + def __init__(self, filter_expression): + self.filter_expression = filter_expression + + def __repr__(self): + return "<Variable Node: %s>" % self.filter_expression + + def render(self, context): + try: + output = self.filter_expression.resolve(context) + except UnicodeDecodeError: + # Unicode conversion can fail sometimes for reasons out of our + # control (e.g. exception rendering). In that case, we fail + # quietly. + return '' + return render_value_in_context(output, context) + +# Regex for token keyword arguments +kwarg_re = re.compile(r"(?:(\w+)=)?(.+)") + +def token_kwargs(bits, parser, support_legacy=False): + """ + A utility method for parsing token keyword arguments. + + :param bits: A list containing remainder of the token (split by spaces) + that is to be checked for arguments. Valid arguments will be removed + from this list. + + :param support_legacy: If set to true ``True``, the legacy format + ``1 as foo`` will be accepted. Otherwise, only the standard ``foo=1`` + format is allowed. + + :returns: A dictionary of the arguments retrieved from the ``bits`` token + list. + + There is no requirement for all remaining token ``bits`` to be keyword + arguments, so the dictionary will be returned as soon as an invalid + argument format is reached. + """ + if not bits: + return {} + match = kwarg_re.match(bits[0]) + kwarg_format = match and match.group(1) + if not kwarg_format: + if not support_legacy: + return {} + if len(bits) < 3 or bits[1] != 'as': + return {} + + kwargs = {} + while bits: + if kwarg_format: + match = kwarg_re.match(bits[0]) + if not match or not match.group(1): + return kwargs + key, value = match.groups() + del bits[:1] + else: + if len(bits) < 3 or bits[1] != 'as': + return kwargs + key, value = bits[2], bits[0] + del bits[:3] + kwargs[key] = parser.compile_filter(value) + if bits and not kwarg_format: + if bits[0] != 'and': + return kwargs + del bits[:1] + return kwargs + +def parse_bits(parser, bits, params, varargs, varkw, defaults, + takes_context, name): + """ + Parses bits for template tag helpers (simple_tag, include_tag and + assignment_tag), in particular by detecting syntax errors and by + extracting positional and keyword arguments. + """ + if takes_context: + if params[0] == 'context': + params = params[1:] + else: + raise TemplateSyntaxError( + "'%s' is decorated with takes_context=True so it must " + "have a first argument of 'context'" % name) + args = [] + kwargs = {} + unhandled_params = list(params) + for bit in bits: + # First we try to extract a potential kwarg from the bit + kwarg = token_kwargs([bit], parser) + if kwarg: + # The kwarg was successfully extracted + param, value = list(six.iteritems(kwarg))[0] + if param not in params and varkw is None: + # An unexpected keyword argument was supplied + raise TemplateSyntaxError( + "'%s' received unexpected keyword argument '%s'" % + (name, param)) + elif param in kwargs: + # The keyword argument has already been supplied once + raise TemplateSyntaxError( + "'%s' received multiple values for keyword argument '%s'" % + (name, param)) + else: + # All good, record the keyword argument + kwargs[str(param)] = value + if param in unhandled_params: + # If using the keyword syntax for a positional arg, then + # consume it. + unhandled_params.remove(param) + else: + if kwargs: + raise TemplateSyntaxError( + "'%s' received some positional argument(s) after some " + "keyword argument(s)" % name) + else: + # Record the positional argument + args.append(parser.compile_filter(bit)) + try: + # Consume from the list of expected positional arguments + unhandled_params.pop(0) + except IndexError: + if varargs is None: + raise TemplateSyntaxError( + "'%s' received too many positional arguments" % + name) + if defaults is not None: + # Consider the last n params handled, where n is the + # number of defaults. + unhandled_params = unhandled_params[:-len(defaults)] + if unhandled_params: + # Some positional arguments were not supplied + raise TemplateSyntaxError( + "'%s' did not receive value(s) for the argument(s): %s" % + (name, ", ".join(["'%s'" % p for p in unhandled_params]))) + return args, kwargs + +def generic_tag_compiler(parser, token, params, varargs, varkw, defaults, + name, takes_context, node_class): + """ + Returns a template.Node subclass. + """ + bits = token.split_contents()[1:] + args, kwargs = parse_bits(parser, bits, params, varargs, varkw, + defaults, takes_context, name) + return node_class(takes_context, args, kwargs) + +class TagHelperNode(Node): + """ + Base class for tag helper nodes such as SimpleNode, InclusionNode and + AssignmentNode. Manages the positional and keyword arguments to be passed + to the decorated function. + """ + + def __init__(self, takes_context, args, kwargs): + self.takes_context = takes_context + self.args = args + self.kwargs = kwargs + + def get_resolved_arguments(self, context): + resolved_args = [var.resolve(context) for var in self.args] + if self.takes_context: + resolved_args = [context] + resolved_args + resolved_kwargs = dict((k, v.resolve(context)) + for k, v in self.kwargs.items()) + return resolved_args, resolved_kwargs + +class Library(object): + def __init__(self): + self.filters = {} + self.tags = {} + + def tag(self, name=None, compile_function=None): + if name is None and compile_function is None: + # @register.tag() + return self.tag_function + elif name is not None and compile_function is 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 is not None and compile_function is not 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[getattr(func, "_decorated_function", func).__name__] = func + return func + + def filter(self, name=None, filter_func=None, **flags): + if name is None and filter_func is None: + # @register.filter() + def dec(func): + return self.filter_function(func, **flags) + return dec + + elif name is not None and filter_func is None: + if callable(name): + # @register.filter + return self.filter_function(name, **flags) + else: + # @register.filter('somename') or @register.filter(name='somename') + def dec(func): + return self.filter(name, func, **flags) + return dec + + elif name is not None and filter_func is not None: + # register.filter('somename', somefunc) + self.filters[name] = filter_func + for attr in ('expects_localtime', 'is_safe', 'needs_autoescape'): + if attr in flags: + value = flags[attr] + # set the flag on the filter for FilterExpression.resolve + setattr(filter_func, attr, value) + # set the flag on the innermost decorated function + # for decorators that need it e.g. stringfilter + if hasattr(filter_func, "_decorated_function"): + setattr(filter_func._decorated_function, attr, value) + filter_func._filter_name = name + return filter_func + else: + raise InvalidTemplateLibrary("Unsupported arguments to " + "Library.filter: (%r, %r)", (name, filter_func)) + + def filter_function(self, func, **flags): + name = getattr(func, "_decorated_function", func).__name__ + return self.filter(name, func, **flags) + + def simple_tag(self, func=None, takes_context=None, name=None): + def dec(func): + params, varargs, varkw, defaults = getargspec(func) + + class SimpleNode(TagHelperNode): + + def render(self, context): + resolved_args, resolved_kwargs = self.get_resolved_arguments(context) + return func(*resolved_args, **resolved_kwargs) + + function_name = (name or + getattr(func, '_decorated_function', func).__name__) + compile_func = partial(generic_tag_compiler, + params=params, varargs=varargs, varkw=varkw, + defaults=defaults, name=function_name, + takes_context=takes_context, node_class=SimpleNode) + compile_func.__doc__ = func.__doc__ + self.tag(function_name, compile_func) + return func + + if func is None: + # @register.simple_tag(...) + return dec + elif callable(func): + # @register.simple_tag + return dec(func) + else: + raise TemplateSyntaxError("Invalid arguments provided to simple_tag") + + def assignment_tag(self, func=None, takes_context=None, name=None): + def dec(func): + params, varargs, varkw, defaults = getargspec(func) + + class AssignmentNode(TagHelperNode): + def __init__(self, takes_context, args, kwargs, target_var): + super(AssignmentNode, self).__init__(takes_context, args, kwargs) + self.target_var = target_var + + def render(self, context): + resolved_args, resolved_kwargs = self.get_resolved_arguments(context) + context[self.target_var] = func(*resolved_args, **resolved_kwargs) + return '' + + function_name = (name or + getattr(func, '_decorated_function', func).__name__) + + def compile_func(parser, token): + bits = token.split_contents()[1:] + if len(bits) < 2 or bits[-2] != 'as': + raise TemplateSyntaxError( + "'%s' tag takes at least 2 arguments and the " + "second last argument must be 'as'" % function_name) + target_var = bits[-1] + bits = bits[:-2] + args, kwargs = parse_bits(parser, bits, params, + varargs, varkw, defaults, takes_context, function_name) + return AssignmentNode(takes_context, args, kwargs, target_var) + + compile_func.__doc__ = func.__doc__ + self.tag(function_name, compile_func) + return func + + if func is None: + # @register.assignment_tag(...) + return dec + elif callable(func): + # @register.assignment_tag + return dec(func) + else: + raise TemplateSyntaxError("Invalid arguments provided to assignment_tag") + + def inclusion_tag(self, file_name, context_class=Context, takes_context=False, name=None): + def dec(func): + params, varargs, varkw, defaults = getargspec(func) + + class InclusionNode(TagHelperNode): + + def render(self, context): + resolved_args, resolved_kwargs = self.get_resolved_arguments(context) + _dict = func(*resolved_args, **resolved_kwargs) + + if not getattr(self, 'nodelist', False): + from django.template.loader import get_template, select_template + if isinstance(file_name, Template): + t = file_name + elif not isinstance(file_name, six.string_types) and is_iterable(file_name): + t = select_template(file_name) + else: + t = get_template(file_name) + self.nodelist = t.nodelist + new_context = context_class(_dict, **{ + 'autoescape': context.autoescape, + 'current_app': context.current_app, + 'use_l10n': context.use_l10n, + 'use_tz': context.use_tz, + }) + # Copy across the CSRF token, if present, because + # inclusion tags are often used for forms, and we need + # instructions for using CSRF protection to be as simple + # as possible. + csrf_token = context.get('csrf_token', None) + if csrf_token is not None: + new_context['csrf_token'] = csrf_token + return self.nodelist.render(new_context) + + function_name = (name or + getattr(func, '_decorated_function', func).__name__) + compile_func = partial(generic_tag_compiler, + params=params, varargs=varargs, varkw=varkw, + defaults=defaults, name=function_name, + takes_context=takes_context, node_class=InclusionNode) + compile_func.__doc__ = func.__doc__ + self.tag(function_name, compile_func) + return func + return dec + +def is_library_missing(name): + """Check if library that failed to load cannot be found under any + templatetags directory or does exist but fails to import. + + Non-existing condition is checked recursively for each subpackage in cases + like <appdir>/templatetags/subpackage/package/module.py. + """ + # Don't bother to check if '.' is in name since any name will be prefixed + # with some template root. + path, module = name.rsplit('.', 1) + try: + package = import_module(path) + return not module_has_submodule(package, module) + except ImportError: + return is_library_missing(path) + +def import_library(taglib_module): + """ + Load a template tag library module. + + Verifies that the library contains a 'register' attribute, and + returns that attribute as the representation of the library + """ + try: + mod = import_module(taglib_module) + except ImportError as e: + # If the ImportError is because the taglib submodule does not exist, + # that's not an error that should be raised. If the submodule exists + # and raised an ImportError on the attempt to load it, that we want + # to raise. + if is_library_missing(taglib_module): + return None + else: + raise InvalidTemplateLibrary("ImportError raised loading %s: %s" % + (taglib_module, e)) + try: + return mod.register + except AttributeError: + raise InvalidTemplateLibrary("Template library %s does not have " + "a variable named 'register'" % + taglib_module) + +templatetags_modules = [] + +def get_templatetags_modules(): + """ + Return the list of all available template tag modules. + + Caches the result for faster access. + """ + global templatetags_modules + if not templatetags_modules: + _templatetags_modules = [] + # Populate list once per process. Mutate the local list first, and + # then assign it to the global name to ensure there are no cases where + # two threads try to populate it simultaneously. + for app_module in ['django'] + list(settings.INSTALLED_APPS): + try: + templatetag_module = '%s.templatetags' % app_module + import_module(templatetag_module) + _templatetags_modules.append(templatetag_module) + except ImportError: + continue + templatetags_modules = _templatetags_modules + return templatetags_modules + +def get_library(library_name): + """ + Load the template library module with the given name. + + If library is not already loaded loop over all templatetags modules + to locate it. + + {% load somelib %} and {% load someotherlib %} loops twice. + + Subsequent loads eg. {% load somelib %} in the same process will grab + the cached module from libraries. + """ + lib = libraries.get(library_name, None) + if not lib: + templatetags_modules = get_templatetags_modules() + tried_modules = [] + for module in templatetags_modules: + taglib_module = '%s.%s' % (module, library_name) + tried_modules.append(taglib_module) + lib = import_library(taglib_module) + if lib: + libraries[library_name] = lib + break + if not lib: + raise InvalidTemplateLibrary("Template library %s not found, " + "tried %s" % + (library_name, + ','.join(tried_modules))) + return lib + + +def add_to_builtins(module): + builtins.append(import_library(module)) + + +add_to_builtins('django.template.defaulttags') +add_to_builtins('django.template.defaultfilters') diff --git a/lib/python2.7/site-packages/django/template/context.py b/lib/python2.7/site-packages/django/template/context.py new file mode 100644 index 0000000..1ef7e88 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/context.py @@ -0,0 +1,169 @@ +from copy import copy +from django.utils.module_loading import import_by_path + +# Cache of actual callables. +_standard_context_processors = None +# We need the CSRF processor no matter what the user has in their settings, +# because otherwise it is a security vulnerability, and we can't afford to leave +# this to human error or failure to read migration instructions. +_builtin_context_processors = ('django.core.context_processors.csrf',) + +class ContextPopException(Exception): + "pop() has been called more times than push()" + pass + +class BaseContext(object): + def __init__(self, dict_=None): + self._reset_dicts(dict_) + + def _reset_dicts(self, value=None): + builtins = {'True': True, 'False': False, 'None': None} + self.dicts = [builtins] + if value is not None: + self.dicts.append(value) + + def __copy__(self): + duplicate = copy(super(BaseContext, self)) + duplicate.dicts = self.dicts[:] + return duplicate + + def __repr__(self): + return repr(self.dicts) + + def __iter__(self): + for d in reversed(self.dicts): + yield d + + def push(self): + d = {} + self.dicts.append(d) + return d + + def pop(self): + if len(self.dicts) == 1: + raise ContextPopException + return self.dicts.pop() + + def __setitem__(self, key, value): + "Set a variable in the current context" + self.dicts[-1][key] = value + + def __getitem__(self, key): + "Get a variable's value, starting at the current context and going upward" + for d in reversed(self.dicts): + if key in d: + return d[key] + raise KeyError(key) + + def __delitem__(self, key): + "Delete a variable from the current context" + del self.dicts[-1][key] + + def has_key(self, key): + for d in self.dicts: + if key in d: + return True + return False + + def __contains__(self, key): + return self.has_key(key) + + def get(self, key, otherwise=None): + for d in reversed(self.dicts): + if key in d: + return d[key] + return otherwise + + def new(self, values=None): + """ + Returns a new context with the same properties, but with only the + values given in 'values' stored. + """ + new_context = copy(self) + new_context._reset_dicts(values) + return new_context + +class Context(BaseContext): + "A stack container for variable context" + def __init__(self, dict_=None, autoescape=True, current_app=None, + use_l10n=None, use_tz=None): + self.autoescape = autoescape + self.current_app = current_app + self.use_l10n = use_l10n + self.use_tz = use_tz + self.render_context = RenderContext() + super(Context, self).__init__(dict_) + + def __copy__(self): + duplicate = super(Context, self).__copy__() + duplicate.render_context = copy(self.render_context) + return duplicate + + def update(self, other_dict): + "Pushes other_dict to the stack of dictionaries in the Context" + if not hasattr(other_dict, '__getitem__'): + raise TypeError('other_dict must be a mapping (dictionary-like) object.') + self.dicts.append(other_dict) + return other_dict + +class RenderContext(BaseContext): + """ + A stack container for storing Template state. + + RenderContext simplifies the implementation of template Nodes by providing a + safe place to store state between invocations of a node's `render` method. + + The RenderContext also provides scoping rules that are more sensible for + 'template local' variables. The render context stack is pushed before each + template is rendered, creating a fresh scope with nothing in it. Name + resolution fails if a variable is not found at the top of the RequestContext + stack. Thus, variables are local to a specific template and don't affect the + rendering of other templates as they would if they were stored in the normal + template context. + """ + def __iter__(self): + for d in self.dicts[-1]: + yield d + + def has_key(self, key): + return key in self.dicts[-1] + + def get(self, key, otherwise=None): + d = self.dicts[-1] + if key in d: + return d[key] + return otherwise + +# This is a function rather than module-level procedural code because we only +# want it to execute if somebody uses RequestContext. +def get_standard_processors(): + from django.conf import settings + global _standard_context_processors + if _standard_context_processors is None: + processors = [] + collect = [] + collect.extend(_builtin_context_processors) + collect.extend(settings.TEMPLATE_CONTEXT_PROCESSORS) + for path in collect: + func = import_by_path(path) + processors.append(func) + _standard_context_processors = tuple(processors) + return _standard_context_processors + +class RequestContext(Context): + """ + This subclass of template.Context automatically populates itself using + the processors defined in TEMPLATE_CONTEXT_PROCESSORS. + Additional processors can be specified as a list of callables + using the "processors" keyword argument. + """ + def __init__(self, request, dict_=None, processors=None, current_app=None, + use_l10n=None, use_tz=None): + Context.__init__(self, dict_, current_app=current_app, + use_l10n=use_l10n, use_tz=use_tz) + if processors is None: + processors = () + else: + processors = tuple(processors) + for processor in get_standard_processors() + processors: + self.update(processor(request)) diff --git a/lib/python2.7/site-packages/django/template/debug.py b/lib/python2.7/site-packages/django/template/debug.py new file mode 100644 index 0000000..043dd91 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/debug.py @@ -0,0 +1,101 @@ +from django.template.base import Lexer, Parser, tag_re, NodeList, VariableNode, TemplateSyntaxError +from django.utils.encoding import force_text +from django.utils.html import escape +from django.utils.safestring import SafeData, EscapeData +from django.utils.formats import localize +from django.utils.timezone import template_localtime + + +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" + result, upto = [], 0 + for match in tag_re.finditer(self.template_string): + start, end = match.span() + if start > upto: + result.append(self.create_token(self.template_string[upto:start], (upto, start), False)) + upto = start + result.append(self.create_token(self.template_string[start:end], (start, end), True)) + upto = end + last_bit = self.template_string[upto:] + if last_bit: + result.append(self.create_token(last_bit, (upto, upto + len(last_bit)), False)) + return result + + def create_token(self, token_string, source, in_tag): + token = super(DebugLexer, self).create_token(token_string, in_tag) + token.source = self.origin, source + return token + +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.django_template_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_filter_error(self, token, e): + if not hasattr(e, 'django_template_source'): + e.django_template_source = token.source + + def compile_function_error(self, token, e): + if not hasattr(e, 'django_template_source'): + e.django_template_source = token.source + +class DebugNodeList(NodeList): + def render_node(self, node, context): + try: + return node.render(context) + except Exception as e: + if not hasattr(e, 'django_template_source'): + e.django_template_source = node.source + raise + + +class DebugVariableNode(VariableNode): + def render(self, context): + try: + output = self.filter_expression.resolve(context) + output = template_localtime(output, use_tz=context.use_tz) + output = localize(output, use_l10n=context.use_l10n) + output = force_text(output) + except UnicodeDecodeError: + return '' + except Exception as e: + if not hasattr(e, 'django_template_source'): + e.django_template_source = self.source + raise + if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData): + return escape(output) + else: + return output diff --git a/lib/python2.7/site-packages/django/template/defaultfilters.py b/lib/python2.7/site-packages/django/template/defaultfilters.py new file mode 100644 index 0000000..76c0121 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/defaultfilters.py @@ -0,0 +1,893 @@ +"""Default variable filters.""" +from __future__ import unicode_literals + +import re +import random as random_module +from decimal import Decimal, InvalidOperation, Context, ROUND_HALF_UP +from functools import wraps +from pprint import pformat + +from django.template.base import Variable, Library, VariableDoesNotExist +from django.conf import settings +from django.utils import formats +from django.utils.dateformat import format, time_format +from django.utils.encoding import force_text, iri_to_uri +from django.utils.html import (conditional_escape, escapejs, fix_ampersands, + escape, urlize as urlize_impl, linebreaks, strip_tags, avoid_wrapping) +from django.utils.http import urlquote +from django.utils.text import Truncator, wrap, phone2numeric +from django.utils.safestring import mark_safe, SafeData, mark_for_escaping +from django.utils import six +from django.utils.timesince import timesince, timeuntil +from django.utils.translation import ugettext, ungettext +from django.utils.text import normalize_newlines + +register = Library() + +####################### +# STRING DECORATOR # +####################### + +def stringfilter(func): + """ + Decorator for filters which should only receive unicode objects. The object + passed as the first positional argument will be converted to a unicode + object. + """ + def _dec(*args, **kwargs): + if args: + args = list(args) + args[0] = force_text(args[0]) + if (isinstance(args[0], SafeData) and + getattr(_dec._decorated_function, 'is_safe', False)): + return mark_safe(func(*args, **kwargs)) + return func(*args, **kwargs) + + # Include a reference to the real function (used to check original + # arguments by the template parser, and to bear the 'is_safe' attribute + # when multiple decorators are applied). + _dec._decorated_function = getattr(func, '_decorated_function', func) + + return wraps(func)(_dec) + + +################### +# STRINGS # +################### + +@register.filter(is_safe=True) +@stringfilter +def addslashes(value): + """ + Adds slashes before quotes. Useful for escaping strings in CSV, for + example. Less useful for escaping JavaScript; use the ``escapejs`` + filter instead. + """ + return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") + +@register.filter(is_safe=True) +@stringfilter +def capfirst(value): + """Capitalizes the first character of the value.""" + return value and value[0].upper() + value[1:] + +@register.filter("escapejs") +@stringfilter +def escapejs_filter(value): + """Hex encodes characters for use in JavaScript strings.""" + return escapejs(value) + +@register.filter("fix_ampersands", is_safe=True) +@stringfilter +def fix_ampersands_filter(value): + """Replaces ampersands with ``&`` entities.""" + return fix_ampersands(value) + +# Values for testing floatformat input against infinity and NaN representations, +# which differ across platforms and Python versions. Some (i.e. old Windows +# ones) are not recognized by Decimal but we want to return them unchanged vs. +# returning an empty string as we do for completely invalid input. Note these +# need to be built up from values that are not inf/nan, since inf/nan values do +# not reload properly from .pyc files on Windows prior to some level of Python 2.5 +# (see Python Issue757815 and Issue1080440). +pos_inf = 1e200 * 1e200 +neg_inf = -1e200 * 1e200 +nan = (1e200 * 1e200) // (1e200 * 1e200) +special_floats = [str(pos_inf), str(neg_inf), str(nan)] + +@register.filter(is_safe=True) +def floatformat(text, arg=-1): + """ + Displays a float to a specified number of decimal places. + + If called without an argument, it displays the floating point number with + one decimal place -- but only if there's a decimal place to be displayed: + + * num1 = 34.23234 + * num2 = 34.00000 + * num3 = 34.26000 + * {{ num1|floatformat }} displays "34.2" + * {{ num2|floatformat }} displays "34" + * {{ num3|floatformat }} displays "34.3" + + If arg is positive, it will always display exactly arg number of decimal + places: + + * {{ num1|floatformat:3 }} displays "34.232" + * {{ num2|floatformat:3 }} displays "34.000" + * {{ num3|floatformat:3 }} displays "34.260" + + If arg is negative, it will display arg number of decimal places -- but + only if there are places to be displayed: + + * {{ num1|floatformat:"-3" }} displays "34.232" + * {{ num2|floatformat:"-3" }} displays "34" + * {{ num3|floatformat:"-3" }} displays "34.260" + + If the input float is infinity or NaN, the (platform-dependent) string + representation of that value will be displayed. + """ + + try: + input_val = force_text(text) + d = Decimal(input_val) + except UnicodeEncodeError: + return '' + except InvalidOperation: + if input_val in special_floats: + return input_val + try: + d = Decimal(force_text(float(text))) + except (ValueError, InvalidOperation, TypeError, UnicodeEncodeError): + return '' + try: + p = int(arg) + except ValueError: + return input_val + + try: + m = int(d) - d + except (ValueError, OverflowError, InvalidOperation): + return input_val + + if not m and p < 0: + return mark_safe(formats.number_format('%d' % (int(d)), 0)) + + if p == 0: + exp = Decimal(1) + else: + exp = Decimal('1.0') / (Decimal(10) ** abs(p)) + try: + # Set the precision high enough to avoid an exception, see #15789. + tupl = d.as_tuple() + units = len(tupl[1]) - tupl[2] + prec = abs(p) + units + 1 + + # Avoid conversion to scientific notation by accessing `sign`, `digits` + # and `exponent` from `Decimal.as_tuple()` directly. + sign, digits, exponent = d.quantize(exp, ROUND_HALF_UP, + Context(prec=prec)).as_tuple() + digits = [six.text_type(digit) for digit in reversed(digits)] + while len(digits) <= abs(exponent): + digits.append('0') + digits.insert(-exponent, '.') + if sign: + digits.append('-') + number = ''.join(reversed(digits)) + return mark_safe(formats.number_format(number, abs(p))) + except InvalidOperation: + return input_val + +@register.filter(is_safe=True) +@stringfilter +def iriencode(value): + """Escapes an IRI value for use in a URL.""" + return force_text(iri_to_uri(value)) + +@register.filter(is_safe=True, needs_autoescape=True) +@stringfilter +def linenumbers(value, autoescape=None): + """Displays text with line numbers.""" + lines = value.split('\n') + # Find the maximum width of the line count, for use with zero padding + # string format command + width = six.text_type(len(six.text_type(len(lines)))) + if not autoescape or isinstance(value, SafeData): + for i, line in enumerate(lines): + lines[i] = ("%0" + width + "d. %s") % (i + 1, line) + else: + for i, line in enumerate(lines): + lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line)) + return mark_safe('\n'.join(lines)) + +@register.filter(is_safe=True) +@stringfilter +def lower(value): + """Converts a string into all lowercase.""" + return value.lower() + +@register.filter(is_safe=False) +@stringfilter +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(value) + +@register.filter(is_safe=True) +@stringfilter +def slugify(value): + """ + Converts to lowercase, removes non-word characters (alphanumerics and + underscores) and converts spaces to hyphens. Also strips leading and + trailing whitespace. + """ + from django.utils.text import slugify + return slugify(value) + +@register.filter(is_safe=True) +def stringformat(value, arg): + """ + Formats the variable according to the arg, 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 ("%" + six.text_type(arg)) % value + except (ValueError, TypeError): + return "" + +@register.filter(is_safe=True) +@stringfilter +def title(value): + """Converts a string into titlecase.""" + t = re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) + return re.sub("\d([A-Z])", lambda m: m.group(0).lower(), t) + +@register.filter(is_safe=True) +@stringfilter +def truncatechars(value, arg): + """ + Truncates a string after a certain number of characters. + + Argument: Number of characters to truncate after. + """ + try: + length = int(arg) + except ValueError: # Invalid literal for int(). + return value # Fail silently. + return Truncator(value).chars(length) + +@register.filter(is_safe=True) +@stringfilter +def truncatewords(value, arg): + """ + Truncates a string after a certain number of words. + + Argument: Number of words to truncate after. + + Newlines within the string are removed. + """ + try: + length = int(arg) + except ValueError: # Invalid literal for int(). + return value # Fail silently. + return Truncator(value).words(length, truncate=' ...') + +@register.filter(is_safe=True) +@stringfilter +def truncatewords_html(value, arg): + """ + Truncates HTML after a certain number of words. + + Argument: Number of words to truncate after. + + Newlines in the HTML are preserved. + """ + try: + length = int(arg) + except ValueError: # invalid literal for int() + return value # Fail silently. + return Truncator(value).words(length, html=True, truncate=' ...') + +@register.filter(is_safe=False) +@stringfilter +def upper(value): + """Converts a string into all uppercase.""" + return value.upper() + +@register.filter(is_safe=False) +@stringfilter +def urlencode(value, safe=None): + """ + Escapes a value for use in a URL. + + Takes an optional ``safe`` parameter used to determine the characters which + should not be escaped by Django's ``urlquote`` method. If not provided, the + default safe characters will be used (but an empty string can be provided + when *all* characters should be escaped). + """ + kwargs = {} + if safe is not None: + kwargs['safe'] = safe + return urlquote(value, **kwargs) + +@register.filter(is_safe=True, needs_autoescape=True) +@stringfilter +def urlize(value, autoescape=None): + """Converts URLs in plain text into clickable links.""" + return mark_safe(urlize_impl(value, nofollow=True, autoescape=autoescape)) + +@register.filter(is_safe=True, needs_autoescape=True) +@stringfilter +def urlizetrunc(value, limit, autoescape=None): + """ + 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. + """ + return mark_safe(urlize_impl(value, trim_url_limit=int(limit), nofollow=True, + autoescape=autoescape)) + +@register.filter(is_safe=False) +@stringfilter +def wordcount(value): + """Returns the number of words.""" + return len(value.split()) + +@register.filter(is_safe=True) +@stringfilter +def wordwrap(value, arg): + """ + Wraps words at specified line length. + + Argument: number of characters to wrap the text at. + """ + return wrap(value, int(arg)) + +@register.filter(is_safe=True) +@stringfilter +def ljust(value, arg): + """ + Left-aligns the value in a field of a given width. + + Argument: field size. + """ + return value.ljust(int(arg)) + +@register.filter(is_safe=True) +@stringfilter +def rjust(value, arg): + """ + Right-aligns the value in a field of a given width. + + Argument: field size. + """ + return value.rjust(int(arg)) + +@register.filter(is_safe=True) +@stringfilter +def center(value, arg): + """Centers the value in a field of a given width.""" + return value.center(int(arg)) + +@register.filter +@stringfilter +def cut(value, arg): + """ + Removes all values of arg from the given string. + """ + safe = isinstance(value, SafeData) + value = value.replace(arg, '') + if safe and arg != ';': + return mark_safe(value) + return value + +################### +# HTML STRINGS # +################### + +@register.filter("escape", is_safe=True) +@stringfilter +def escape_filter(value): + """ + Marks the value as a string that should not be auto-escaped. + """ + return mark_for_escaping(value) + +@register.filter(is_safe=True) +@stringfilter +def force_escape(value): + """ + Escapes a string's HTML. This returns a new string containing the escaped + characters (as opposed to "escape", which marks the content for later + possible escaping). + """ + return escape(value) + +@register.filter("linebreaks", is_safe=True, needs_autoescape=True) +@stringfilter +def linebreaks_filter(value, autoescape=None): + """ + Replaces line breaks in plain text with appropriate HTML; a single + newline becomes an HTML line break (``<br />``) and a new line + followed by a blank line becomes a paragraph break (``</p>``). + """ + autoescape = autoescape and not isinstance(value, SafeData) + return mark_safe(linebreaks(value, autoescape)) + +@register.filter(is_safe=True, needs_autoescape=True) +@stringfilter +def linebreaksbr(value, autoescape=None): + """ + Converts all newlines in a piece of plain text to HTML line breaks + (``<br />``). + """ + autoescape = autoescape and not isinstance(value, SafeData) + value = normalize_newlines(value) + if autoescape: + value = escape(value) + return mark_safe(value.replace('\n', '<br />')) + +@register.filter(is_safe=True) +@stringfilter +def safe(value): + """ + Marks the value as a string that should not be auto-escaped. + """ + return mark_safe(value) + +@register.filter(is_safe=True) +def safeseq(value): + """ + A "safe" filter for sequences. Marks each element in the sequence, + individually, as safe, after converting them to unicode. Returns a list + with the results. + """ + return [mark_safe(force_text(obj)) for obj in value] + +@register.filter(is_safe=True) +@stringfilter +def removetags(value, tags): + """Removes a space separated list of [X]HTML tags from the output.""" + from django.utils.html import remove_tags + return remove_tags(value, tags) + +@register.filter(is_safe=True) +@stringfilter +def striptags(value): + """Strips all [X]HTML tags.""" + return strip_tags(value) + +################### +# LISTS # +################### + +@register.filter(is_safe=False) +def dictsort(value, arg): + """ + Takes a list of dicts, returns that list sorted by the property given in + the argument. + """ + try: + return sorted(value, key=Variable(arg).resolve) + except (TypeError, VariableDoesNotExist): + return '' + +@register.filter(is_safe=False) +def dictsortreversed(value, arg): + """ + Takes a list of dicts, returns that list sorted in reverse order by the + property given in the argument. + """ + try: + return sorted(value, key=Variable(arg).resolve, reverse=True) + except (TypeError, VariableDoesNotExist): + return '' + +@register.filter(is_safe=False) +def first(value): + """Returns the first item in a list.""" + try: + return value[0] + except IndexError: + return '' + +@register.filter(is_safe=True, needs_autoescape=True) +def join(value, arg, autoescape=None): + """ + Joins a list with a string, like Python's ``str.join(list)``. + """ + value = map(force_text, value) + if autoescape: + value = [conditional_escape(v) for v in value] + try: + data = conditional_escape(arg).join(value) + except AttributeError: # fail silently but nicely + return value + return mark_safe(data) + +@register.filter(is_safe=True) +def last(value): + "Returns the last item in a list" + try: + return value[-1] + except IndexError: + return '' + +@register.filter(is_safe=True) +def length(value): + """Returns the length of the value - useful for lists.""" + try: + return len(value) + except (ValueError, TypeError): + return '' + +@register.filter(is_safe=False) +def length_is(value, arg): + """Returns a boolean of whether the value's length is the argument.""" + try: + return len(value) == int(arg) + except (ValueError, TypeError): + return '' + +@register.filter(is_safe=True) +def random(value): + """Returns a random item from the list.""" + return random_module.choice(value) + +@register.filter("slice", is_safe=True) +def slice_filter(value, arg): + """ + Returns a slice of the list. + + Uses the same syntax as Python's list slicing; see + http://www.diveintopython3.net/native-datatypes.html#slicinglists + 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. + +@register.filter(is_safe=True, needs_autoescape=True) +def unordered_list(value, autoescape=None): + """ + Recursively takes a self-nested list and returns an HTML unordered list -- + WITHOUT opening and closing <ul> 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:: + + <li>States + <ul> + <li>Kansas + <ul> + <li>Lawrence</li> + <li>Topeka</li> + </ul> + </li> + <li>Illinois</li> + </ul> + </li> + """ + if autoescape: + escaper = conditional_escape + else: + escaper = lambda x: x + def convert_old_style_list(list_): + """ + Converts old style lists to the new easier to understand format. + + The old list format looked like: + ['Item 1', [['Item 1.1', []], ['Item 1.2', []]] + + And it is converted to: + ['Item 1', ['Item 1.1', 'Item 1.2]] + """ + if not isinstance(list_, (tuple, list)) or len(list_) != 2: + return list_, False + first_item, second_item = list_ + if second_item == []: + return [first_item], True + try: + # see if second item is iterable + iter(second_item) + except TypeError: + return list_, False + old_style_list = True + new_second_item = [] + for sublist in second_item: + item, old_style_list = convert_old_style_list(sublist) + if not old_style_list: + break + new_second_item.extend(item) + if old_style_list: + second_item = new_second_item + return [first_item, second_item], old_style_list + def _helper(list_, tabs=1): + indent = '\t' * tabs + output = [] + + list_length = len(list_) + i = 0 + while i < list_length: + title = list_[i] + sublist = '' + sublist_item = None + if isinstance(title, (list, tuple)): + sublist_item = title + title = '' + elif i < list_length - 1: + next_item = list_[i+1] + if next_item and isinstance(next_item, (list, tuple)): + # The next item is a sub-list. + sublist_item = next_item + # We've processed the next item now too. + i += 1 + if sublist_item: + sublist = _helper(sublist_item, tabs+1) + sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist, + indent, indent) + output.append('%s<li>%s%s</li>' % (indent, + escaper(force_text(title)), sublist)) + i += 1 + return '\n'.join(output) + value, converted = convert_old_style_list(value) + return mark_safe(_helper(value)) + +################### +# INTEGERS # +################### + +@register.filter(is_safe=False) +def add(value, arg): + """Adds the arg to the value.""" + try: + return int(value) + int(arg) + except (ValueError, TypeError): + try: + return value + arg + except Exception: + return '' + +@register.filter(is_safe=False) +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 # +################### + +@register.filter(expects_localtime=True, is_safe=False) +def date(value, arg=None): + """Formats a date according to the given format.""" + if value in (None, ''): + return '' + if arg is None: + arg = settings.DATE_FORMAT + try: + return formats.date_format(value, arg) + except AttributeError: + try: + return format(value, arg) + except AttributeError: + return '' + +@register.filter(expects_localtime=True, is_safe=False) +def time(value, arg=None): + """Formats a time according to the given format.""" + if value in (None, ''): + return '' + if arg is None: + arg = settings.TIME_FORMAT + try: + return formats.time_format(value, arg) + except AttributeError: + try: + return time_format(value, arg) + except AttributeError: + return '' + +@register.filter("timesince", is_safe=False) +def timesince_filter(value, arg=None): + """Formats a date as the time since that date (i.e. "4 days, 6 hours").""" + if not value: + return '' + try: + if arg: + return timesince(value, arg) + return timesince(value) + except (ValueError, TypeError): + return '' + +@register.filter("timeuntil", is_safe=False) +def timeuntil_filter(value, arg=None): + """Formats a date as the time until that date (i.e. "4 days, 6 hours").""" + if not value: + return '' + try: + return timeuntil(value, arg) + except (ValueError, TypeError): + return '' + +################### +# LOGIC # +################### + +@register.filter(is_safe=False) +def default(value, arg): + """If value is unavailable, use given default.""" + return value or arg + +@register.filter(is_safe=False) +def default_if_none(value, arg): + """If value is None, use given default.""" + if value is None: + return arg + return value + +@register.filter(is_safe=False) +def divisibleby(value, arg): + """Returns True if the value is devisible by the argument.""" + return int(value) % int(arg) == 0 + +@register.filter(is_safe=False) +def yesno(value, arg=None): + """ + Given a string mapping values for true, false and (optionally) None, + returns one of those strings according 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 = ugettext('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 # +################### + +@register.filter(is_safe=True) +def filesizeformat(bytes): + """ + Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, + 102 bytes, etc). + """ + try: + bytes = float(bytes) + except (TypeError,ValueError,UnicodeDecodeError): + value = ungettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0} + return avoid_wrapping(value) + + filesize_number_format = lambda value: formats.number_format(round(value, 1), 1) + + KB = 1<<10 + MB = 1<<20 + GB = 1<<30 + TB = 1<<40 + PB = 1<<50 + + if bytes < KB: + value = ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes} + elif bytes < MB: + value = ugettext("%s KB") % filesize_number_format(bytes / KB) + elif bytes < GB: + value = ugettext("%s MB") % filesize_number_format(bytes / MB) + elif bytes < TB: + value = ugettext("%s GB") % filesize_number_format(bytes / GB) + elif bytes < PB: + value = ugettext("%s TB") % filesize_number_format(bytes / TB) + else: + value = ugettext("%s PB") % filesize_number_format(bytes / PB) + + return avoid_wrapping(value) + +@register.filter(is_safe=False) +def pluralize(value, arg='s'): + """ + Returns a plural suffix if the value is not 1. By default, 's' is used as + the suffix: + + * If value is 0, vote{{ value|pluralize }} displays "0 votes". + * If value is 1, vote{{ value|pluralize }} displays "1 vote". + * If value is 2, vote{{ value|pluralize }} displays "2 votes". + + If an argument is provided, that string is used instead: + + * If value is 0, class{{ value|pluralize:"es" }} displays "0 classes". + * If value is 1, class{{ value|pluralize:"es" }} displays "1 class". + * If value is 2, class{{ value|pluralize:"es" }} displays "2 classes". + + If the provided argument contains a comma, the text before the comma is + used for the singular case and the text after the comma is used for the + plural case: + + * If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies". + * If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy". + * If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies". + """ + if not ',' in arg: + arg = ',' + arg + bits = arg.split(',') + if len(bits) > 2: + return '' + singular_suffix, plural_suffix = bits[:2] + + try: + if int(value) != 1: + return plural_suffix + 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 plural_suffix + except TypeError: # len() of unsized object. + pass + return singular_suffix + +@register.filter("phone2numeric", is_safe=True) +def phone2numeric_filter(value): + """Takes a phone number and converts it in to its numerical equivalent.""" + return phone2numeric(value) + +@register.filter(is_safe=True) +def pprint(value): + """A wrapper around pprint.pprint -- for debugging, really.""" + try: + return pformat(value) + except Exception as e: + return "Error in formatting: %s" % force_text(e, errors="replace") diff --git a/lib/python2.7/site-packages/django/template/defaulttags.py b/lib/python2.7/site-packages/django/template/defaulttags.py new file mode 100644 index 0000000..554c7bc --- /dev/null +++ b/lib/python2.7/site-packages/django/template/defaulttags.py @@ -0,0 +1,1409 @@ +"""Default tags used by the template system, available to all templates.""" +from __future__ import unicode_literals + +import os +import sys +import re +from datetime import datetime +from itertools import groupby, cycle as itertools_cycle +import warnings + +from django.conf import settings +from django.template.base import (Node, NodeList, Template, Context, Library, + TemplateSyntaxError, VariableDoesNotExist, InvalidTemplateLibrary, + BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, + SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END, + VARIABLE_ATTRIBUTE_SEPARATOR, get_library, token_kwargs, kwarg_re, + render_value_in_context) +from django.template.smartif import IfParser, Literal +from django.template.defaultfilters import date +from django.utils.encoding import smart_text +from django.utils.safestring import mark_safe +from django.utils.html import format_html +from django.utils import six +from django.utils import timezone + +register = Library() + +class AutoEscapeControlNode(Node): + """Implements the actions of the autoescape tag.""" + def __init__(self, setting, nodelist): + self.setting, self.nodelist = setting, nodelist + + def render(self, context): + old_setting = context.autoescape + context.autoescape = self.setting + output = self.nodelist.render(context) + context.autoescape = old_setting + if self.setting: + return mark_safe(output) + else: + return output + +class CommentNode(Node): + def render(self, context): + return '' + +class CsrfTokenNode(Node): + def render(self, context): + csrf_token = context.get('csrf_token', None) + if csrf_token: + if csrf_token == 'NOTPROVIDED': + return format_html("") + else: + return format_html("<input type='hidden' name='csrfmiddlewaretoken' value='{0}' />", csrf_token) + else: + # It's very probable that the token is missing because of + # misconfiguration, so we raise a warning + from django.conf import settings + if settings.DEBUG: + warnings.warn("A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.") + return '' + +class CycleNode(Node): + def __init__(self, cyclevars, variable_name=None, silent=False, escape=False): + self.cyclevars = cyclevars + self.variable_name = variable_name + self.silent = silent + self.escape = escape # only while the "future" version exists + + def render(self, context): + if self not in context.render_context: + # First time the node is rendered in template + context.render_context[self] = itertools_cycle(self.cyclevars) + cycle_iter = context.render_context[self] + value = next(cycle_iter).resolve(context) + if self.variable_name: + context[self.variable_name] = value + if self.silent: + return '' + if not self.escape: + value = mark_safe(value) + return render_value_in_context(value, context) + +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. + context.update({'var': output}) + filtered = self.filter_expr.resolve(context) + context.pop() + return filtered + +class FirstOfNode(Node): + def __init__(self, variables, escape=False): + self.vars = variables + self.escape = escape # only while the "future" version exists + + def render(self, context): + for var in self.vars: + value = var.resolve(context, True) + if value: + if not self.escape: + value = mark_safe(value) + return render_value_in_context(value, context) + return '' + +class ForNode(Node): + child_nodelists = ('nodelist_loop', 'nodelist_empty') + + def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None): + self.loopvars, self.sequence = loopvars, sequence + self.is_reversed = is_reversed + self.nodelist_loop = nodelist_loop + if nodelist_empty is None: + self.nodelist_empty = NodeList() + else: + self.nodelist_empty = nodelist_empty + + def __repr__(self): + reversed_text = ' reversed' if self.is_reversed else '' + return "<For Node: for %s in %s, tail_len: %d%s>" % \ + (', '.join(self.loopvars), self.sequence, len(self.nodelist_loop), + reversed_text) + + def __iter__(self): + for node in self.nodelist_loop: + yield node + for node in self.nodelist_empty: + yield node + + def render(self, context): + if 'forloop' in context: + parentloop = context['forloop'] + else: + parentloop = {} + context.push() + try: + values = self.sequence.resolve(context, True) + except VariableDoesNotExist: + values = [] + if values is None: + values = [] + if not hasattr(values, '__len__'): + values = list(values) + len_values = len(values) + if len_values < 1: + context.pop() + return self.nodelist_empty.render(context) + nodelist = NodeList() + if self.is_reversed: + values = reversed(values) + unpack = len(self.loopvars) > 1 + # Create a forloop value in the context. We'll update counters on each + # iteration just below. + loop_dict = context['forloop'] = {'parentloop': parentloop} + for i, item in enumerate(values): + # Shortcuts for current loop iteration number. + loop_dict['counter0'] = i + loop_dict['counter'] = i+1 + # Reverse counter iteration numbers. + loop_dict['revcounter'] = len_values - i + loop_dict['revcounter0'] = len_values - i - 1 + # Boolean values designating first and last times through loop. + loop_dict['first'] = (i == 0) + loop_dict['last'] = (i == len_values - 1) + + pop_context = False + if unpack: + # If there are multiple loop variables, unpack the item into + # them. + try: + unpacked_vars = dict(zip(self.loopvars, item)) + except TypeError: + pass + else: + pop_context = True + context.update(unpacked_vars) + else: + context[self.loopvars[0]] = item + # In TEMPLATE_DEBUG mode provide source of the node which + # actually raised the exception + if settings.TEMPLATE_DEBUG: + for node in self.nodelist_loop: + try: + nodelist.append(node.render(context)) + except Exception as e: + if not hasattr(e, 'django_template_source'): + e.django_template_source = node.source + raise + else: + for node in self.nodelist_loop: + nodelist.append(node.render(context)) + if pop_context: + # The loop variables were pushed on to the context so pop them + # off again. This is necessary because the tag lets the length + # of loopvars differ to the length of each set of items and we + # don't want to leave any vars from the previous loop on the + # context. + context.pop() + context.pop() + return nodelist.render(context) + +class IfChangedNode(Node): + child_nodelists = ('nodelist_true', 'nodelist_false') + + def __init__(self, nodelist_true, nodelist_false, *varlist): + self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false + self._varlist = varlist + + def render(self, context): + # Init state storage + state_frame = self._get_context_stack_frame(context) + if self not in state_frame: + state_frame[self] = None + + nodelist_true_output = None + try: + if self._varlist: + # Consider multiple parameters. This automatically behaves + # like an OR evaluation of the multiple variables. + compare_to = [var.resolve(context, True) for var in self._varlist] + else: + # The "{% ifchanged %}" syntax (without any variables) compares the rendered output. + compare_to = nodelist_true_output = self.nodelist_true.render(context) + except VariableDoesNotExist: + compare_to = None + + if compare_to != state_frame[self]: + state_frame[self] = compare_to + return nodelist_true_output or self.nodelist_true.render(context) # render true block if not already rendered + elif self.nodelist_false: + return self.nodelist_false.render(context) + return '' + + def _get_context_stack_frame(self, context): + # The Context object behaves like a stack where each template tag can create a new scope. + # Find the place where to store the state to detect changes. + if 'forloop' in context: + # Ifchanged is bound to the local for loop. + # When there is a loop-in-loop, the state is bound to the inner loop, + # so it resets when the outer loop continues. + return context['forloop'] + else: + # Using ifchanged outside loops. Effectively this is a no-op because the state is associated with 'self'. + return context.render_context + +class IfEqualNode(Node): + child_nodelists = ('nodelist_true', 'nodelist_false') + + 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 "<IfEqualNode>" + + def render(self, context): + val1 = self.var1.resolve(context, True) + val2 = self.var2.resolve(context, True) + 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, conditions_nodelists): + self.conditions_nodelists = conditions_nodelists + + def __repr__(self): + return "<IfNode>" + + def __iter__(self): + for _, nodelist in self.conditions_nodelists: + for node in nodelist: + yield node + + @property + def nodelist(self): + return NodeList(node for _, nodelist in self.conditions_nodelists for node in nodelist) + + def render(self, context): + for condition, nodelist in self.conditions_nodelists: + + if condition is not None: # if / elif clause + try: + match = condition.eval(context) + except VariableDoesNotExist: + match = None + else: # else clause + match = True + + if match: + return nodelist.render(context) + + return '' + +class RegroupNode(Node): + def __init__(self, target, expression, var_name): + self.target, self.expression = target, expression + self.var_name = var_name + + def resolve_expression(self, obj, context): + # This method is called for each object in self.target. See regroup() + # for the reason why we temporarily put the object in the context. + context[self.var_name] = obj + return self.expression.resolve(context, True) + + def render(self, context): + obj_list = self.target.resolve(context, True) + if obj_list == None: + # target variable wasn't found in context; fail silently. + context[self.var_name] = [] + return '' + # List of dictionaries in the format: + # {'grouper': 'key', 'list': [list of contents]}. + context[self.var_name] = [ + {'grouper': key, 'list': list(val)} + for key, val in + groupby(obj_list, lambda obj: self.resolve_expression(obj, context)) + ] + return '' + +def include_is_allowed(filepath): + filepath = os.path.abspath(filepath) + for root in settings.ALLOWED_INCLUDE_ROOTS: + if filepath.startswith(root): + return True + return False + +class SsiNode(Node): + def __init__(self, filepath, parsed): + self.filepath = filepath + self.parsed = parsed + + def render(self, context): + filepath = self.filepath.resolve(context) + + if not include_is_allowed(filepath): + if settings.DEBUG: + return "[Didn't have permission to include file]" + else: + return '' # Fail silently for invalid includes. + try: + with open(filepath, 'r') as fp: + output = fp.read() + except IOError: + output = '' + if self.parsed: + try: + t = Template(output, name=filepath) + return t.render(context) + except TemplateSyntaxError as e: + if settings.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): + tzinfo = timezone.get_current_timezone() if settings.USE_TZ else None + return date(datetime.now(tz=tzinfo), self.format_string) + +class SpacelessNode(Node): + def __init__(self, nodelist): + self.nodelist = nodelist + + def render(self, context): + from django.utils.html import strip_spaces_between_tags + return strip_spaces_between_tags(self.nodelist.render(context).strip()) + +class TemplateTagNode(Node): + mapping = {'openblock': BLOCK_TAG_START, + 'closeblock': BLOCK_TAG_END, + 'openvariable': VARIABLE_TAG_START, + 'closevariable': VARIABLE_TAG_END, + 'openbrace': SINGLE_BRACE_START, + 'closebrace': SINGLE_BRACE_END, + 'opencomment': COMMENT_TAG_START, + 'closecomment': COMMENT_TAG_END, + } + + def __init__(self, tagtype): + self.tagtype = tagtype + + def render(self, context): + return self.mapping.get(self.tagtype, '') + +class URLNode(Node): + def __init__(self, view_name, args, kwargs, asvar): + self.view_name = view_name + self.args = args + self.kwargs = kwargs + self.asvar = asvar + + def render(self, context): + from django.core.urlresolvers import reverse, NoReverseMatch + args = [arg.resolve(context) for arg in self.args] + kwargs = dict([(smart_text(k, 'ascii'), v.resolve(context)) + for k, v in self.kwargs.items()]) + + view_name = self.view_name.resolve(context) + + if not view_name: + raise NoReverseMatch("'url' requires a non-empty first argument. " + "The syntax changed in Django 1.5, see the docs.") + + # Try to look up the URL twice: once given the view name, and again + # relative to what we guess is the "main" app. If they both fail, + # re-raise the NoReverseMatch unless we're using the + # {% url ... as var %} construct in which case return nothing. + url = '' + try: + url = reverse(view_name, args=args, kwargs=kwargs, current_app=context.current_app) + except NoReverseMatch: + exc_info = sys.exc_info() + if settings.SETTINGS_MODULE: + project_name = settings.SETTINGS_MODULE.split('.')[0] + try: + url = reverse(project_name + '.' + view_name, + args=args, kwargs=kwargs, + current_app=context.current_app) + except NoReverseMatch: + if self.asvar is None: + # Re-raise the original exception, not the one with + # the path relative to the project. This makes a + # better error message. + six.reraise(*exc_info) + else: + if self.asvar is None: + raise + + if self.asvar: + context[self.asvar] = url + return '' + else: + return url + +class VerbatimNode(Node): + def __init__(self, content): + self.content = content + + def render(self, context): + return self.content + +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) + max_value = self.max_expr.resolve(context) + max_width = int(self.max_width.resolve(context)) + except VariableDoesNotExist: + return '' + except (ValueError, TypeError): + raise TemplateSyntaxError("widthratio final argument must be a number") + try: + value = float(value) + max_value = float(max_value) + ratio = (value / max_value) * max_width + except ZeroDivisionError: + return '0' + except (ValueError, TypeError): + return '' + return str(int(round(ratio))) + +class WithNode(Node): + def __init__(self, var, name, nodelist, extra_context=None): + self.nodelist = nodelist + # var and name are legacy attributes, being left in case they are used + # by third-party subclasses of this Node. + self.extra_context = extra_context or {} + if name: + self.extra_context[name] = var + + def __repr__(self): + return "<WithNode>" + + def render(self, context): + values = dict([(key, val.resolve(context)) for key, val in + six.iteritems(self.extra_context)]) + context.update(values) + output = self.nodelist.render(context) + context.pop() + return output + +@register.tag +def autoescape(parser, token): + """ + Force autoescape behavior for this block. + """ + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments + args = token.contents.split() + if len(args) != 2: + raise TemplateSyntaxError("'autoescape' tag requires exactly one argument.") + arg = args[1] + if arg not in ('on', 'off'): + raise TemplateSyntaxError("'autoescape' argument should be 'on' or 'off'") + nodelist = parser.parse(('endautoescape',)) + parser.delete_first_token() + return AutoEscapeControlNode((arg == 'on'), nodelist) + +@register.tag +def comment(parser, token): + """ + Ignores everything between ``{% comment %}`` and ``{% endcomment %}``. + """ + parser.skip_past('endcomment') + return CommentNode() + +@register.tag +def cycle(parser, token, escape=False): + """ + Cycles 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 %} + <tr class="{% cycle 'row1' 'row2' %}"> + ... + </tr> + {% 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:: + + <tr class="{% cycle 'row1' 'row2' 'row3' as rowcolors %}">...</tr> + <tr class="{% cycle rowcolors %}">...</tr> + <tr class="{% cycle rowcolors %}">...</tr> + + You can use any number of values, separated by spaces. Commas can also + be used to separate values; if a comma is used, the cycle values are + interpreted as literal strings. + + The optional flag "silent" can be used to prevent the cycle declaration + from returning any value:: + + {% for o in some_list %} + {% cycle 'row1' 'row2' as rowcolors silent %} + <tr class="{{ rowcolors }}">{% include "subtemplate.html " %}</tr> + {% endfor %} + + """ + if not escape: + warnings.warn( + "'The `cycle` template tag is changing to escape its arguments; " + "the non-autoescaping version is deprecated. Load it " + "from the `future` tag library to start using the new behavior.", + PendingDeprecationWarning, stacklevel=2) + + # 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.split_contents() + + if len(args) < 2: + raise TemplateSyntaxError("'cycle' tag requires at least two arguments") + + if ',' in args[1]: + # Backwards compatibility: {% cycle a,b %} or {% cycle a,b as foo %} + # case. + args[1:2] = ['"%s"' % arg for arg in args[1].split(",")] + + if len(args) == 2: + # {% cycle foo %} case. + name = args[1] + if not hasattr(parser, '_namedCycleNodes'): + raise TemplateSyntaxError("No named cycles in template. '%s' is not defined" % name) + if not name in parser._namedCycleNodes: + raise TemplateSyntaxError("Named cycle '%s' does not exist" % name) + return parser._namedCycleNodes[name] + + as_form = False + + if len(args) > 4: + # {% cycle ... as foo [silent] %} case. + if args[-3] == "as": + if args[-1] != "silent": + raise TemplateSyntaxError("Only 'silent' flag is allowed after cycle's name, not '%s'." % args[-1]) + as_form = True + silent = True + args = args[:-1] + elif args[-2] == "as": + as_form = True + silent = False + + if as_form: + name = args[-1] + values = [parser.compile_filter(arg) for arg in args[1:-2]] + node = CycleNode(values, name, silent=silent, escape=escape) + if not hasattr(parser, '_namedCycleNodes'): + parser._namedCycleNodes = {} + parser._namedCycleNodes[name] = node + else: + values = [parser.compile_filter(arg) for arg in args[1:]] + node = CycleNode(values, escape=escape) + return node + +@register.tag +def csrf_token(parser, token): + return CsrfTokenNode() + +@register.tag +def debug(parser, token): + """ + Outputs a whole load of debugging information, including the current + context and imported modules. + + Sample usage:: + + <pre> + {% debug %} + </pre> + """ + return DebugNode() + +@register.tag('filter') +def do_filter(parser, token): + """ + Filters the contents of the block through variable filters. + + Filters can also be piped through each other, and they can have + arguments -- just like in variable syntax. + + Sample usage:: + + {% filter force_escape|lower %} + This text will be HTML-escaped, and will appear in lowercase. + {% endfilter %} + + Note that the ``escape`` and ``safe`` filters are not acceptable arguments. + Instead, use the ``autoescape`` tag to manage autoescaping for blocks of + template code. + """ + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments + _, rest = token.contents.split(None, 1) + filter_expr = parser.compile_filter("var|%s" % (rest)) + for func, unused in filter_expr.filters: + filter_name = getattr(func, '_filter_name', None) + if filter_name in ('escape', 'safe'): + raise TemplateSyntaxError('"filter %s" is not permitted. Use the "autoescape" tag instead.' % filter_name) + nodelist = parser.parse(('endfilter',)) + parser.delete_first_token() + return FilterNode(filter_expr, nodelist) + +@register.tag +def firstof(parser, token, escape=False): + """ + Outputs the first variable passed that is not False, without escaping. + + Outputs nothing if all the passed variables are False. + + Sample usage:: + + {% firstof var1 var2 var3 %} + + This is equivalent to:: + + {% if var1 %} + {{ var1|safe }} + {% elif var2 %} + {{ var2|safe }} + {% elif var3 %} + {{ var3|safe }} + {% endif %} + + but obviously much cleaner! + + You can also use a literal string as a fallback value in case all + passed variables are False:: + + {% firstof var1 var2 var3 "fallback value" %} + + If you want to escape the output, use a filter tag:: + + {% filter force_escape %} + {% firstof var1 var2 var3 "fallback value" %} + {% endfilter %} + + """ + if not escape: + warnings.warn( + "'The `firstof` template tag is changing to escape its arguments; " + "the non-autoescaping version is deprecated. Load it " + "from the `future` tag library to start using the new behavior.", + PendingDeprecationWarning, stacklevel=2) + + bits = token.split_contents()[1:] + if len(bits) < 1: + raise TemplateSyntaxError("'firstof' statement requires at least one argument") + return FirstOfNode([parser.compile_filter(bit) for bit in bits], escape=escape) + +@register.tag('for') +def do_for(parser, token): + """ + Loops over each item in an array. + + For example, to display a list of athletes given ``athlete_list``:: + + <ul> + {% for athlete in athlete_list %} + <li>{{ athlete.name }}</li> + {% endfor %} + </ul> + + You can loop over a list in reverse by using + ``{% for obj in list reversed %}``. + + You can also unpack multiple values from a two-dimensional array:: + + {% for key,value in dict.items %} + {{ key }}: {{ value }} + {% endfor %} + + The ``for`` tag can take an optional ``{% empty %}`` clause that will + be displayed if the given array is empty or could not be found:: + + <ul> + {% for athlete in athlete_list %} + <li>{{ athlete.name }}</li> + {% empty %} + <li>Sorry, no athletes in this list.</li> + {% endfor %} + <ul> + + The above is equivalent to -- but shorter, cleaner, and possibly faster + than -- the following:: + + <ul> + {% if althete_list %} + {% for athlete in athlete_list %} + <li>{{ athlete.name }}</li> + {% endfor %} + {% else %} + <li>Sorry, no athletes in this list.</li> + {% endif %} + </ul> + + 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.split_contents() + if len(bits) < 4: + raise TemplateSyntaxError("'for' statements should have at least four" + " words: %s" % token.contents) + + is_reversed = bits[-1] == 'reversed' + in_index = -3 if is_reversed else -2 + if bits[in_index] != 'in': + raise TemplateSyntaxError("'for' statements should use the format" + " 'for x in y': %s" % token.contents) + + loopvars = re.split(r' *, *', ' '.join(bits[1:in_index])) + for var in loopvars: + if not var or ' ' in var: + raise TemplateSyntaxError("'for' tag received an invalid argument:" + " %s" % token.contents) + + sequence = parser.compile_filter(bits[in_index+1]) + nodelist_loop = parser.parse(('empty', 'endfor',)) + token = parser.next_token() + if token.contents == 'empty': + nodelist_empty = parser.parse(('endfor',)) + parser.delete_first_token() + else: + nodelist_empty = None + return ForNode(loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty) + +def do_ifequal(parser, token, negate): + bits = list(token.split_contents()) + 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() + val1 = parser.compile_filter(bits[1]) + val2 = parser.compile_filter(bits[2]) + return IfEqualNode(val1, val2, nodelist_true, nodelist_false, negate) + +@register.tag +def ifequal(parser, token): + """ + Outputs the contents of the block if the two arguments equal each other. + + Examples:: + + {% ifequal user.id comment.user_id %} + ... + {% endifequal %} + + {% ifnotequal user.id comment.user_id %} + ... + {% else %} + ... + {% endifnotequal %} + """ + return do_ifequal(parser, token, False) + +@register.tag +def ifnotequal(parser, token): + """ + Outputs the contents of the block if the two arguments are not equal. + See ifequal. + """ + return do_ifequal(parser, token, True) + +class TemplateLiteral(Literal): + def __init__(self, value, text): + self.value = value + self.text = text # for better error messages + + def display(self): + return self.text + + def eval(self, context): + return self.value.resolve(context, ignore_failures=True) + +class TemplateIfParser(IfParser): + error_class = TemplateSyntaxError + + def __init__(self, parser, *args, **kwargs): + self.template_parser = parser + super(TemplateIfParser, self).__init__(*args, **kwargs) + + def create_var(self, value): + return TemplateLiteral(self.template_parser.compile_filter(value), value) + +@register.tag('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 athlete_list %} + Number of athletes: {{ athlete_list|count }} + {% elif athlete_in_locker_room_list %} + Athletes should be out of the locker room soon! + {% 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 may take one or several `` {% elif %}`` + clauses, as well as an ``{% else %}`` clause that will be displayed if all + previous conditions fail. These clauses are optional. + + ``if`` tags may use ``or``, ``and`` 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 athlete_list and coach_list %} + Both atheletes and coaches are available. + {% endif %} + + {% if not athlete_list or coach_list %} + There are no athletes, or there are some coaches. + {% endif %} + + {% if athlete_list and not coach_list %} + There are some athletes and absolutely no coaches. + {% endif %} + + Comparison operators are also available, and the use of filters is also + allowed, for example:: + + {% if articles|length >= 5 %}...{% endif %} + + Arguments and operators _must_ have a space between them, so + ``{% if 1>2 %}`` is not a valid if tag. + + All supported operators are: ``or``, ``and``, ``in``, ``not in`` + ``==`` (or ``=``), ``!=``, ``>``, ``>=``, ``<`` and ``<=``. + + Operator precedence follows Python. + """ + # {% if ... %} + bits = token.split_contents()[1:] + condition = TemplateIfParser(parser, bits).parse() + nodelist = parser.parse(('elif', 'else', 'endif')) + conditions_nodelists = [(condition, nodelist)] + token = parser.next_token() + + # {% elif ... %} (repeatable) + while token.contents.startswith('elif'): + bits = token.split_contents()[1:] + condition = TemplateIfParser(parser, bits).parse() + nodelist = parser.parse(('elif', 'else', 'endif')) + conditions_nodelists.append((condition, nodelist)) + token = parser.next_token() + + # {% else %} (optional) + if token.contents == 'else': + nodelist = parser.parse(('endif',)) + conditions_nodelists.append((None, nodelist)) + token = parser.next_token() + + # {% endif %} + assert token.contents == 'endif' + + return IfNode(conditions_nodelists) + + +@register.tag +def ifchanged(parser, token): + """ + Checks if a value has changed from the last iteration of a loop. + + The ``{% ifchanged %}`` block tag is used within a loop. It has two + possible uses. + + 1. Checks its own rendered contents against its previous state and only + displays the content if it has changed. For example, this displays a + list of days, only displaying the month if it changes:: + + <h1>Archive for {{ year }}</h1> + + {% for date in days %} + {% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %} + <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a> + {% endfor %} + + 2. If given one or more variables, check whether any variable has changed. + For example, the following shows the date every time it changes, while + showing the hour if either the hour or the date has changed:: + + {% for date in days %} + {% ifchanged date.date %} {{ date.date }} {% endifchanged %} + {% ifchanged date.hour date.date %} + {{ date.hour }} + {% endifchanged %} + {% endfor %} + """ + bits = token.split_contents() + nodelist_true = parser.parse(('else', 'endifchanged')) + token = parser.next_token() + if token.contents == 'else': + nodelist_false = parser.parse(('endifchanged',)) + parser.delete_first_token() + else: + nodelist_false = NodeList() + values = [parser.compile_filter(bit) for bit in bits[1:]] + return IfChangedNode(nodelist_true, nodelist_false, *values) + +@register.tag +def ssi(parser, token): + """ + Outputs 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 path -- + 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.split_contents() + 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]) + filepath = parser.compile_filter(bits[1]) + return SsiNode(filepath, parsed) + +@register.tag +def load(parser, token): + """ + Loads a custom template tag set. + + For example, to load the template tags in + ``django/templatetags/news/photos.py``:: + + {% load news.photos %} + + Can also be used to load an individual tag/filter from + a library:: + + {% load byline from news %} + + """ + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments + bits = token.contents.split() + if len(bits) >= 4 and bits[-2] == "from": + try: + taglib = bits[-1] + lib = get_library(taglib) + except InvalidTemplateLibrary as e: + raise TemplateSyntaxError("'%s' is not a valid tag library: %s" % + (taglib, e)) + else: + temp_lib = Library() + for name in bits[1:-2]: + if name in lib.tags: + temp_lib.tags[name] = lib.tags[name] + # a name could be a tag *and* a filter, so check for both + if name in lib.filters: + temp_lib.filters[name] = lib.filters[name] + elif name in lib.filters: + temp_lib.filters[name] = lib.filters[name] + else: + raise TemplateSyntaxError("'%s' is not a valid tag or filter in tag library '%s'" % + (name, taglib)) + parser.add_library(temp_lib) + else: + for taglib in bits[1:]: + # add the library to the parser + try: + lib = get_library(taglib) + parser.add_library(lib) + except InvalidTemplateLibrary as e: + raise TemplateSyntaxError("'%s' is not a valid tag library: %s" % + (taglib, e)) + return LoadNode() + +@register.tag +def now(parser, token): + """ + Displays 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.split_contents() + if len(bits) != 2: + raise TemplateSyntaxError("'now' statement takes one argument") + format_string = bits[1][1:-1] + return NowNode(format_string) + +@register.tag +def regroup(parser, token): + """ + Regroups 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 %} + <ul> + {% for group in grouped %} + <li>{{ group.grouper }} + <ul> + {% for item in group.list %} + <li>{{ item }}</li> + {% endfor %} + </ul> + {% endfor %} + </ul> + + 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 %} + + """ + bits = token.split_contents() + if len(bits) != 6: + raise TemplateSyntaxError("'regroup' tag takes five arguments") + target = parser.compile_filter(bits[1]) + if bits[2] != 'by': + raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'") + if bits[4] != 'as': + raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must" + " be 'as'") + var_name = bits[5] + # RegroupNode will take each item in 'target', put it in the context under + # 'var_name', evaluate 'var_name'.'expression' in the current context, and + # group by the resulting value. After all items are processed, it will + # save the final result in the context under 'var_name', thus clearing the + # temporary values. This hack is necessary because the template engine + # doesn't provide a context-aware equivalent of Python's getattr. + expression = parser.compile_filter(var_name + + VARIABLE_ATTRIBUTE_SEPARATOR + + bits[3]) + return RegroupNode(target, expression, var_name) + +@register.tag +def spaceless(parser, token): + """ + Removes whitespace between HTML tags, including tab and newline characters. + + Example usage:: + + {% spaceless %} + <p> + <a href="foo/">Foo</a> + </p> + {% endspaceless %} + + This example would return this HTML:: + + <p><a href="foo/">Foo</a></p> + + Only space between *tags* is normalized -- not space between tags and text. + In this example, the space around ``Hello`` won't be stripped:: + + {% spaceless %} + <strong> + Hello + </strong> + {% endspaceless %} + """ + nodelist = parser.parse(('endspaceless',)) + parser.delete_first_token() + return SpacelessNode(nodelist) + +@register.tag +def templatetag(parser, token): + """ + Outputs 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`` ``}}`` + ``openbrace`` ``{`` + ``closebrace`` ``}`` + ``opencomment`` ``{#`` + ``closecomment`` ``#}`` + ================== ======= + """ + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments + bits = token.contents.split() + if len(bits) != 2: + raise TemplateSyntaxError("'templatetag' statement takes one argument") + tag = bits[1] + if tag not in TemplateTagNode.mapping: + raise TemplateSyntaxError("Invalid templatetag argument: '%s'." + " Must be one of: %s" % + (tag, list(TemplateTagNode.mapping))) + return TemplateTagNode(tag) + +@register.tag +def url(parser, token): + """ + Returns an absolute URL matching given view with its parameters. + + This is a way to define links that aren't tied to a particular URL + configuration:: + + {% url "path.to.some_view" arg1 arg2 %} + + or + + {% url "path.to.some_view" name1=value1 name2=value2 %} + + The first argument is a path to a view. It can be an absolute Python path + or just ``app_name.view_name`` without the project name if the view is + located inside the project. + + Other arguments are space-separated values that will be filled in place of + positional and keyword arguments in the URL. Don't mix positional and + keyword arguments. + + All arguments for the URL should be present. + + For example if you have a view ``app_name.client`` taking client's id and + the corresponding line in a URLconf looks like this:: + + ('^client/(\d+)/$', 'app_name.client') + + and this app's URLconf is included into the project's URLconf under some + path:: + + ('^clients/', include('project_name.app_name.urls')) + + then in a template you can create a link for a certain client like this:: + + {% url "app_name.client" client.id %} + + The URL will look like ``/clients/client/123/``. + + The first argument can also be a named URL instead of the Python path to + the view callable. For example if the URLconf entry looks like this:: + + url('^client/(\d+)/$', name='client-detail-view') + + then in the template you can use:: + + {% url "client-detail-view" client.id %} + + There is even another possible value type for the first argument. It can be + the name of a template variable that will be evaluated to obtain the view + name or the URL name, e.g.:: + + {% with view_path="app_name.client" %} + {% url view_path client.id %} + {% endwith %} + + or, + + {% with url_name="client-detail-view" %} + {% url url_name client.id %} + {% endwith %} + + """ + bits = token.split_contents() + if len(bits) < 2: + raise TemplateSyntaxError("'%s' takes at least one argument" + " (path to a view)" % bits[0]) + try: + viewname = parser.compile_filter(bits[1]) + except TemplateSyntaxError as exc: + exc.args = (exc.args[0] + ". " + "The syntax of 'url' changed in Django 1.5, see the docs."), + raise + args = [] + kwargs = {} + asvar = None + bits = bits[2:] + if len(bits) >= 2 and bits[-2] == 'as': + asvar = bits[-1] + bits = bits[:-2] + + if len(bits): + for bit in bits: + match = kwarg_re.match(bit) + if not match: + raise TemplateSyntaxError("Malformed arguments to url tag") + name, value = match.groups() + if name: + kwargs[name] = parser.compile_filter(value) + else: + args.append(parser.compile_filter(value)) + + return URLNode(viewname, args, kwargs, asvar) + +@register.tag +def verbatim(parser, token): + """ + Stops the template engine from rendering the contents of this block tag. + + Usage:: + + {% verbatim %} + {% don't process this %} + {% endverbatim %} + + You can also designate a specific closing tag block (allowing the + unrendered use of ``{% endverbatim %}``):: + + {% verbatim myblock %} + ... + {% endverbatim myblock %} + """ + nodelist = parser.parse(('endverbatim',)) + parser.delete_first_token() + return VerbatimNode(nodelist.render(Context())) + +@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:: + + <img src='bar.gif' height='10' width='{% widthratio this_value max_value max_width %}' /> + + If ``this_value`` is 175, ``max_value`` is 200, and ``max_width`` is 100, + 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.split_contents() + if len(bits) != 4: + raise TemplateSyntaxError("widthratio takes three arguments") + tag, this_value_expr, max_value_expr, max_width = bits + + return WidthRatioNode(parser.compile_filter(this_value_expr), + parser.compile_filter(max_value_expr), + parser.compile_filter(max_width)) + +@register.tag('with') +def do_with(parser, token): + """ + Adds one or more values to the context (inside of this block) for caching + and easy access. + + For example:: + + {% with total=person.some_sql_method %} + {{ total }} object{{ total|pluralize }} + {% endwith %} + + Multiple values can be added to the context:: + + {% with foo=1 bar=2 %} + ... + {% endwith %} + + The legacy format of ``{% with person.some_sql_method as total %}`` is + still accepted. + """ + bits = token.split_contents() + remaining_bits = bits[1:] + extra_context = token_kwargs(remaining_bits, parser, support_legacy=True) + if not extra_context: + raise TemplateSyntaxError("%r expected at least one variable " + "assignment" % bits[0]) + if remaining_bits: + raise TemplateSyntaxError("%r received an invalid token: %r" % + (bits[0], remaining_bits[0])) + nodelist = parser.parse(('endwith',)) + parser.delete_first_token() + return WithNode(None, None, nodelist, extra_context=extra_context) diff --git a/lib/python2.7/site-packages/django/template/loader.py b/lib/python2.7/site-packages/django/template/loader.py new file mode 100644 index 0000000..6df4e43 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/loader.py @@ -0,0 +1,188 @@ +# 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. +# +# A loader may return an already-compiled template instead of the actual +# template source. In that case the path returned should be None, since the +# path information is associated with the template during the compilation, +# which has already been done. +# +# 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.template.base import Origin, Template, Context, TemplateDoesNotExist, add_to_builtins +from django.conf import settings +from django.utils.module_loading import import_by_path +from django.utils import six + +template_source_loaders = None + +class BaseLoader(object): + is_usable = False + + def __init__(self, *args, **kwargs): + pass + + def __call__(self, template_name, template_dirs=None): + return self.load_template(template_name, template_dirs) + + def load_template(self, template_name, template_dirs=None): + source, display_name = self.load_template_source(template_name, template_dirs) + origin = make_origin(display_name, self.load_template_source, template_name, template_dirs) + try: + template = get_template_from_string(source, origin, template_name) + return template, None + except TemplateDoesNotExist: + # If compiling the template we found raises TemplateDoesNotExist, back off to + # returning the source and display name for the template we were asked to load. + # This allows for correct identification (later) of the actual template that does + # not exist. + return source, display_name + + def load_template_source(self, template_name, template_dirs=None): + """ + Returns a tuple containing the source and origin for the given template + name. + + """ + raise NotImplementedError + + def reset(self): + """ + Resets any state maintained by the loader instance (e.g., cached + templates or cached loader modules). + + """ + pass + +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 and display_name: + return LoaderOrigin(display_name, loader, name, dirs) + else: + return None + +def find_template_loader(loader): + if isinstance(loader, (tuple, list)): + loader, args = loader[0], loader[1:] + else: + args = [] + if isinstance(loader, six.string_types): + TemplateLoader = import_by_path(loader) + + if hasattr(TemplateLoader, 'load_template_source'): + func = TemplateLoader(*args) + else: + # Try loading module the old way - string is full path to callable + if args: + raise ImproperlyConfigured("Error importing template source loader %s - can't pass arguments to function-based loader." % loader) + func = TemplateLoader + + 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." % loader) + return None + else: + return func + else: + raise ImproperlyConfigured('Loader does not define a "load_template" callable template source loader') + +def find_template(name, dirs=None): + # Calculate template_source_loaders the first time the function is executed + # because putting this logic in the module-level namespace may cause + # circular import errors. See Django ticket #1292. + global template_source_loaders + if template_source_loaders is None: + loaders = [] + for loader_name in settings.TEMPLATE_LOADERS: + loader = find_template_loader(loader_name) + if loader is not None: + loaders.append(loader) + template_source_loaders = tuple(loaders) + 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. + """ + template, origin = find_template(template_name) + if not hasattr(template, 'render'): + # template needs to be compiled + template = get_template_from_string(template, origin, template_name) + return template + +def get_template_from_string(source, origin=None, name=None): + """ + Returns a compiled Template object for the given template code, + handling template inheritance recursively. + """ + return Template(source, origin, name) + +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 not context_instance: + return t.render(Context(dictionary)) + # Add the dictionary to the context stack, ensuring it gets removed again + # to keep the context_instance in the same state it started in. + context_instance.update(dictionary) + try: + return t.render(context_instance) + finally: + context_instance.pop() + +def select_template(template_name_list): + "Given a list of template names, returns the first that can be loaded." + if not template_name_list: + raise TemplateDoesNotExist("No template names provided") + not_found = [] + for template_name in template_name_list: + try: + return get_template(template_name) + except TemplateDoesNotExist as e: + if e.args[0] not in not_found: + not_found.append(e.args[0]) + continue + # If we get here, none of the templates could be loaded + raise TemplateDoesNotExist(', '.join(not_found)) + +add_to_builtins('django.template.loader_tags') diff --git a/lib/python2.7/site-packages/django/template/loader_tags.py b/lib/python2.7/site-packages/django/template/loader_tags.py new file mode 100644 index 0000000..85ffcf1 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/loader_tags.py @@ -0,0 +1,265 @@ +from collections import defaultdict + +from django.conf import settings +from django.template.base import TemplateSyntaxError, Library, Node, TextNode,\ + token_kwargs, Variable +from django.template.loader import get_template +from django.utils.safestring import mark_safe +from django.utils import six + +register = Library() + +BLOCK_CONTEXT_KEY = 'block_context' + +class ExtendsError(Exception): + pass + +class BlockContext(object): + def __init__(self): + # Dictionary of FIFO queues. + self.blocks = defaultdict(list) + + def add_blocks(self, blocks): + for name, block in six.iteritems(blocks): + self.blocks[name].insert(0, block) + + def pop(self, name): + try: + return self.blocks[name].pop() + except IndexError: + return None + + def push(self, name, block): + self.blocks[name].append(block) + + def get_block(self, name): + try: + return self.blocks[name][-1] + except IndexError: + return None + +class BlockNode(Node): + def __init__(self, name, nodelist, parent=None): + self.name, self.nodelist, self.parent = name, nodelist, parent + + def __repr__(self): + return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist) + + def render(self, context): + block_context = context.render_context.get(BLOCK_CONTEXT_KEY) + context.push() + if block_context is None: + context['block'] = self + result = self.nodelist.render(context) + else: + push = block = block_context.pop(self.name) + if block is None: + block = self + # Create new block so we can store context without thread-safety issues. + block = BlockNode(block.name, block.nodelist) + block.context = context + context['block'] = block + result = block.nodelist.render(context) + if push is not None: + block_context.push(self.name, push) + context.pop() + return result + + def super(self): + render_context = self.context.render_context + if (BLOCK_CONTEXT_KEY in render_context and + render_context[BLOCK_CONTEXT_KEY].get_block(self.name) is not None): + return mark_safe(self.render(self.context)) + return '' + +class ExtendsNode(Node): + must_be_first = True + + def __init__(self, nodelist, parent_name, template_dirs=None): + self.nodelist = nodelist + self.parent_name = parent_name + self.template_dirs = template_dirs + self.blocks = dict([(n.name, n) for n in nodelist.get_nodes_by_type(BlockNode)]) + + def __repr__(self): + return '<ExtendsNode: extends %s>' % self.parent_name.token + + def get_parent(self, context): + parent = self.parent_name.resolve(context) + if not parent: + error_msg = "Invalid template name in 'extends' tag: %r." % parent + if self.parent_name.filters or\ + isinstance(self.parent_name.var, Variable): + error_msg += " Got this from the '%s' variable." %\ + self.parent_name.token + raise TemplateSyntaxError(error_msg) + if hasattr(parent, 'render'): + return parent # parent is a Template object + return get_template(parent) + + def render(self, context): + compiled_parent = self.get_parent(context) + + if BLOCK_CONTEXT_KEY not in context.render_context: + context.render_context[BLOCK_CONTEXT_KEY] = BlockContext() + block_context = context.render_context[BLOCK_CONTEXT_KEY] + + # Add the block nodes from this node to the block context + block_context.add_blocks(self.blocks) + + # If this block's parent doesn't have an extends node it is the root, + # and its block nodes also need to be added to the block context. + for node in compiled_parent.nodelist: + # The ExtendsNode has to be the first non-text node. + if not isinstance(node, TextNode): + if not isinstance(node, ExtendsNode): + blocks = dict([(n.name, n) for n in + compiled_parent.nodelist.get_nodes_by_type(BlockNode)]) + block_context.add_blocks(blocks) + break + + # Call Template._render explicitly so the parser context stays + # the same. + return compiled_parent._render(context) + +class BaseIncludeNode(Node): + def __init__(self, *args, **kwargs): + self.extra_context = kwargs.pop('extra_context', {}) + self.isolated_context = kwargs.pop('isolated_context', False) + super(BaseIncludeNode, self).__init__(*args, **kwargs) + + def render_template(self, template, context): + values = dict([(name, var.resolve(context)) for name, var + in six.iteritems(self.extra_context)]) + if self.isolated_context: + return template.render(context.new(values)) + context.update(values) + output = template.render(context) + context.pop() + return output + +class ConstantIncludeNode(BaseIncludeNode): + def __init__(self, template_path, *args, **kwargs): + super(ConstantIncludeNode, self).__init__(*args, **kwargs) + try: + t = get_template(template_path) + self.template = t + except: + if settings.TEMPLATE_DEBUG: + raise + self.template = None + + def render(self, context): + if not self.template: + return '' + return self.render_template(self.template, context) + +class IncludeNode(BaseIncludeNode): + def __init__(self, template_name, *args, **kwargs): + super(IncludeNode, self).__init__(*args, **kwargs) + self.template_name = template_name + + def render(self, context): + try: + template_name = self.template_name.resolve(context) + template = get_template(template_name) + return self.render_template(template, context) + except: + if settings.TEMPLATE_DEBUG: + raise + return '' + +@register.tag('block') +def do_block(parser, token): + """ + Define a block that can be overridden by child templates. + """ + # token.split_contents() isn't useful here because this tag doesn't accept variable as arguments + 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',)) + + # This check is kept for backwards-compatibility. See #3100. + endblock = parser.next_token() + acceptable_endblocks = ('endblock', 'endblock %s' % block_name) + if endblock.contents not in acceptable_endblocks: + parser.invalid_block_tag(endblock, 'endblock', acceptable_endblocks) + + return BlockNode(block_name, nodelist) + +@register.tag('extends') +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 either the + name of the parent template to extend (if it evaluates to a string) or as + the parent template itself (if it evaluates to a Template object). + """ + bits = token.split_contents() + if len(bits) != 2: + raise TemplateSyntaxError("'%s' takes one argument" % bits[0]) + parent_name = 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) + +@register.tag('include') +def do_include(parser, token): + """ + Loads a template and renders it with the current context. You can pass + additional context using keyword arguments. + + Example:: + + {% include "foo/some_include" %} + {% include "foo/some_include" with bar="BAZZ!" baz="BING!" %} + + Use the ``only`` argument to exclude the current context when rendering + the included template:: + + {% include "foo/some_include" only %} + {% include "foo/some_include" with bar="1" only %} + """ + bits = token.split_contents() + if len(bits) < 2: + raise TemplateSyntaxError("%r tag takes at least one argument: the name of the template to be included." % bits[0]) + options = {} + remaining_bits = bits[2:] + while remaining_bits: + option = remaining_bits.pop(0) + if option in options: + raise TemplateSyntaxError('The %r option was specified more ' + 'than once.' % option) + if option == 'with': + value = token_kwargs(remaining_bits, parser, support_legacy=False) + if not value: + raise TemplateSyntaxError('"with" in %r tag needs at least ' + 'one keyword argument.' % bits[0]) + elif option == 'only': + value = True + else: + raise TemplateSyntaxError('Unknown argument for %r tag: %r.' % + (bits[0], option)) + options[option] = value + isolated_context = options.get('only', False) + namemap = options.get('with', {}) + path = bits[1] + if path[0] in ('"', "'") and path[-1] == path[0]: + return ConstantIncludeNode(path[1:-1], extra_context=namemap, + isolated_context=isolated_context) + return IncludeNode(parser.compile_filter(bits[1]), extra_context=namemap, + isolated_context=isolated_context) diff --git a/lib/python2.7/site-packages/django/template/loaders/__init__.py b/lib/python2.7/site-packages/django/template/loaders/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/loaders/__init__.py diff --git a/lib/python2.7/site-packages/django/template/loaders/app_directories.py b/lib/python2.7/site-packages/django/template/loaders/app_directories.py new file mode 100644 index 0000000..c82817a --- /dev/null +++ b/lib/python2.7/site-packages/django/template/loaders/app_directories.py @@ -0,0 +1,63 @@ +""" +Wrapper for loading templates from "templates" directories in INSTALLED_APPS +packages. +""" + +import os +import sys + +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.template.base import TemplateDoesNotExist +from django.template.loader import BaseLoader +from django.utils._os import safe_join +from django.utils.importlib import import_module +from django.utils import six + +# At compile time, cache the directories to search. +if six.PY2: + fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() +app_template_dirs = [] +for app in settings.INSTALLED_APPS: + try: + mod = import_module(app) + except ImportError as 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): + if six.PY2: + template_dir = template_dir.decode(fs_encoding) + 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) + +class Loader(BaseLoader): + is_usable = True + + def get_template_sources(self, template_name, template_dirs=None): + """ + Returns the absolute paths to "template_name", when appended to each + directory in "template_dirs". Any paths that don't lie inside one of the + template dirs are excluded from the result set, for security reasons. + """ + if not template_dirs: + template_dirs = app_template_dirs + for template_dir in template_dirs: + try: + yield safe_join(template_dir, template_name) + except UnicodeDecodeError: + # The template dir name was a bytestring that wasn't valid UTF-8. + raise + except ValueError: + # The joined path was located outside of template_dir. + pass + + def load_template_source(self, template_name, template_dirs=None): + for filepath in self.get_template_sources(template_name, template_dirs): + try: + with open(filepath, 'rb') as fp: + return (fp.read().decode(settings.FILE_CHARSET), filepath) + except IOError: + pass + raise TemplateDoesNotExist(template_name) diff --git a/lib/python2.7/site-packages/django/template/loaders/cached.py b/lib/python2.7/site-packages/django/template/loaders/cached.py new file mode 100644 index 0000000..c61045d --- /dev/null +++ b/lib/python2.7/site-packages/django/template/loaders/cached.py @@ -0,0 +1,64 @@ +""" +Wrapper class that takes a list of template loaders as an argument and attempts +to load templates from them in order, caching the result. +""" + +import hashlib +from django.template.base import TemplateDoesNotExist +from django.template.loader import BaseLoader, get_template_from_string, find_template_loader, make_origin +from django.utils.encoding import force_bytes + +class Loader(BaseLoader): + is_usable = True + + def __init__(self, loaders): + self.template_cache = {} + self._loaders = loaders + self._cached_loaders = [] + + @property + def loaders(self): + # Resolve loaders on demand to avoid circular imports + if not self._cached_loaders: + # Set self._cached_loaders atomically. Otherwise, another thread + # could see an incomplete list. See #17303. + cached_loaders = [] + for loader in self._loaders: + cached_loaders.append(find_template_loader(loader)) + self._cached_loaders = cached_loaders + return self._cached_loaders + + def find_template(self, name, dirs=None): + for loader in self.loaders: + try: + template, display_name = loader(name, dirs) + return (template, make_origin(display_name, loader, name, dirs)) + except TemplateDoesNotExist: + pass + raise TemplateDoesNotExist(name) + + def load_template(self, template_name, template_dirs=None): + key = template_name + if template_dirs: + # If template directories were specified, use a hash to differentiate + key = '-'.join([template_name, hashlib.sha1(force_bytes('|'.join(template_dirs))).hexdigest()]) + + try: + template = self.template_cache[key] + except KeyError: + template, origin = self.find_template(template_name, template_dirs) + if not hasattr(template, 'render'): + try: + template = get_template_from_string(template, origin, template_name) + except TemplateDoesNotExist: + # If compiling the template we found raises TemplateDoesNotExist, + # back off to returning the source and display name for the template + # we were asked to load. This allows for correct identification (later) + # of the actual template that does not exist. + return template, origin + self.template_cache[key] = template + return template, None + + def reset(self): + "Empty the template cache." + self.template_cache.clear() diff --git a/lib/python2.7/site-packages/django/template/loaders/eggs.py b/lib/python2.7/site-packages/django/template/loaders/eggs.py new file mode 100644 index 0000000..7da1803 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/loaders/eggs.py @@ -0,0 +1,33 @@ +# Wrapper for loading templates from eggs via pkg_resources.resource_string. +from __future__ import unicode_literals + +try: + from pkg_resources import resource_string +except ImportError: + resource_string = None + +from django.conf import settings +from django.template.base import TemplateDoesNotExist +from django.template.loader import BaseLoader +from django.utils import six + +class Loader(BaseLoader): + is_usable = resource_string is not None + + def load_template_source(self, 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 + for app in settings.INSTALLED_APPS: + try: + resource = resource_string(app, pkg_name) + except Exception: + continue + if six.PY2: + resource = resource.decode(settings.FILE_CHARSET) + return (resource, 'egg:%s:%s' % (app, pkg_name)) + raise TemplateDoesNotExist(template_name) diff --git a/lib/python2.7/site-packages/django/template/loaders/filesystem.py b/lib/python2.7/site-packages/django/template/loaders/filesystem.py new file mode 100644 index 0000000..1a7f0d2 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/loaders/filesystem.py @@ -0,0 +1,46 @@ +""" +Wrapper for loading templates from the filesystem. +""" + +from django.conf import settings +from django.template.base import TemplateDoesNotExist +from django.template.loader import BaseLoader +from django.utils._os import safe_join + +class Loader(BaseLoader): + is_usable = True + + def get_template_sources(self, template_name, template_dirs=None): + """ + Returns the absolute paths to "template_name", when appended to each + directory in "template_dirs". Any paths that don't lie inside one of the + template dirs are excluded from the result set, for security reasons. + """ + if not template_dirs: + template_dirs = settings.TEMPLATE_DIRS + for template_dir in template_dirs: + try: + yield safe_join(template_dir, template_name) + except UnicodeDecodeError: + # The template dir name was a bytestring that wasn't valid UTF-8. + raise + except ValueError: + # The joined path was located outside of this particular + # template_dir (it might be inside another one, so this isn't + # fatal). + pass + + def load_template_source(self, template_name, template_dirs=None): + tried = [] + for filepath in self.get_template_sources(template_name, template_dirs): + try: + with open(filepath, 'rb') as fp: + return (fp.read().decode(settings.FILE_CHARSET), filepath) + except IOError: + tried.append(filepath) + if tried: + 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 diff --git a/lib/python2.7/site-packages/django/template/response.py b/lib/python2.7/site-packages/django/template/response.py new file mode 100644 index 0000000..3b3b413 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/response.py @@ -0,0 +1,159 @@ +from django.http import HttpResponse +from django.template import loader, Context, RequestContext +from django.utils import six + + +class ContentNotRenderedError(Exception): + pass + + +class SimpleTemplateResponse(HttpResponse): + rendering_attrs = ['template_name', 'context_data', '_post_render_callbacks'] + + def __init__(self, template, context=None, content_type=None, status=None, + mimetype=None): + # It would seem obvious to call these next two members 'template' and + # 'context', but those names are reserved as part of the test Client + # API. To avoid the name collision, we use tricky-to-debug problems + self.template_name = template + self.context_data = context + + self._post_render_callbacks = [] + + # content argument doesn't make sense here because it will be replaced + # with rendered template so we always pass empty string in order to + # prevent errors and provide shorter signature. + super(SimpleTemplateResponse, self).__init__('', content_type, status, + mimetype) + + # _is_rendered tracks whether the template and context has been baked + # into a final response. + # Super __init__ doesn't know any better than to set self.content to + # the empty string we just gave it, which wrongly sets _is_rendered + # True, so we initialize it to False after the call to super __init__. + self._is_rendered = False + + def __getstate__(self): + """Pickling support function. + + Ensures that the object can't be pickled before it has been + rendered, and that the pickled state only includes rendered + data, not the data used to construct the response. + """ + obj_dict = super(SimpleTemplateResponse, self).__getstate__() + if not self._is_rendered: + raise ContentNotRenderedError('The response content must be ' + 'rendered before it can be pickled.') + for attr in self.rendering_attrs: + if attr in obj_dict: + del obj_dict[attr] + + return obj_dict + + def resolve_template(self, template): + "Accepts a template object, path-to-template or list of paths" + if isinstance(template, (list, tuple)): + return loader.select_template(template) + elif isinstance(template, six.string_types): + return loader.get_template(template) + else: + return template + + def resolve_context(self, context): + """Converts context data into a full Context object + (assuming it isn't already a Context object). + """ + if isinstance(context, Context): + return context + else: + return Context(context) + + @property + def rendered_content(self): + """Returns the freshly rendered content for the template and context + described by the TemplateResponse. + + This *does not* set the final content of the response. To set the + response content, you must either call render(), or set the + content explicitly using the value of this property. + """ + template = self.resolve_template(self.template_name) + context = self.resolve_context(self.context_data) + content = template.render(context) + return content + + def add_post_render_callback(self, callback): + """Adds a new post-rendering callback. + + If the response has already been rendered, + invoke the callback immediately. + """ + if self._is_rendered: + callback(self) + else: + self._post_render_callbacks.append(callback) + + def render(self): + """Renders (thereby finalizing) the content of the response. + + If the content has already been rendered, this is a no-op. + + Returns the baked response instance. + """ + retval = self + if not self._is_rendered: + self.content = self.rendered_content + for post_callback in self._post_render_callbacks: + newretval = post_callback(retval) + if newretval is not None: + retval = newretval + return retval + + @property + def is_rendered(self): + return self._is_rendered + + def __iter__(self): + if not self._is_rendered: + raise ContentNotRenderedError('The response content must be ' + 'rendered before it can be iterated over.') + return super(SimpleTemplateResponse, self).__iter__() + + @property + def content(self): + if not self._is_rendered: + raise ContentNotRenderedError('The response content must be ' + 'rendered before it can be accessed.') + return super(SimpleTemplateResponse, self).content + + @content.setter + def content(self, value): + """Sets the content for the response + """ + HttpResponse.content.fset(self, value) + self._is_rendered = True + + +class TemplateResponse(SimpleTemplateResponse): + rendering_attrs = SimpleTemplateResponse.rendering_attrs + \ + ['_request', '_current_app'] + + def __init__(self, request, template, context=None, content_type=None, + status=None, mimetype=None, current_app=None): + # self.request gets over-written by django.test.client.Client - and + # unlike context_data and template_name the _request should not + # be considered part of the public API. + self._request = request + # As a convenience we'll allow callers to provide current_app without + # having to avoid needing to create the RequestContext directly + self._current_app = current_app + super(TemplateResponse, self).__init__( + template, context, content_type, status, mimetype) + + def resolve_context(self, context): + """Convert context data into a full RequestContext object + (assuming it isn't already a Context object). + """ + if isinstance(context, Context): + return context + return RequestContext(self._request, context, current_app=self._current_app) diff --git a/lib/python2.7/site-packages/django/template/smartif.py b/lib/python2.7/site-packages/django/template/smartif.py new file mode 100644 index 0000000..e2ca395 --- /dev/null +++ b/lib/python2.7/site-packages/django/template/smartif.py @@ -0,0 +1,205 @@ +""" +Parser and utilities for the smart 'if' tag +""" + +# Using a simple top down parser, as described here: +# http://effbot.org/zone/simple-top-down-parsing.htm. +# 'led' = left denotation +# 'nud' = null denotation +# 'bp' = binding power (left = lbp, right = rbp) + +class TokenBase(object): + """ + Base class for operators and literals, mainly for debugging and for throwing + syntax errors. + """ + id = None # node/token type name + value = None # used by literals + first = second = None # used by tree nodes + + def nud(self, parser): + # Null denotation - called in prefix context + raise parser.error_class( + "Not expecting '%s' in this position in if tag." % self.id + ) + + def led(self, left, parser): + # Left denotation - called in infix context + raise parser.error_class( + "Not expecting '%s' as infix operator in if tag." % self.id + ) + + def display(self): + """ + Returns what to display in error messages for this node + """ + return self.id + + def __repr__(self): + out = [str(x) for x in [self.id, self.first, self.second] if x is not None] + return "(" + " ".join(out) + ")" + + +def infix(bp, func): + """ + Creates an infix operator, given a binding power and a function that + evaluates the node + """ + class Operator(TokenBase): + lbp = bp + + def led(self, left, parser): + self.first = left + self.second = parser.expression(bp) + return self + + def eval(self, context): + try: + return func(context, self.first, self.second) + except Exception: + # Templates shouldn't throw exceptions when rendering. We are + # most likely to get exceptions for things like {% if foo in bar + # %} where 'bar' does not support 'in', so default to False + return False + + return Operator + + +def prefix(bp, func): + """ + Creates a prefix operator, given a binding power and a function that + evaluates the node. + """ + class Operator(TokenBase): + lbp = bp + + def nud(self, parser): + self.first = parser.expression(bp) + self.second = None + return self + + def eval(self, context): + try: + return func(context, self.first) + except Exception: + return False + + return Operator + + +# Operator precedence follows Python. +# NB - we can get slightly more accurate syntax error messages by not using the +# same object for '==' and '='. +# We defer variable evaluation to the lambda to ensure that terms are +# lazily evaluated using Python's boolean parsing logic. +OPERATORS = { + 'or': infix(6, lambda context, x, y: x.eval(context) or y.eval(context)), + 'and': infix(7, lambda context, x, y: x.eval(context) and y.eval(context)), + 'not': prefix(8, lambda context, x: not x.eval(context)), + 'in': infix(9, lambda context, x, y: x.eval(context) in y.eval(context)), + 'not in': infix(9, lambda context, x, y: x.eval(context) not in y.eval(context)), + '=': infix(10, lambda context, x, y: x.eval(context) == y.eval(context)), + '==': infix(10, lambda context, x, y: x.eval(context) == y.eval(context)), + '!=': infix(10, lambda context, x, y: x.eval(context) != y.eval(context)), + '>': infix(10, lambda context, x, y: x.eval(context) > y.eval(context)), + '>=': infix(10, lambda context, x, y: x.eval(context) >= y.eval(context)), + '<': infix(10, lambda context, x, y: x.eval(context) < y.eval(context)), + '<=': infix(10, lambda context, x, y: x.eval(context) <= y.eval(context)), +} + +# Assign 'id' to each: +for key, op in OPERATORS.items(): + op.id = key + + +class Literal(TokenBase): + """ + A basic self-resolvable object similar to a Django template variable. + """ + # IfParser uses Literal in create_var, but TemplateIfParser overrides + # create_var so that a proper implementation that actually resolves + # variables, filters etc is used. + id = "literal" + lbp = 0 + + def __init__(self, value): + self.value = value + + def display(self): + return repr(self.value) + + def nud(self, parser): + return self + + def eval(self, context): + return self.value + + def __repr__(self): + return "(%s %r)" % (self.id, self.value) + + +class EndToken(TokenBase): + lbp = 0 + + def nud(self, parser): + raise parser.error_class("Unexpected end of expression in if tag.") + +EndToken = EndToken() + + +class IfParser(object): + error_class = ValueError + + def __init__(self, tokens): + # pre-pass necessary to turn 'not','in' into single token + l = len(tokens) + mapped_tokens = [] + i = 0 + while i < l: + token = tokens[i] + if token == "not" and i + 1 < l and tokens[i+1] == "in": + token = "not in" + i += 1 # skip 'in' + mapped_tokens.append(self.translate_token(token)) + i += 1 + + self.tokens = mapped_tokens + self.pos = 0 + self.current_token = self.next_token() + + def translate_token(self, token): + try: + op = OPERATORS[token] + except (KeyError, TypeError): + return self.create_var(token) + else: + return op() + + def next_token(self): + if self.pos >= len(self.tokens): + return EndToken + else: + retval = self.tokens[self.pos] + self.pos += 1 + return retval + + def parse(self): + retval = self.expression() + # Check that we have exhausted all the tokens + if self.current_token is not EndToken: + raise self.error_class("Unused '%s' at end of if expression." % + self.current_token.display()) + return retval + + def expression(self, rbp=0): + t = self.current_token + self.current_token = self.next_token() + left = t.nud(self) + while rbp < self.current_token.lbp: + t = self.current_token + self.current_token = self.next_token() + left = t.led(left, self) + return left + + def create_var(self, value): + return Literal(value) |