Ticket #6262: cache_templates.2.diff
File cache_templates.2.diff, 28.6 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
9 9 from django.conf import settings 10 10 from django.core.exceptions import ImproperlyConfigured 11 11 from django.template import TemplateDoesNotExist 12 from django.template.loader import BaseLoader 12 13 from django.utils._os import safe_join 13 14 from django.utils.importlib import import_module 14 15 … … 27 28 # It won't change, so convert it to a tuple to save memory. 28 29 app_template_dirs = tuple(app_template_dirs) 29 30 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 31 class Loader(BaseLoader): 32 is_usable = True 47 33 34 def get_template_sources(self, template_name, template_dirs=None): 35 """ 36 Returns the absolute paths to "template_name", when appended to each 37 directory in "template_dirs". Any paths that don't lie inside one of the 38 template dirs are excluded from the result set, for security reasons. 39 """ 40 if not template_dirs: 41 template_dirs = app_template_dirs 42 for template_dir in template_dirs: 43 try: 44 yield safe_join(template_dir, template_name) 45 except UnicodeDecodeError: 46 # The template dir name was a bytestring that wasn't valid UTF-8. 47 raise 48 except ValueError: 49 # The joined path was located outside of template_dir. 50 pass 51 52 def load_template_source(self, template_name, template_dirs=None): 53 for filepath in self.get_template_sources(template_name, template_dirs): 54 try: 55 return (open(filepath).read().decode(settings.FILE_CHARSET), filepath) 56 except IOError: 57 pass 58 raise TemplateDoesNotExist, template_name 59 60 loader = Loader() 61 48 62 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 63 # For backwards compatibility 64 return loader.load_template_source(template_name, template_dirs) 55 65 load_template_source.is_usable = True -
django/template/loaders/filesystem.py
4 4 5 5 from django.conf import settings 6 6 from django.template import TemplateDoesNotExist 7 from django.template.loader import BaseLoader 7 8 from django.utils._os import safe_join 8 9 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 10 class Loader(BaseLoader): 11 is_usable = True 28 12 13 def get_template_sources(self, template_name, template_dirs=None): 14 """ 15 Returns the absolute paths to "template_name", when appended to each 16 directory in "template_dirs". Any paths that don't lie inside one of the 17 template dirs are excluded from the result set, for security reasons. 18 """ 19 if not template_dirs: 20 template_dirs = settings.TEMPLATE_DIRS 21 for template_dir in template_dirs: 22 try: 23 yield safe_join(template_dir, template_name) 24 except UnicodeDecodeError: 25 # The template dir name was a bytestring that wasn't valid UTF-8. 26 raise 27 except ValueError: 28 # The joined path was located outside of this particular 29 # template_dir (it might be inside another one, so this isn't 30 # fatal). 31 pass 32 33 def load_template_source(self, template_name, template_dirs=None): 34 tried = [] 35 for filepath in self.get_template_sources(template_name, template_dirs): 36 try: 37 return (open(filepath).read().decode(settings.FILE_CHARSET), filepath) 38 except IOError: 39 tried.append(filepath) 40 if tried: 41 error_msg = "Tried %s" % tried 42 else: 43 error_msg = "Your TEMPLATE_DIRS setting is empty. Change it to point to at least one template directory." 44 raise TemplateDoesNotExist, error_msg 45 load_template_source.is_usable = True 46 47 loader = Loader() 48 29 49 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 50 # For backwards compatibility 51 return loader.load_template_source(template_name, template_dirs) 41 52 load_template_source.is_usable = True -
django/template/loaders/eggs.py
6 6 resource_string = None 7 7 8 8 from django.template import TemplateDoesNotExist 9 from django.template.loader import BaseLoader 9 10 from django.conf import settings 10 11 12 class Loader(BaseLoader): 13 is_usable = resource_string is not None 14 15 def load_template_source(self, template_name, template_dirs=None): 16 """ 17 Loads templates from Python eggs via pkg_resource.resource_string. 18 19 For every installed app, it tries to get the resource (app, template_name). 20 """ 21 if resource_string is not None: 22 pkg_name = 'templates/' + template_name 23 for app in settings.INSTALLED_APPS: 24 try: 25 return (resource_string(app, pkg_name).decode(settings.FILE_CHARSET), 'egg:%s:%s' % (app, pkg_name)) 26 except: 27 pass 28 raise TemplateDoesNotExist, template_name 29 30 loader = Loader() 31 11 32 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 33 return loader.load_template_source(template_name, template_dirs) 25 34 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 TemplateDoesNotExist 7 from django.template.loader import BaseLoader, get_template_from_string, find_template_loader, make_origin 8 from django.utils.importlib import import_module 9 from django.core.exceptions import ImproperlyConfigured 10 11 class Loader(BaseLoader): 12 is_usable = True 13 template_cache = {} 14 15 def __init__(self, loaders): 16 self._loaders = loaders 17 self._cached_loaders = [] 18 19 #@property 20 def loaders(self): 21 # Resolve loaders on demand to avoid circular imports 22 if not self._cached_loaders: 23 for loader in self._loaders: 24 self._cached_loaders.append(find_template_loader(loader)) 25 return self._cached_loaders 26 loaders = property(loaders) 27 28 def find_template(self, name, dirs=None): 29 for loader in self.loaders: 30 try: 31 template, display_name = loader(name, dirs) 32 return (template, make_origin(display_name, loader, name, dirs)) 33 except TemplateDoesNotExist: 34 pass 35 raise TemplateDoesNotExist, name 36 37 def load_template(self, template_name, template_dirs=None): 38 if template_name not in self.template_cache: 39 template, origin = self.find_template(template_name, template_dirs) 40 if not hasattr(template, 'render'): 41 template = get_template_from_string(template, origin, template_name) 42 self.template_cache[template_name] = (template, origin) 43 return self.template_cache[template_name] 44 45 def reset(self): 46 "Empty the template cache." 47 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[-1]: 83 yield d 84 85 def push(self): 86 d = {} 87 self.dicts.append(d) 88 return d 89 90 def pop(self): 91 if len(self.dicts) == 1: 92 raise ContextPopException 93 return self.dicts.pop() 94 95 def __setitem__(self, key, value): 96 "Set a variable in the current context" 97 self.dicts[-1][key] = value 98 99 def __getitem__(self, key): 100 "Get a variable's value from the current context" 101 return self.dicts[-1][key] 102 103 def __delitem__(self, key): 104 "Deletes a variable from the current context" 105 del self.dicts[-1][key] 106 107 def has_key(self, key): 108 return key in self.dicts[-1] 109 110 __contains__ = has_key 111 112 def get(self, key, otherwise=None): 113 d = self.dicts[-1] 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-safety 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 BaseLoader(object): 31 is_usable = False 32 33 def __call__(self, template_name, template_dirs=None): 34 return self.load_template(template_name, template_dirs) 35 36 def load_template(self, template_name, template_dirs=None): 37 source, origin = self.load_template_source(template_name, template_dirs) 38 template = get_template_from_string(source, name=template_name) 39 return template, origin 40 41 def load_template_from_source(self, template_name, template_dirs=None): 42 raise NotImplementedError 43 30 44 class LoaderOrigin(Origin): 31 45 def __init__(self, display_name, loader, name, dirs): 32 46 super(LoaderOrigin, self).__init__(display_name) … … 41 55 else: 42 56 return None 43 57 44 def find_template_source(name, dirs=None): 58 def find_template_loader(loader): 59 if callable(loader): 60 return loader 61 elif 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 return func 77 else: 78 raise ImproperlyConfigured, 'Loader does not define a "load_template" callable template source loader' 79 80 def find_template(name, dirs=None): 45 81 # Calculate template_source_loaders the first time the function is executed 46 82 # because putting this logic in the module-level namespace may cause 47 83 # circular import errors. See Django ticket #1292. 48 84 global template_source_loaders 49 85 if template_source_loaders is None: 50 86 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) 65 else: 66 loaders.append(func) 87 for loader in settings.TEMPLATE_LOADERS: 88 loaders.append(find_template_loader(loader)) 67 89 template_source_loaders = tuple(loaders) 68 90 for loader in template_source_loaders: 69 91 try: … … 78 100 Returns a compiled Template object for the given template name, 79 101 handling template inheritance recursively. 80 102 """ 81 source, origin = find_template_source(template_name) 82 template = get_template_from_string(source, origin, template_name) 103 template, origin = find_template(template_name) 104 if not hasattr(template, 'render'): 105 # template needs to be compiled 106 template = get_template_from_string(template, origin, template_name) 83 107 return template 84 108 85 109 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] 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()