Ticket #3544: 3544.contexttemplatecache.3.diff

File 3544.contexttemplatecache.3.diff, 19.4 KB (added by Johannes Dollinger, 16 years ago)
  • django/template/__init__.py

     
    196196            ({TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block', TOKEN_COMMENT: 'Comment'}[self.token_type],
    197197            self.contents[:20].replace('\n', ''))
    198198
    199     def split_contents(self):
    200         return list(smart_split(self.contents))
     199    def split_contents(self, separator_chars=None):
     200        return list(smart_split(self.contents, separator_chars=separator_chars))
    201201
    202202class Lexer(object):
    203203    def __init__(self, template_string, origin):
     
    898898                    dict = func(*args)
    899899
    900900                    if not getattr(self, 'nodelist', False):
    901                         from django.template.loader import get_template, select_template
    902901                        if not isinstance(file_name, basestring) and is_iterable(file_name):
    903                             t = select_template(file_name)
     902                            t = context.select_template(file_name)
    904903                        else:
    905                             t = get_template(file_name)
     904                            t = context.get_template(file_name)
    906905                        self.nodelist = t.nodelist
    907906                    return self.nodelist.render(context_class(dict,
    908907                            autoescape=context.autoescape))
     
    913912            return func
    914913        return dec
    915914
     915def parse_context_map(bits, separators=('and', ',')):
     916    if len(bits) % 4 != 3:
     917        raise ValueError
     918    context_map = {}
     919    bits = [separators[0]] + bits
     920    for i in range(len(bits) / 4):
     921        separator, value, as_, name = bits[4*i:4*(i+1)]
     922        if not separator in separators or as_ != 'as':
     923            raise ValueError
     924        context_map[name] = Variable(value)
     925    return context_map
     926
     927def parse_string_literal(bit, allow_empty=True):   
     928    if len(bit) < 2 or not allow_empty and len(bit) < 3:
     929        raise ValueError
     930    q = bit[0]
     931    if not q in ('"',"'") or bit[-1] != q:
     932        raise ValueError
     933    return bit[1:-1]
     934
     935def render_with_context_map(renderable, context_map, context):
     936    """
     937    renderable: a NodeList or Template
     938    context_map: a dict mapping strings to Variables
     939    context: a context object
     940   
     941    """
     942    if context_map is None:
     943        return renderable.render(context)
     944    dict = {}
     945    for name in context_map:
     946        dict[name] = context_map[name].resolve(context)
     947    context.update(dict)
     948    output = renderable.render(context)
     949    context.pop()
     950    return output
     951
    916952def get_library(module_name):
    917953    lib = libraries.get(module_name, None)
    918954    if not lib:
  • django/template/defaulttags.py

     
    1111from django.template import Node, NodeList, Template, Context, Variable
    1212from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END
    1313from django.template import get_library, Library, InvalidTemplateLibrary
     14from django.template import render_with_context_map, parse_context_map
    1415from django.conf import settings
    1516from django.utils.encoding import smart_str, smart_unicode
    1617from django.utils.itercompat import groupby
     
    390391        return str(int(round(ratio)))
    391392
    392393class WithNode(Node):
    393     def __init__(self, var, name, nodelist):
    394         self.var = var
    395         self.name = name
     394    def __init__(self, context_map, nodelist):
     395        self.context_map = context_map
    396396        self.nodelist = nodelist
    397397
    398398    def __repr__(self):
    399399        return "<WithNode>"
    400400
    401401    def render(self, context):
    402         val = self.var.resolve(context)
    403         context.push()
    404         context[self.name] = val
    405         output = self.nodelist.render(context)
    406         context.pop()
    407         return output
     402        return render_with_context_map(self.nodelist, self.context_map, context)
    408403
    409404#@register.tag
    410405def autoescape(parser, token):
     
    10821077#@register.tag
    10831078def do_with(parser, token):
    10841079    """
    1085     Adds a value to the context (inside of this block) for caching and easy
     1080    Adds a values to the context (inside of this block) for caching and easy
    10861081    access.
    10871082
    10881083    For example::
     
    10901085        {% with person.some_sql_method as total %}
    10911086            {{ total }} object{{ total|pluralize }}
    10921087        {% endwith %}
     1088       
     1089        {% with person.some_sql_method as total, person.get_full_name as full_name %}
     1090            {{ full_name }}: {{ total }} object{{ total|pluralize }}
     1091        {% endwith %}
     1092       
    10931093    """
    1094     bits = list(token.split_contents())
    1095     if len(bits) != 4 or bits[2] != "as":
    1096         raise TemplateSyntaxError("%r expected format is 'value as name'" %
     1094    bits = list(token.split_contents(separator_chars=','))
     1095    try:
     1096        context_map = parse_context_map(bits[1:])
     1097    except ValueError:
     1098        raise TemplateSyntaxError("%r expected format is 'value as name (and value as name)*'" %
    10971099                                  bits[0])
    1098     var = parser.compile_filter(bits[1])
    1099     name = bits[3]
    11001100    nodelist = parser.parse(('endwith',))
    11011101    parser.delete_first_token()
    1102     return WithNode(var, name, nodelist)
     1102    return WithNode(context_map, nodelist)
    11031103do_with = register.tag('with', do_with)
  • django/template/context.py

     
    99
    1010class Context(object):
    1111    "A stack container for variable context"
    12     def __init__(self, dict_=None, autoescape=True):
     12    def __init__(self, dict_=None, autoescape=True, loader=None):
    1313        dict_ = dict_ or {}
    1414        self.dicts = [dict_]
    1515        self.autoescape = autoescape
     16        self.template_cache = {}
     17        self.loader = loader
    1618
    1719    def __repr__(self):
    1820        return repr(self.dicts)
     
    6567        self.dicts = [other_dict] + self.dicts
    6668        return other_dict
    6769
     70    def get_template(self, template_name):
     71        if not template_name in self.template_cache:
     72            if self.loader is None:
     73                from django.template import loader
     74                tpl = loader.get_template(template_name)
     75            else:
     76                tpl = self.loader.get_template(template_name)
     77            self.template_cache[template_name] = tpl
     78        return self.template_cache[template_name]
     79
     80    def select_template(self, template_name_list):
     81        from django.template import TemplateDoesNotExist
     82        for template_name in template_name_list:
     83            try:
     84                return self.get_template(template_name)
     85            except TemplateDoesNotExist:
     86                continue
     87        raise TemplateDoesNotExist, ', '.join(template_name_list)
     88
    6889# This is a function rather than module-level procedural code because we only
    6990# want it to execute if somebody uses RequestContext.
    7091def get_standard_processors():
     
    93114    Additional processors can be specified as a list of callables
    94115    using the "processors" keyword argument.
    95116    """
    96     def __init__(self, request, dict=None, processors=None):
    97         Context.__init__(self, dict)
     117    def __init__(self, request, dict=None, processors=None, loader=None):
     118        Context.__init__(self, dict, loader=loader)
    98119        if processors is None:
    99120            processors = ()
    100121        else:
  • django/template/loader_tags.py

     
    11from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable
    22from django.template import Library, Node, TextNode
     3from django.template import render_with_context_map, parse_context_map, parse_string_literal
    34from django.template.loader import get_template, get_template_from_string, find_template_source
    45from django.conf import settings
    56from django.utils.safestring import mark_safe
     
    3940class ExtendsNode(Node):
    4041    must_be_first = True
    4142
    42     def __init__(self, nodelist, parent_name, parent_name_expr, template_dirs=None):
     43    def __init__(self, nodelist, parent_name, parent_name_expr):
    4344        self.nodelist = nodelist
    4445        self.parent_name, self.parent_name_expr = parent_name, parent_name_expr
    45         self.template_dirs = template_dirs
    4646
    4747    def __repr__(self):
    4848        if self.parent_name_expr:
     
    6161        if hasattr(parent, 'render'):
    6262            return parent # parent is a Template object
    6363        try:
    64             source, origin = find_template_source(parent, self.template_dirs)
     64            return context.get_template(parent)
    6565        except TemplateDoesNotExist:
    6666            raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
    67         else:
    68             return get_template_from_string(source, origin, parent)
    6967
    7068    def render(self, context):
    7169        compiled_parent = self.get_parent(context)
     
    9694                parent_block.nodelist = block_node.nodelist
    9795        return compiled_parent.render(context)
    9896
    99 class ConstantIncludeNode(Node):
    100     def __init__(self, template_path):
    101         try:
    102             t = get_template(template_path)
    103             self.template = t
    104         except:
    105             if settings.TEMPLATE_DEBUG:
    106                 raise
    107             self.template = None
    108 
    109     def render(self, context):
    110         if self.template:
    111             return self.template.render(context)
    112         else:
    113             return ''
    114 
    11597class IncludeNode(Node):
    116     def __init__(self, template_name):
     98    def __init__(self, template_name, context_map=None):
    11799        self.template_name = Variable(template_name)
     100        self.context_map = context_map
    118101
    119102    def render(self, context):
    120103        try:
    121104            template_name = self.template_name.resolve(context)
    122             t = get_template(template_name)
    123             return t.render(context)
     105            tpl = context.get_template(template_name)           
     106            return render_with_context_map(tpl, self.context_map, context)
    124107        except TemplateSyntaxError, e:
    125108            if settings.TEMPLATE_DEBUG:
    126109                raise
     
    162145    if len(bits) != 2:
    163146        raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
    164147    parent_name, parent_name_expr = None, None
    165     if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
    166         parent_name = bits[1][1:-1]
    167     else:
     148    try:
     149        parent_name = parse_string_literal(bits[1])
     150    except ValueError:
    168151        parent_name_expr = parser.compile_filter(bits[1])
    169152    nodelist = parser.parse()
    170153    if nodelist.get_nodes_by_type(ExtendsNode):
     
    174157def do_include(parser, token):
    175158    """
    176159    Loads a template and renders it with the current context.
     160    Optionally takes a "with value as name (, value as name)*" clause.
    177161
    178162    Example::
    179163
    180164        {% include "foo/some_include" %}
     165        {% include "foo" with value as name %}
     166        {% include "foo" with value as name, bar as baz %}
    181167    """
    182     bits = token.contents.split()
    183     if len(bits) != 2:
    184         raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
    185     path = bits[1]
    186     if path[0] in ('"', "'") and path[-1] == path[0]:
    187         return ConstantIncludeNode(path[1:-1])
    188     return IncludeNode(bits[1])
     168    bits = token.split_contents(separator_chars=',')
     169    syntax_error = False
     170    if len(bits) == 2:
     171        return IncludeNode(bits[1])
     172    if len(bits) > 2 and bits[2] == 'with':
     173        try:
     174            context_map = parse_context_map(bits[3:])           
     175            return IncludeNode(bits[1], context_map)
     176        except ValueError:
     177            raise ValueError, bits[3:]
     178    raise TemplateSyntaxError, "%r tag takes the name of the template to be included and an optional 'with value as name' clause." % bits[0]   
     179   
    189180
    190181register.tag('block', do_block)
    191182register.tag('extends', do_extends)
  • django/utils/text.py

     
    11import re
    22from django.conf import settings
    33from django.utils.encoding import force_unicode
    4 from django.utils.functional import allow_lazy
     4from django.utils.functional import allow_lazy, memoize
    55from django.utils.translation import ugettext_lazy
    66
    77# Capitalizes the first letter of a string.
     
    196196    return str(ustring_re.sub(fix, s))
    197197javascript_quote = allow_lazy(javascript_quote, unicode)
    198198
    199 smart_split_re = re.compile('("(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|[^\\s]+)')
    200 def smart_split(text):
     199quoted_string_pattern = '"(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\''
     200def get_smart_split_regexp(chars):
     201    if chars is None:
     202        return re.compile('(%s|[^\\s]+)' % quoted_string_pattern)
     203    return re.compile('(%s|[^\\s%s]+|[%s])' % (quoted_string_pattern, chars, chars))
     204get_smart_split_regexp = memoize(get_smart_split_regexp, {}, 1)
     205
     206def smart_split(text, separator_chars=None):
    201207    r"""
    202208    Generator that splits a string by spaces, leaving quoted phrases together.
    203209    Supports both single and double quotes, and supports escaping quotes with
    204210    backslashes. In the output, strings will keep their initial and trailing
    205211    quote marks.
    206 
     212    A `separator_chars` string can be given to additionally split by each
     213    contained character. Matched characters will be included in the output.
     214   
    207215    >>> list(smart_split(r'This is "a person\'s" test.'))
    208216    [u'This', u'is', u'"a person\\\'s"', u'test.']
    209217        >>> list(smart_split(r"Another 'person\'s' test."))
    210218        [u'Another', u"'person's'", u'test.']
    211219        >>> list(smart_split(r'A "\"funky\" style" test.'))
    212220        [u'A', u'""funky" style"', u'test.']
     221    >>> list(smart_split('a, b,c ,d, e ,f', separator_chars=','))
     222    [u'a', u',', u'b', u',', u'c', u',', u'd', u',', u'e', u',', u'f']
     223    >>> list(smart_split('a=1, b=2 , c = 3', separator_chars=',='))
     224    [u'a', u'=', u'1', u',', u'b', u'=', u'2', u',', u'c', u'=', u'3']         
    213225    """
    214226    text = force_unicode(text)
    215     for bit in smart_split_re.finditer(text):
     227    regexp = get_smart_split_regexp(separator_chars)
     228    for bit in regexp.finditer(text):
    216229        bit = bit.group(0)
    217230        if bit[0] == '"' and bit[-1] == '"':
    218231            yield '"' + bit[1:-1].replace('\\"', '"').replace('\\\\', '\\') + '"'
  • tests/regressiontests/templates/tests.py

     
    611611            'include02': ('{% include "basic-syntax02" %}', {'headline': 'Included'}, "Included"),
    612612            'include03': ('{% include template_name %}', {'template_name': 'basic-syntax02', 'headline': 'Included'}, "Included"),
    613613            'include04': ('a{% include "nonexistent" %}b', {}, "ab"),
     614            'include-with01': ('{% include "basic-syntax02" with foo as headline %}', {'foo': 'Included'}, "Included"),
     615            'include-with02': ('{% include "basic-syntax03" with foo as first, bar as second %}', {"foo" : 1, "bar" : 2}, "1 --- 2"),
     616            'recursive-include': ('{% for item in items %}{{ item.label }}{% if not item.children|length_is:0 %}{% with item.children as items %}({% include "recursive-include" %}){% endwith %}{% endif %}{% endfor %}', {
     617                'items': [
     618                    {'label': 1, 'children': [
     619                        {'label': 2, 'children': [
     620                            {'label': 3, 'children': []},
     621                            {'label': 4, 'children': []},
     622                        ]},
     623                        {'label': 5, 'children': [
     624                             {'label': 6, 'children': [
     625                                 {'label': 7, 'children': [
     626                                     {'label': 8, 'children': []},     
     627                                 ]},
     628                                {'label': 9, 'children': []},   
     629                             ]},
     630                        ]},
     631                    ]},
     632                ],
     633            }, '1(2(34)5(6(7(8)9)))'),
    614634
    615635            ### NAMED ENDBLOCKS #######################################################
    616636
     
    854874            ### WITH TAG ########################################################
    855875            'with01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, '50'),
    856876            'with02': ('{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key':50}}, ('50-50-50', 'INVALID50-50-50INVALID')),
     877            'with03': ('{% with a as b and b as a %}{{ a }}{{ b }}{% endwith %}', {'a': 'A', 'b': 'B'}, 'BA'),
     878            'with04': ('{% with a as b, b as a %}{{ a }}{{ b }}{% endwith %}', {'a': 'A', 'b': 'B'}, 'BA'),
     879            'with05': ('{% with a as b, b as a, "," as s %}{{ a }}{{ s }}{{ b }}{% endwith %}', {'a': 'A', 'b': 'B'}, 'B,A'),
     880            'with06': ('{% with a as b and \',\' as s, b as a %}{{ a }}{{ s }}{{ b }}{% endwith %}', {'a': 'A', 'b': 'B'}, 'B,A'),
    857881
    858882            'with-error01': ('{% with dict.key xx key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError),
    859883            'with-error02': ('{% with dict.key as %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError),
    860 
     884            'with-error03': ('{% with a as x, as y %}x{% endwith %}', {'a': 'A', 'b': 'B'}, template.TemplateSyntaxError),
     885            'with-error04': ('{% with a as x, b as %}x{% endwith %}', {'a': 'A', 'b': 'B'}, template.TemplateSyntaxError),
     886            'with-error05': ('{% with as x, b as y %}x{% endwith %}', {'a': 'A', 'b': 'B'}, template.TemplateSyntaxError),                       
     887            'with-error06': ('{% with a as x | b as y %}x{% endwith %}', {'a': 'A', 'b': 'B'}, template.TemplateSyntaxError),
     888            'with-error07': ('{% with a as x xxx b as y %}x{% endwith %}', {'a': 'A', 'b': 'B'}, template.TemplateSyntaxError),
     889            'with-error08': ('{% with a xx x, b xx y %}x{% endwith %}', {'a': 'A', 'b': 'B'}, template.TemplateSyntaxError),
     890                       
    861891            ### NOW TAG ########################################################
    862892            # Simple case
    863893            'now01' : ('{% now "j n Y"%}', {}, str(datetime.now().day) + ' ' + str(datetime.now().month) + ' ' + str(datetime.now().year)),
  • tests/regressiontests/text/tests.py

     
    1515[u'"a', u"'one"]
    1616>>> print list(smart_split(r'''all friends' tests'''))[1]
    1717friends'
     18>>> list(smart_split('a, b,c ,d, e ,f', separator_chars=','))
     19[u'a', u',', u'b', u',', u'c', u',', u'd', u',', u'e', u',', u'f']
     20>>> list(smart_split('a=1, b=2 , c = 3', separator_chars=',='))
     21[u'a', u'=', u'1', u',', u'b', u'=', u'2', u',', u'c', u'=', u'3']
    1822
    1923### urlquote #############################################################
    2024>>> from django.utils.http import urlquote, urlquote_plus
Back to Top