Ticket #6516: cleanup.diff

File cleanup.diff, 11.1 KB (added by Wayne K. Werner, 16 years ago)
  • loader_tags.py

     
    1 from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable
     1from django.template import Template, TemplateSyntaxError, TemplateDoesNotExist, Variable
    22from django.template import Library, Node
    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
     
    1010    pass
    1111
    1212class BlockNode(Node):
    13     def __init__(self, name, nodelist, parent=None):
    14         self.name, self.nodelist, self.parent = name, nodelist, parent
     13    def __init__(self, name, nodelist):
     14        self.name = name
     15        self.nodelist = nodelist
     16        self.parent = None
    1517
    1618    def __repr__(self):
    1719        return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
     
    3032            return mark_safe(self.parent.render(self.context))
    3133        return ''
    3234
    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)
     35    def insert_as_child(self, parent):
     36        eldest = self
     37        while eldest.parent:
     38            eldest = eldest.parent
     39        eldest.parent = BlockNode(parent.name, parent.nodelist)
     40        parent.nodelist = self.nodelist
     41        parent.parent = self.parent
    3842
    3943class ExtendsNode(Node):
    40     def __init__(self, nodelist, parent_name, parent_name_expr, template_dirs=None):
     44    def __init__(self, nodelist, extended_name=None, extended_name_expr=None):
    4145        self.nodelist = nodelist
    42         self.parent_name, self.parent_name_expr = parent_name, parent_name_expr
    43         self.template_dirs = template_dirs
     46        self.extended_name = extended_name
     47        self.extended_name_expr = extended_name_expr
    4448
    45     def get_parent(self, context):
    46         if self.parent_name_expr:
    47             self.parent_name = self.parent_name_expr.resolve(context)
    48         parent = self.parent_name
    49         if not parent:
    50             error_msg = "Invalid template name in 'extends' tag: %r." % parent
    51             if self.parent_name_expr:
    52                 error_msg += " Got this from the %r variable." % self.parent_name_expr #TODO nice repr.
    53             raise TemplateSyntaxError, error_msg
    54         if hasattr(parent, 'render'):
    55             return parent # parent is a Template object
     49    def _get_extended_template(self, context):
     50        extended_name = self.extended_name
     51        if self.extended_name_expr:
     52            resolved_expr = self.extended_name_expr.resolve(context)
     53            if not resolved_expr:
     54                raise TemplateSyntaxError("Can't resolve 'extends' tag argument '%r'" % self.extended_name_expr)
     55            # If the expr resolves to a template, just return it.
     56            if isinstance(resolved_expr, Template):
     57                return resolved_expr
     58            # Assume that resolved_expr is the extended templates name string.
     59            extended_name = resolved_expr
    5660        try:
    57             source, origin = find_template_source(parent, self.template_dirs)
     61            return get_template(extended_name)
     62        # Translate does not exist errors to syntax errors for extends tag.
    5863        except TemplateDoesNotExist:
    59             raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
    60         else:
    61             return get_template_from_string(source, origin, parent)
     64            raise TemplateSyntaxError("Can't find %r in 'extends' tag" % extended_name)
    6265
    6366    def render(self, context):
    64         compiled_parent = self.get_parent(context)
    65         parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode)
    66         parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
    67         for block_node in self.nodelist.get_nodes_by_type(BlockNode):
    68             # Check for a BlockNode with this node's name, and replace it if found.
     67        extended = self._get_extended_template(context)
     68        extended_extends = isinstance(extended.nodelist[0], ExtendsNode)
     69        extended_blocks = dict([(n.name, n) for n in extended.nodelist.get_nodes_by_type(BlockNode)])
     70
     71        # For each block node in this template.
     72        for block in self.nodelist.get_nodes_by_type(BlockNode):
     73            # Check extended template for a BlockNode with this block's name
     74            # and replace it with this block node if found.
    6975            try:
    70                 parent_block = parent_blocks[block_node.name]
     76                extended_block = extended_blocks[block.name]
     77            # If this BlockNode wasn't found in the extended template.
    7178            except KeyError:
    72                 # This BlockNode wasn't found in the parent template, but the
    73                 # parent block might be defined in the parent's *parent*, so we
    74                 # add this BlockNode to the parent's ExtendsNode nodelist, so
    75                 # it'll be checked when the parent node's render() is called.
    76                 if parent_is_child:
    77                     compiled_parent.nodelist[0].nodelist.append(block_node)
     79                # If the extended template doesn't extend another,
     80                # this block will not be rendered, so we can just drop it.
     81                #
     82                # If the extended template extends another,
     83                # it might be defined higher up the hierarchy.
     84                #
     85                # For this case, we add this block
     86                # to the extended templates ExtendsNode nodelist so it will
     87                # be checked when the extended template is rendered.
     88                if extended_extends:
     89                    extended.nodelist[0].nodelist.append(block)
     90            # Block was found in extended template
    7891            else:
    79                 # Keep any existing parents and add a new one. Used by BlockNode.
    80                 parent_block.parent = block_node.parent
    81                 parent_block.add_parent(parent_block.nodelist)
    82                 parent_block.nodelist = block_node.nodelist
    83         return compiled_parent.render(context)
     92                # Make the extended block a parent of block,
     93                # and replace the extended block with block
     94                # in the template
     95                block.insert_as_child(extended_block)
     96        # Return the result of rendering the extended template
     97        # now that blocks in the extended template have been replaced with
     98        # blocks from this template.
     99        return extended.render(context)
    84100
    85101class ConstantIncludeNode(Node):
    86102    def __init__(self, template_path):
     
    95111    def render(self, context):
    96112        if self.template:
    97113            return self.template.render(context)
    98         else:
    99             return ''
     114        return ''
    100115
    101116class IncludeNode(Node):
    102117    def __init__(self, template_name):
     
    107122            template_name = self.template_name.resolve(context)
    108123            t = get_template(template_name)
    109124            return t.render(context)
    110         except TemplateSyntaxError, e:
     125        # If debugging templates, propagate any syntax errors,
     126        # otherwise, just eat any any syntax errors.
     127        except TemplateSyntaxError:
    111128            if settings.TEMPLATE_DEBUG:
    112129                raise
    113             return ''
     130        # Eat any other errors that may have occurred.
    114131        except:
    115             return '' # Fail silently for invalid included templates.
     132            pass
     133        return ''
    116134
    117135def do_block(parser, token):
    118136    """
     
    120138    """
    121139    bits = token.contents.split()
    122140    if len(bits) != 2:
    123         raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
     141        raise TemplateSyntaxError("'%s' tag takes only one argument" % bits[0])
    124142    block_name = bits[1]
    125     # Keep track of the names of BlockNodes found in this template, so we can
    126     # check for duplication.
     143    # Keep track of the BlockNodes found in this template,
     144    # so we can prevent duplication and reuse them.
    127145    try:
    128146        if block_name in parser.__loaded_blocks:
    129             raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
    130         parser.__loaded_blocks.append(block_name)
    131     except AttributeError: # parser.__loaded_blocks isn't a list yet
    132         parser.__loaded_blocks = [block_name]
     147            raise TemplateSyntaxError("'%s' tag with name '%s' appears more than once" % (bits[0], block_name))
     148    except AttributeError:
     149        # this is the first block, make __loaded_blocks dict
     150        parser.__loaded_blocks = {}
    133151    nodelist = parser.parse(('endblock', 'endblock %s' % block_name))
    134152    parser.delete_first_token()
    135     return BlockNode(block_name, nodelist)
     153    block_node = BlockNode(block_name, nodelist)
     154    parser.__loaded_blocks[block_name] = block_node
     155    return block_node
    136156
    137157def do_extends(parser, token):
    138158    """
     
    142162    uses the literal value "base" as the name of the parent template to extend,
    143163    or ``{% extends variable %}`` uses the value of ``variable`` as either the
    144164    name of the parent template to extend (if it evaluates to a string) or as
    145     the parent tempate itelf (if it evaluates to a Template object).
     165    the parent tempate itself (if it evaluates to a Template object).
    146166    """
     167    nodelist = parser.parse()
     168    # Make reasonably sure that there are no other extends tags.
     169    if nodelist.get_nodes_by_type(ExtendsNode):
     170        raise TemplateSyntaxError("'%s' tag cannot appear more than once in the same template" % bits[0])
     171
    147172    bits = token.contents.split()
    148173    if len(bits) != 2:
    149         raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
    150     parent_name, parent_name_expr = None, None
     174        raise TemplateSyntaxError("'%s' takes one argument" % bits[0])
    151175    if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
    152         parent_name = bits[1][1:-1]
     176        return ExtendsNode(nodelist, extended_name=bits[1][1:-1])
    153177    else:
    154         parent_name_expr = parser.compile_filter(bits[1])
    155     nodelist = parser.parse()
    156     if nodelist.get_nodes_by_type(ExtendsNode):
    157         raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
    158     return ExtendsNode(nodelist, parent_name, parent_name_expr)
     178        return ExtendsNode(nodelist, extended_name_expr=parser.compile_filter(bits[1]))
    159179
    160180def do_include(parser, token):
    161181    """
    162     Loads a template and renders it with the current context.
     182    Load a template and render it with the current context.
    163183
    164184    Example::
    165185
     
    167187    """
    168188    bits = token.contents.split()
    169189    if len(bits) != 2:
    170         raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
     190        raise TemplateSyntaxError("%r tag takes one argument: the name of the template to be included" % bits[0])
    171191    path = bits[1]
    172192    if path[0] in ('"', "'") and path[-1] == path[0]:
    173193        return ConstantIncludeNode(path[1:-1])
Back to Top