Ticket #6262: cached_templates.3.diff
File cached_templates.3.diff, 28.4 KB (added by , 15 years ago) |
---|
-
django/template/__init__.py
175 175 176 176 def render(self, context): 177 177 "Display stage -- can be called many times" 178 return self.nodelist.render(context) 178 context.parser_context.push() 179 try: 180 return self.nodelist.render(context) 181 finally: 182 context.parser_context.pop() 179 183 180 184 def compile_string(template_string, origin): 181 185 "Compiles template_string into NodeList ready for rendering" -
django/template/loaders/app_directories.py
8 8 9 9 from django.conf import settings 10 10 from django.core.exceptions import ImproperlyConfigured 11 from django.template import TemplateDoesNotExist11 from django.template import loader, TemplateDoesNotExist 12 12 from django.utils._os import safe_join 13 13 from django.utils.importlib import import_module 14 14 … … 27 27 # It won't change, so convert it to a tuple to save memory. 28 28 app_template_dirs = tuple(app_template_dirs) 29 29 30 def get_template_sources(template_name, template_dirs=None): 31 """ 32 Returns the absolute paths to "template_name", when appended to each 33 directory in "template_dirs". Any paths that don't lie inside one of the 34 template dirs are excluded from the result set, for security reasons. 35 """ 36 if not template_dirs: 37 template_dirs = app_template_dirs 38 for template_dir in template_dirs: 39 try: 40 yield safe_join(template_dir, template_name) 41 except UnicodeDecodeError: 42 # The template dir name was a bytestring that wasn't valid UTF-8. 43 raise 44 except ValueError: 45 # The joined path was located outside of template_dir. 46 pass 30 class Loader(loader.Loader): 31 def get_template_sources(self, template_name, template_dirs=None): 32 """ 33 Returns the absolute paths to "template_name", when appended to each 34 directory in "template_dirs". Any paths that don't lie inside one of the 35 template dirs are excluded from the result set, for security reasons. 36 """ 37 if not template_dirs: 38 template_dirs = app_template_dirs 39 for template_dir in template_dirs: 40 try: 41 yield safe_join(template_dir, template_name) 42 except UnicodeDecodeError: 43 # The template dir name was a bytestring that wasn't valid UTF-8. 44 raise 45 except ValueError: 46 # The joined path was located outside of template_dir. 47 pass 47 48 49 def load_template_source(self, template_name, template_dirs=None): 50 for filepath in self.get_template_sources(template_name, template_dirs): 51 try: 52 return (open(filepath).read().decode(settings.FILE_CHARSET), filepath) 53 except IOError: 54 pass 55 raise TemplateDoesNotExist, template_name 56 load_template_source.is_usable = True 57 58 loader = Loader() 59 48 60 def load_template_source(template_name, template_dirs=None): 49 for filepath in get_template_sources(template_name, template_dirs): 50 try: 51 return (open(filepath).read().decode(settings.FILE_CHARSET), filepath) 52 except IOError: 53 pass 54 raise TemplateDoesNotExist, template_name 61 # For backwards compatibility 62 return loader.load_template_source(template_name, template_dirs) 55 63 load_template_source.is_usable = True -
django/template/loaders/filesystem.py
3 3 """ 4 4 5 5 from django.conf import settings 6 from django.template import TemplateDoesNotExist6 from django.template import loader, TemplateDoesNotExist 7 7 from django.utils._os import safe_join 8 8 9 def get_template_sources(template_name, template_dirs=None): 10 """ 11 Returns the absolute paths to "template_name", when appended to each 12 directory in "template_dirs". Any paths that don't lie inside one of the 13 template dirs are excluded from the result set, for security reasons. 14 """ 15 if not template_dirs: 16 template_dirs = settings.TEMPLATE_DIRS 17 for template_dir in template_dirs: 18 try: 19 yield safe_join(template_dir, template_name) 20 except UnicodeDecodeError: 21 # The template dir name was a bytestring that wasn't valid UTF-8. 22 raise 23 except ValueError: 24 # The joined path was located outside of this particular 25 # template_dir (it might be inside another one, so this isn't 26 # fatal). 27 pass 9 class Loader(loader.Loader): 10 def get_template_sources(self, template_name, template_dirs=None): 11 """ 12 Returns the absolute paths to "template_name", when appended to each 13 directory in "template_dirs". Any paths that don't lie inside one of the 14 template dirs are excluded from the result set, for security reasons. 15 """ 16 if not template_dirs: 17 template_dirs = settings.TEMPLATE_DIRS 18 for template_dir in template_dirs: 19 try: 20 yield safe_join(template_dir, template_name) 21 except UnicodeDecodeError: 22 # The template dir name was a bytestring that wasn't valid UTF-8. 23 raise 24 except ValueError: 25 # The joined path was located outside of this particular 26 # template_dir (it might be inside another one, so this isn't 27 # fatal). 28 pass 28 29 30 def load_template_source(self, template_name, template_dirs=None): 31 tried = [] 32 for filepath in self.get_template_sources(template_name, template_dirs): 33 try: 34 return (open(filepath).read().decode(settings.FILE_CHARSET), filepath) 35 except IOError: 36 tried.append(filepath) 37 if tried: 38 error_msg = "Tried %s" % tried 39 else: 40 error_msg = "Your TEMPLATE_DIRS setting is empty. Change it to point to at least one template directory." 41 raise TemplateDoesNotExist, error_msg 42 load_template_source.is_usable = True 43 44 loader = Loader() 45 29 46 def load_template_source(template_name, template_dirs=None): 30 tried = [] 31 for filepath in get_template_sources(template_name, template_dirs): 32 try: 33 return (open(filepath).read().decode(settings.FILE_CHARSET), filepath) 34 except IOError: 35 tried.append(filepath) 36 if tried: 37 error_msg = "Tried %s" % tried 38 else: 39 error_msg = "Your TEMPLATE_DIRS setting is empty. Change it to point to at least one template directory." 40 raise TemplateDoesNotExist, error_msg 47 # For backwards compatibility 48 return loader.load_template_source(template_name, template_dirs) 41 49 load_template_source.is_usable = True -
django/template/loaders/eggs.py
5 5 except ImportError: 6 6 resource_string = None 7 7 8 from django.template import TemplateDoesNotExist8 from django.template import loader, TemplateDoesNotExist 9 9 from django.conf import settings 10 10 11 class Loader(loader.Loader): 12 def load_template_source(self, template_name, template_dirs=None): 13 """ 14 Loads templates from Python eggs via pkg_resource.resource_string. 15 16 For every installed app, it tries to get the resource (app, template_name). 17 """ 18 if resource_string is not None: 19 pkg_name = 'templates/' + template_name 20 for app in settings.INSTALLED_APPS: 21 try: 22 return (resource_string(app, pkg_name).decode(settings.FILE_CHARSET), 'egg:%s:%s' % (app, pkg_name)) 23 except: 24 pass 25 raise TemplateDoesNotExist, template_name 26 load_template_source.is_usable = resource_string is not None 27 28 loader = Loader() 29 11 30 def load_template_source(template_name, template_dirs=None): 12 """ 13 Loads templates from Python eggs via pkg_resource.resource_string. 14 15 For every installed app, it tries to get the resource (app, template_name). 16 """ 17 if resource_string is not None: 18 pkg_name = 'templates/' + template_name 19 for app in settings.INSTALLED_APPS: 20 try: 21 return (resource_string(app, pkg_name).decode(settings.FILE_CHARSET), 'egg:%s:%s' % (app, pkg_name)) 22 except: 23 pass 24 raise TemplateDoesNotExist, template_name 31 return loader.load_template_source(template_name, template_dirs) 25 32 load_template_source.is_usable = resource_string is not None -
django/template/loaders/cached.py
1 """ 2 Wrapper class that takes a list of template loaders as an argument and attempts 3 to load templates from them in order, caching the result. 4 """ 5 6 from django.template import loader 7 from django.utils.importlib import import_module 8 from django.core.exceptions import ImproperlyConfigured 9 10 class Loader(loader.Loader): 11 template_cache = {} 12 13 def __init__(self, loader): 14 if isinstance(loader, basestring): 15 self._loader_name = loader 16 self._loader = None 17 else: 18 self._loader_name = None 19 self._loader = loader.load_template 20 21 @property 22 def loader(self): 23 # Resolve loader on demand to avoid circular imports 24 if self._loader is None: 25 loader_name = self._loader_name 26 i = loader_name.rfind('.') 27 module, attr = loader_name[:i], loader_name[i+1:] 28 try: 29 mod = import_module(module) 30 except ImportError, e: 31 raise ImproperlyConfigured, 'Error importing template source loader %s: "%s"' % (module, e) 32 try: 33 loader = getattr(mod, attr) 34 except AttributeError: 35 raise ImproperlyConfigured, 'Module "%s" does not define a "%s" callable template source loader' % (module, attr) 36 self._loader = loader.load_template_source 37 return self._loader 38 39 def load_template(self, template_name, template_dirs=None): 40 if template_name not in self.template_cache: 41 source, origin = self.loader(template_name, template_dirs) 42 template = loader.get_template_from_string(source, name=origin) 43 self.template_cache[template_name] = (template, origin) 44 return self.template_cache[template_name] 45 load_template.is_usable = True 46 47 def reset(self): 48 "Empty the template cache." 49 self.template_cache = {} -
django/template/defaulttags.py
39 39 40 40 class CycleNode(Node): 41 41 def __init__(self, cyclevars, variable_name=None): 42 self.cycle _iter = itertools_cycle(cyclevars)42 self.cyclevars = cyclevars 43 43 self.variable_name = variable_name 44 44 45 45 def render(self, context): 46 value = self.cycle_iter.next().resolve(context) 46 if self not in context.parser_context: 47 context.parser_context[self] = {'cycle_iter': itertools_cycle(self.cyclevars)} 48 cycle_iter = context.parser_context[self]['cycle_iter'] 49 value = cycle_iter.next().resolve(context) 47 50 if self.variable_name: 48 51 context[self.variable_name] = value 49 52 return value -
django/template/context.py
14 14 self.dicts = [dict_] 15 15 self.autoescape = autoescape 16 16 self.current_app = current_app 17 self.parser_context = ParserContext() 17 18 18 19 def __repr__(self): 19 20 return repr(self.dicts) … … 68 69 self.dicts = [other_dict] + self.dicts 69 70 return other_dict 70 71 72 class ParserContext(object): 73 """A stack container for storing Template state.""" 74 def __init__(self, dict_=None): 75 dict_ = dict_ or {} 76 self.dicts = [dict_] 77 78 def __repr__(self): 79 return repr(self.dicts) 80 81 def __iter__(self): 82 for d in self.dicts[0]: 83 yield d 84 85 def push(self): 86 d = {} 87 self.dicts = [d] + self.dicts 88 return d 89 90 def pop(self): 91 if len(self.dicts) == 1: 92 raise ContextPopException 93 return self.dicts.pop(0) 94 95 def __setitem__(self, key, value): 96 "Set a variable in the current context" 97 self.dicts[0][key] = value 98 99 def __getitem__(self, key): 100 "Get a variable's value from the current context" 101 return self.dicts[0][key] 102 103 def __delitem__(self, key): 104 "Deletes a variable from the current context" 105 del self.dicts[0][key] 106 107 def has_key(self, key): 108 return key in self.dicts[0] 109 110 __contains__ = has_key 111 112 def get(self, key, otherwise=None): 113 d = self.dicts[0] 114 if key in d: 115 return d[key] 116 return otherwise 117 71 118 # This is a function rather than module-level procedural code because we only 72 119 # want it to execute if somebody uses RequestContext. 73 120 def get_standard_processors(): -
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.loader import get_template , get_template_from_string, find_template_source3 from django.template.loader import get_template 4 4 from django.conf import settings 5 5 from django.utils.safestring import mark_safe 6 6 7 7 register = Library() 8 8 9 BLOCK_CONTEXT_KEY = 'block_context' 10 9 11 class ExtendsError(Exception): 10 12 pass 11 13 14 class BlockContext(object): 15 def __init__(self): 16 # Dictionary of FIFO queues. 17 self.blocks = {} 18 19 def add_blocks(self, blocks): 20 for name, block in blocks.iteritems(): 21 if name in self.blocks: 22 self.blocks[name].insert(0, block) 23 else: 24 self.blocks[name] = [block] 25 26 def pop(self, name): 27 try: 28 return self.blocks[name].pop() 29 except (IndexError, KeyError): 30 return None 31 32 def push(self, name, block): 33 self.blocks[name].append(block) 34 35 def get_block(self, name): 36 try: 37 return self.blocks[name][-1] 38 except (IndexError, KeyError): 39 return None 40 12 41 class BlockNode(Node): 13 42 def __init__(self, name, nodelist, parent=None): 14 43 self.name, self.nodelist, self.parent = name, nodelist, parent … … 17 46 return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist) 18 47 19 48 def render(self, context): 49 block_context = context.parser_context.get(BLOCK_CONTEXT_KEY, None) 20 50 context.push() 21 # Save context in case of block.super(). 22 self.context = context 23 context['block'] = self 24 result = self.nodelist.render(context) 51 if block_context is None: 52 context['block'] = self 53 result = self.nodelist.render(context) 54 else: 55 push = block = block_context.pop(self.name) 56 if block is None: 57 block = self 58 # Create new block so we can store context without thread-safetly issues. 59 block = BlockNode(block.name, block.nodelist) 60 block.context = context 61 context['block'] = block 62 result = block.nodelist.render(context) 63 if push is not None: 64 block_context.push(self.name, push) 25 65 context.pop() 26 66 return result 27 67 28 68 def super(self): 29 if self.parent: 30 return mark_safe(self.parent.render(self.context)) 69 parser_context = self.context.parser_context 70 if (BLOCK_CONTEXT_KEY in parser_context and 71 parser_context[BLOCK_CONTEXT_KEY].get_block(self.name) is not None): 72 return mark_safe(self.render(self.context)) 31 73 return '' 32 74 33 def add_parent(self, nodelist):34 if self.parent:35 self.parent.add_parent(nodelist)36 else:37 self.parent = BlockNode(self.name, nodelist)38 39 75 class ExtendsNode(Node): 40 76 must_be_first = True 41 77 … … 43 79 self.nodelist = nodelist 44 80 self.parent_name, self.parent_name_expr = parent_name, parent_name_expr 45 81 self.template_dirs = template_dirs 46 82 self.blocks = dict([(n.name, n) for n in nodelist.get_nodes_by_type(BlockNode)]) 83 47 84 def __repr__(self): 48 85 if self.parent_name_expr: 49 86 return "<ExtendsNode: extends %s>" % self.parent_name_expr.token … … 61 98 if hasattr(parent, 'render'): 62 99 return parent # parent is a Template object 63 100 try: 64 source, origin = find_template_source(parent, self.template_dirs)101 return get_template(parent) 65 102 except TemplateDoesNotExist: 66 103 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 104 70 105 def render(self, context): 71 106 compiled_parent = self.get_parent(context) 72 parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])73 for block_node in self.nodelist.get_nodes_by_type(BlockNode):74 # Check for a BlockNode with this node's name, and replace it if found.75 try:76 parent_block = parent_blocks[block_node.name]77 except KeyError:78 # This BlockNode wasn't found in the parent template, but the79 # parent block might be defined in the parent's *parent*, so we80 # add this BlockNode to the parent's ExtendsNode nodelist, so81 # it'll be checked when the parent node's render() is called.82 107 83 # Find out if the parent template has a parent itself 84 for node in compiled_parent.nodelist: 85 if not isinstance(node, TextNode): 86 # If the first non-text node is an extends, handle it. 87 if isinstance(node, ExtendsNode): 88 node.nodelist.append(block_node) 89 # Extends must be the first non-text node, so once you find 90 # the first non-text node you can stop looking. 91 break 92 else: 93 # Keep any existing parents and add a new one. Used by BlockNode. 94 parent_block.parent = block_node.parent 95 parent_block.add_parent(parent_block.nodelist) 96 parent_block.nodelist = block_node.nodelist 97 return compiled_parent.render(context) 108 if BLOCK_CONTEXT_KEY not in context.parser_context: 109 context.parser_context[BLOCK_CONTEXT_KEY] = BlockContext() 110 block_context = context.parser_context[BLOCK_CONTEXT_KEY] 98 111 112 # Add the block nodes from this node to the block context 113 block_context.add_blocks(self.blocks) 114 115 # If this block's parent doesn't have an extends node it is the root, 116 # and its block nodes also need to be added to the block context. 117 for node in compiled_parent.nodelist: 118 # The ExtendsNode has to be the first non-text node. 119 if not isinstance(node, TextNode): 120 if not isinstance(node, ExtendsNode): 121 blocks = dict([(n.name, n) for n in 122 compiled_parent.nodelist.get_nodes_by_type(BlockNode)]) 123 block_context.add_blocks(blocks) 124 break 125 126 # Call render on nodelist explicitly so the block context stays 127 # the same. 128 return compiled_parent.nodelist.render(context) 129 99 130 class ConstantIncludeNode(Node): 100 131 def __init__(self, template_path): 101 132 try: -
django/template/loader.py
27 27 28 28 template_source_loaders = None 29 29 30 class Loader(object): 31 def load_template(self, template_name, template_dirs=None): 32 source, origin = self.load_template_source(template_name, template_dirs) 33 template = get_template_from_string(source, name=template_name) 34 return template, origin 35 36 def load_template_from_source(self, template_name, template_dirs=None): 37 raise NotImplementedError 38 30 39 class LoaderOrigin(Origin): 31 40 def __init__(self, display_name, loader, name, dirs): 32 41 super(LoaderOrigin, self).__init__(display_name) … … 48 57 global template_source_loaders 49 58 if template_source_loaders is None: 50 59 loaders = [] 51 for path in settings.TEMPLATE_LOADERS: 52 i = path.rfind('.') 53 module, attr = path[:i], path[i+1:] 54 try: 55 mod = import_module(module) 56 except ImportError, e: 57 raise ImproperlyConfigured, 'Error importing template source loader %s: "%s"' % (module, e) 58 try: 59 func = getattr(mod, attr) 60 except AttributeError: 61 raise ImproperlyConfigured, 'Module "%s" does not define a "%s" callable template source loader' % (module, attr) 62 if not func.is_usable: 63 import warnings 64 warnings.warn("Your TEMPLATE_LOADERS setting includes %r, but your Python installation doesn't support that type of template loading. Consider removing that line from TEMPLATE_LOADERS." % path) 60 for loader in settings.TEMPLATE_LOADERS: 61 if isinstance(loader, basestring): 62 i = loader.rfind('.') 63 module, attr = loader[:i], loader[i+1:] 64 try: 65 mod = import_module(module) 66 except ImportError, e: 67 raise ImproperlyConfigured, 'Error importing template source loader %s: "%s"' % (module, e) 68 try: 69 func = getattr(mod, attr) 70 except AttributeError: 71 raise ImproperlyConfigured, 'Module "%s" does not define a "%s" callable template source loader' % (module, attr) 72 if not func.is_usable: 73 import warnings 74 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) 75 else: 76 loaders.append(func) 77 elif hasattr(loader, 'load_template'): 78 loaders.append(loader.load_template) 65 79 else: 66 loaders.append(func)80 raise ImproperlyConfigured, 'Loader does not define a "load_template" callable template source loader' 67 81 template_source_loaders = tuple(loaders) 68 82 for loader in template_source_loaders: 69 83 try: … … 78 92 Returns a compiled Template object for the given template name, 79 93 handling template inheritance recursively. 80 94 """ 81 source, origin = find_template_source(template_name) 82 template = get_template_from_string(source, origin, template_name) 95 source_or_template, origin = find_template_source(template_name) 96 if hasattr(source_or_template, 'render'): 97 # source is a compiled template 98 return source_or_template 99 template = get_template_from_string(source_or_template, origin, template_name) 83 100 return template 84 101 85 102 def get_template_from_string(source, origin=None, name=None): -
tests/regressiontests/templates/tests.py
105 105 # Fix expected sources so they are normcased and abspathed 106 106 expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources] 107 107 # Test the two loaders (app_directores and filesystem). 108 func1 = lambda p, t: list(app_directories. get_template_sources(p, t))109 func2 = lambda p, t: list(filesystem. get_template_sources(p, t))108 func1 = lambda p, t: list(app_directories.loader.get_template_sources(p, t)) 109 func2 = lambda p, t: list(filesystem.loader.get_template_sources(p, t)) 110 110 for func in (func1, func2): 111 111 if isinstance(expected_sources, list): 112 112 self.assertEqual(func(path, template_dirs), expected_sources) … … 197 197 except KeyError: 198 198 raise template.TemplateDoesNotExist, template_name 199 199 200 from django.template.loaders.cached import Loader 201 cache_loader = Loader(test_template_loader) 202 200 203 old_template_loaders = loader.template_source_loaders 201 loader.template_source_loaders = [ test_template_loader]204 loader.template_source_loaders = [cache_loader.load_template] 202 205 203 206 failures = [] 204 207 tests = template_tests.items() … … 231 234 for invalid_str, result in [('', normal_string_result), 232 235 (expected_invalid_str, invalid_string_result)]: 233 236 settings.TEMPLATE_STRING_IF_INVALID = invalid_str 234 try: 235 test_template = loader.get_template(name) 236 output = self.render(test_template, vals) 237 except ContextStackException: 238 failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Context stack was left imbalanced" % (invalid_str, name)) 239 continue 240 except Exception: 241 exc_type, exc_value, exc_tb = sys.exc_info() 242 if exc_type != result: 243 tb = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_tb)) 244 failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s\n%s" % (invalid_str, name, exc_type, exc_value, tb)) 245 continue 246 if output != result: 247 failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output)) 237 for cached in (False, True): 238 try: 239 test_template = loader.get_template(name) 240 output = self.render(test_template, vals) 241 except ContextStackException: 242 failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Context stack was left imbalanced" % (cached, invalid_str, name)) 243 continue 244 except Exception: 245 exc_type, exc_value, exc_tb = sys.exc_info() 246 if exc_type != result: 247 tb = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_tb)) 248 failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s\n%s" % (cached, invalid_str, name, exc_type, exc_value, tb)) 249 continue 250 if output != result: 251 failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (cached, invalid_str, name, result, output)) 252 cache_loader.reset() 248 253 249 254 if 'LANGUAGE_CODE' in vals[1]: 250 255 deactivate()