Ticket #6262: cache_templates.2.diff

File cache_templates.2.diff, 28.6 KB (added by mmalone, 6 years ago)

template caching, refactored loaders, fixed template tags

  • django/template/__init__.py

     
    175175
    176176    def render(self, context):
    177177        "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()
    179183
    180184def compile_string(template_string, origin):
    181185    "Compiles template_string into NodeList ready for rendering"
  • django/template/loaders/app_directories.py

     
    99from django.conf import settings
    1010from django.core.exceptions import ImproperlyConfigured
    1111from django.template import TemplateDoesNotExist
     12from django.template.loader import BaseLoader
    1213from django.utils._os import safe_join
    1314from django.utils.importlib import import_module
    1415
     
    2728# It won't change, so convert it to a tuple to save memory.
    2829app_template_dirs = tuple(app_template_dirs)
    2930
    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
     31class Loader(BaseLoader):
     32    is_usable = True
    4733
     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
     60loader = Loader()
     61
    4862def 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)
    5565load_template_source.is_usable = True
  • django/template/loaders/filesystem.py

     
    44
    55from django.conf import settings
    66from django.template import TemplateDoesNotExist
     7from django.template.loader import BaseLoader
    78from django.utils._os import safe_join
    89
    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
     10class Loader(BaseLoader):
     11    is_usable = True
    2812
     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
     47loader = Loader()
     48
    2949def 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)
    4152load_template_source.is_usable = True
  • django/template/loaders/eggs.py

     
    66    resource_string = None
    77
    88from django.template import TemplateDoesNotExist
     9from django.template.loader import BaseLoader
    910from django.conf import settings
    1011
     12class 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
     30loader = Loader()
     31
    1132def 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)
    2534load_template_source.is_usable = resource_string is not None
  • django/template/loaders/cached.py

     
     1"""
     2Wrapper class that takes a list of template loaders as an argument and attempts
     3to load templates from them in order, caching the result.
     4"""
     5
     6from django.template import TemplateDoesNotExist
     7from django.template.loader import BaseLoader, get_template_from_string, find_template_loader, make_origin
     8from django.utils.importlib import import_module
     9from django.core.exceptions import ImproperlyConfigured
     10
     11class 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

     
    3939
    4040class CycleNode(Node):
    4141    def __init__(self, cyclevars, variable_name=None):
    42         self.cycle_iter = itertools_cycle(cyclevars)
     42        self.cyclevars = cyclevars
    4343        self.variable_name = variable_name
    4444
    4545    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)
    4750        if self.variable_name:
    4851            context[self.variable_name] = value
    4952        return value
  • django/template/context.py

     
    1414        self.dicts = [dict_]
    1515        self.autoescape = autoescape
    1616        self.current_app = current_app
     17        self.parser_context = ParserContext()
    1718
    1819    def __repr__(self):
    1920        return repr(self.dicts)
     
    6869        self.dicts = [other_dict] + self.dicts
    6970        return other_dict
    7071
     72class 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
    71118# This is a function rather than module-level procedural code because we only
    72119# want it to execute if somebody uses RequestContext.
    73120def get_standard_processors():
  • django/template/loader_tags.py

     
    11from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable
    22from django.template import Library, Node, TextNode
    3 from django.template.loader import get_template, get_template_from_string, find_template_source
     3from django.template.loader import get_template
    44from django.conf import settings
    55from django.utils.safestring import mark_safe
    66
    77register = Library()
    88
     9BLOCK_CONTEXT_KEY = 'block_context'
     10
    911class ExtendsError(Exception):
    1012    pass
    1113
     14class 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
    1241class BlockNode(Node):
    1342    def __init__(self, name, nodelist, parent=None):
    1443        self.name, self.nodelist, self.parent = name, nodelist, parent
     
    1746        return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
    1847
    1948    def render(self, context):
     49        block_context = context.parser_context.get(BLOCK_CONTEXT_KEY, None)
    2050        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)
    2565        context.pop()
    2666        return result
    2767
    2868    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))
    3173        return ''
    3274
    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 
    3975class ExtendsNode(Node):
    4076    must_be_first = True
    4177
     
    4379        self.nodelist = nodelist
    4480        self.parent_name, self.parent_name_expr = parent_name, parent_name_expr
    4581        self.template_dirs = template_dirs
    46 
     82        self.blocks = dict([(n.name, n) for n in nodelist.get_nodes_by_type(BlockNode)])
     83 
    4784    def __repr__(self):
    4885        if self.parent_name_expr:
    4986            return "<ExtendsNode: extends %s>" % self.parent_name_expr.token
     
    6198        if hasattr(parent, 'render'):
    6299            return parent # parent is a Template object
    63100        try:
    64             source, origin = find_template_source(parent, self.template_dirs)
     101            return get_template(parent)
    65102        except TemplateDoesNotExist:
    66103            raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
    67         else:
    68             return get_template_from_string(source, origin, parent)
    69104
    70105    def render(self, context):
    71106        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 the
    79                 # parent block might be defined in the parent's *parent*, so we
    80                 # add this BlockNode to the parent's ExtendsNode nodelist, so
    81                 # it'll be checked when the parent node's render() is called.
    82107
    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]
    98111
     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
    99130class ConstantIncludeNode(Node):
    100131    def __init__(self, template_path):
    101132        try:
  • django/template/loader.py

     
    2727
    2828template_source_loaders = None
    2929
     30class 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
    3044class LoaderOrigin(Origin):
    3145    def __init__(self, display_name, loader, name, dirs):
    3246        super(LoaderOrigin, self).__init__(display_name)
     
    4155    else:
    4256        return None
    4357
    44 def find_template_source(name, dirs=None):
     58def 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
     80def find_template(name, dirs=None):
    4581    # Calculate template_source_loaders the first time the function is executed
    4682    # because putting this logic in the module-level namespace may cause
    4783    # circular import errors. See Django ticket #1292.
    4884    global template_source_loaders
    4985    if template_source_loaders is None:
    5086        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))
    6789        template_source_loaders = tuple(loaders)
    6890    for loader in template_source_loaders:
    6991        try:
     
    78100    Returns a compiled Template object for the given template name,
    79101    handling template inheritance recursively.
    80102    """
    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)
    83107    return template
    84108
    85109def get_template_from_string(source, origin=None, name=None):
  • tests/regressiontests/templates/tests.py

     
    105105                # Fix expected sources so they are normcased and abspathed
    106106                expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources]
    107107            # 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))
    110110            for func in (func1, func2):
    111111                if isinstance(expected_sources, list):
    112112                    self.assertEqual(func(path, template_dirs), expected_sources)
     
    197197            except KeyError:
    198198                raise template.TemplateDoesNotExist, template_name
    199199
     200        from django.template.loaders.cached import Loader
     201        cache_loader = Loader((test_template_loader,))
     202
    200203        old_template_loaders = loader.template_source_loaders
    201         loader.template_source_loaders = [test_template_loader]
     204        loader.template_source_loaders = [cache_loader]
    202205
    203206        failures = []
    204207        tests = template_tests.items()
     
    231234            for invalid_str, result in [('', normal_string_result),
    232235                                        (expected_invalid_str, invalid_string_result)]:
    233236                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()
    248253
    249254            if 'LANGUAGE_CODE' in vals[1]:
    250255                deactivate()
Back to Top