Ticket #3544: 3544.contexttemplatecache.3.2.diff
File 3544.contexttemplatecache.3.2.diff, 19.4 KB (added by , 16 years ago) |
---|
-
django/template/__init__.py
196 196 ({TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block', TOKEN_COMMENT: 'Comment'}[self.token_type], 197 197 self.contents[:20].replace('\n', '')) 198 198 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)) 201 201 202 202 class Lexer(object): 203 203 def __init__(self, template_string, origin): … … 898 898 dict = func(*args) 899 899 900 900 if not getattr(self, 'nodelist', False): 901 from django.template.loader import get_template, select_template902 901 if not isinstance(file_name, basestring) and is_iterable(file_name): 903 t = select_template(file_name)902 t = context.select_template(file_name) 904 903 else: 905 t = get_template(file_name)904 t = context.get_template(file_name) 906 905 self.nodelist = t.nodelist 907 906 return self.nodelist.render(context_class(dict, 908 907 autoescape=context.autoescape)) … … 913 912 return func 914 913 return dec 915 914 915 def 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 927 def 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 935 def 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 916 952 def get_library(module_name): 917 953 lib = libraries.get(module_name, None) 918 954 if not lib: -
django/template/defaulttags.py
11 11 from django.template import Node, NodeList, Template, Context, Variable 12 12 from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END 13 13 from django.template import get_library, Library, InvalidTemplateLibrary 14 from django.template import render_with_context_map, parse_context_map 14 15 from django.conf import settings 15 16 from django.utils.encoding import smart_str, smart_unicode 16 17 from django.utils.itercompat import groupby … … 390 391 return str(int(round(ratio))) 391 392 392 393 class 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 396 396 self.nodelist = nodelist 397 397 398 398 def __repr__(self): 399 399 return "<WithNode>" 400 400 401 401 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) 408 403 409 404 #@register.tag 410 405 def autoescape(parser, token): … … 1082 1077 #@register.tag 1083 1078 def do_with(parser, token): 1084 1079 """ 1085 Adds a valueto the context (inside of this block) for caching and easy1080 Adds values to the context (inside of this block) for caching and easy 1086 1081 access. 1087 1082 1088 1083 For example:: … … 1090 1085 {% with person.some_sql_method as total %} 1091 1086 {{ total }} object{{ total|pluralize }} 1092 1087 {% 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 1093 1093 """ 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)*'" % 1097 1099 bits[0]) 1098 var = parser.compile_filter(bits[1])1099 name = bits[3]1100 1100 nodelist = parser.parse(('endwith',)) 1101 1101 parser.delete_first_token() 1102 return WithNode( var, name, nodelist)1102 return WithNode(context_map, nodelist) 1103 1103 do_with = register.tag('with', do_with) -
django/template/context.py
9 9 10 10 class Context(object): 11 11 "A stack container for variable context" 12 def __init__(self, dict_=None, autoescape=True ):12 def __init__(self, dict_=None, autoescape=True, loader=None): 13 13 dict_ = dict_ or {} 14 14 self.dicts = [dict_] 15 15 self.autoescape = autoescape 16 self.template_cache = {} 17 self.loader = loader 16 18 17 19 def __repr__(self): 18 20 return repr(self.dicts) … … 65 67 self.dicts = [other_dict] + self.dicts 66 68 return other_dict 67 69 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 68 89 # This is a function rather than module-level procedural code because we only 69 90 # want it to execute if somebody uses RequestContext. 70 91 def get_standard_processors(): … … 93 114 Additional processors can be specified as a list of callables 94 115 using the "processors" keyword argument. 95 116 """ 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) 98 119 if processors is None: 99 120 processors = () 100 121 else: -
django/template/loader_tags.py
1 1 from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable 2 2 from django.template import Library, Node, TextNode 3 from django.template import render_with_context_map, parse_context_map, parse_string_literal 3 4 from django.template.loader import get_template, get_template_from_string, find_template_source 4 5 from django.conf import settings 5 6 from django.utils.safestring import mark_safe … … 39 40 class ExtendsNode(Node): 40 41 must_be_first = True 41 42 42 def __init__(self, nodelist, parent_name, parent_name_expr , template_dirs=None):43 def __init__(self, nodelist, parent_name, parent_name_expr): 43 44 self.nodelist = nodelist 44 45 self.parent_name, self.parent_name_expr = parent_name, parent_name_expr 45 self.template_dirs = template_dirs46 46 47 47 def __repr__(self): 48 48 if self.parent_name_expr: … … 61 61 if hasattr(parent, 'render'): 62 62 return parent # parent is a Template object 63 63 try: 64 source, origin = find_template_source(parent, self.template_dirs)64 return context.get_template(parent) 65 65 except TemplateDoesNotExist: 66 66 raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent 67 else:68 return get_template_from_string(source, origin, parent)69 67 70 68 def render(self, context): 71 69 compiled_parent = self.get_parent(context) … … 96 94 parent_block.nodelist = block_node.nodelist 97 95 return compiled_parent.render(context) 98 96 99 class ConstantIncludeNode(Node):100 def __init__(self, template_path):101 try:102 t = get_template(template_path)103 self.template = t104 except:105 if settings.TEMPLATE_DEBUG:106 raise107 self.template = None108 109 def render(self, context):110 if self.template:111 return self.template.render(context)112 else:113 return ''114 115 97 class IncludeNode(Node): 116 def __init__(self, template_name ):98 def __init__(self, template_name, context_map=None): 117 99 self.template_name = Variable(template_name) 100 self.context_map = context_map 118 101 119 102 def render(self, context): 120 103 try: 121 104 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) 124 107 except TemplateSyntaxError, e: 125 108 if settings.TEMPLATE_DEBUG: 126 109 raise … … 162 145 if len(bits) != 2: 163 146 raise TemplateSyntaxError, "'%s' takes one argument" % bits[0] 164 147 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 e lse:148 try: 149 parent_name = parse_string_literal(bits[1]) 150 except ValueError: 168 151 parent_name_expr = parser.compile_filter(bits[1]) 169 152 nodelist = parser.parse() 170 153 if nodelist.get_nodes_by_type(ExtendsNode): … … 174 157 def do_include(parser, token): 175 158 """ 176 159 Loads a template and renders it with the current context. 160 Optionally takes a "with value as name (, value as name)*" clause. 177 161 178 162 Example:: 179 163 180 164 {% include "foo/some_include" %} 165 {% include "foo" with value as name %} 166 {% include "foo" with value as name, bar as baz %} 181 167 """ 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 189 180 190 181 register.tag('block', do_block) 191 182 register.tag('extends', do_extends) -
django/utils/text.py
1 1 import re 2 2 from django.conf import settings 3 3 from django.utils.encoding import force_unicode 4 from django.utils.functional import allow_lazy 4 from django.utils.functional import allow_lazy, memoize 5 5 from django.utils.translation import ugettext_lazy 6 6 7 7 # Capitalizes the first letter of a string. … … 196 196 return str(ustring_re.sub(fix, s)) 197 197 javascript_quote = allow_lazy(javascript_quote, unicode) 198 198 199 smart_split_re = re.compile('("(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|[^\\s]+)') 200 def smart_split(text): 199 quoted_string_pattern = '"(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'' 200 def 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)) 204 get_smart_split_regexp = memoize(get_smart_split_regexp, {}, 1) 205 206 def smart_split(text, separator_chars=None): 201 207 r""" 202 208 Generator that splits a string by spaces, leaving quoted phrases together. 203 209 Supports both single and double quotes, and supports escaping quotes with 204 210 backslashes. In the output, strings will keep their initial and trailing 205 211 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 207 215 >>> list(smart_split(r'This is "a person\'s" test.')) 208 216 [u'This', u'is', u'"a person\\\'s"', u'test.'] 209 217 >>> list(smart_split(r"Another 'person\'s' test.")) 210 218 [u'Another', u"'person's'", u'test.'] 211 219 >>> list(smart_split(r'A "\"funky\" style" test.')) 212 220 [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'] 213 225 """ 214 226 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): 216 229 bit = bit.group(0) 217 230 if bit[0] == '"' and bit[-1] == '"': 218 231 yield '"' + bit[1:-1].replace('\\"', '"').replace('\\\\', '\\') + '"' -
tests/regressiontests/templates/tests.py
611 611 'include02': ('{% include "basic-syntax02" %}', {'headline': 'Included'}, "Included"), 612 612 'include03': ('{% include template_name %}', {'template_name': 'basic-syntax02', 'headline': 'Included'}, "Included"), 613 613 '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)))'), 614 634 615 635 ### NAMED ENDBLOCKS ####################################################### 616 636 … … 854 874 ### WITH TAG ######################################################## 855 875 'with01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, '50'), 856 876 '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'), 857 881 858 882 'with-error01': ('{% with dict.key xx key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError), 859 883 '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 861 891 ### NOW TAG ######################################################## 862 892 # Simple case 863 893 'now01' : ('{% now "j n Y"%}', {}, str(datetime.now().day) + ' ' + str(datetime.now().month) + ' ' + str(datetime.now().year)), -
tests/regressiontests/text/tests.py
15 15 [u'"a', u"'one"] 16 16 >>> print list(smart_split(r'''all friends' tests'''))[1] 17 17 friends' 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'] 18 22 19 23 ### urlquote ############################################################# 20 24 >>> from django.utils.http import urlquote, urlquote_plus