Django

Code

Changeset 796

Show
Ignore:
Timestamp:
10/06/05 20:13:32 (3 years ago)
Author:
rjwittams
Message:

Template fixes for inline tabular to let errors show up properly
Typo fix for extends tag doc

Hugely improved messages for template syntax errors.
Tokens and nodes are tagged with file and line numbers.
These are used in error messages.

Example:

TemplateSyntaxError?: Unclosed tag 'if' starting at /usr/lib/python2.4/site-packages/django/conf/admin_templates/admin_submit_line.html, line 6. Looking for one of: else, endif

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/new-admin/django/conf/admin_templates/admin_edit_inline_tabular.html

    r740 r796  
    2222      {% for bound_field in fcw.bound_fields %} 
    2323         {% if not bound_field.not_in_table %} 
    24          <td "{{ bound_field.cell_class_attribute}}">  
     24         <td {{bound_field.cell_class_attribute}}>  
    2525            {% field_widget bound_field %} 
    2626         </td> 
  • django/branches/new-admin/django/conf/admin_templates/admin_submit_line.html

    r741 r796  
    11<div class="submit-row"> 
    2     {% if show_delete_link %}<p class="float-left"><a href="delete/" class="deletelink">Delete</a></p>{% endif %} 
    3     {% if show_save_as_new %}<input type="submit" value="Save as new" name="_saveasnew" {{onclick_attrib}}/>{%endif%} 
    4     {% if show_save_and_add_another %}<input type="submit" value="Save and add another" name="_addanother" {{onclick_attrib}} />{% endif %} 
    5     {% if show_save_and_continue %}<input type="submit" value="Save and continue editing" name="_continue" {{onclick_attrib}}/>{% endif %} 
    6     {% if show_save %}<input type="submit" value="Save" class="default" {{onclick_attrib}}/>{% endif %} 
     2{% if show_delete_link %}<p class="float-left"><a href="delete/" class="deletelink">Delete</a></p>{% endif %} 
     3{% if show_save_as_new %}<input type="submit" value="Save as new" name="_saveasnew" {{onclick_attrib}}/>{%endif%} 
     4{% if show_save_and_add_another %}<input type="submit" value="Save and add another" name="_addanother" {{onclick_attrib}} />{% endif %} 
     5{% if show_save_and_continue %}<input type="submit" value="Save and continue editing" name="_continue" {{onclick_attrib}}/>{% endif %} 
     6{% if show_save %}<input type="submit" value="Save" class="default" {{onclick_attrib}}/> 
    77</div> 
  • django/branches/new-admin/django/core/defaulttags.py

    r780 r796  
    231231class ConstantIncludeNode(template.Node): 
    232232    def __init__(self, template_path): 
    233         t = template_loader.get_template(template_path)         
    234         self.nodelist = t.nodelist 
    235  
    236     def render(self, context): 
    237         return self.nodelist.render(context) 
     233        try:  
     234            t = template_loader.get_template(template_path)         
     235            self.nodelist = t.nodelist 
     236        except Exception, e: 
     237            self.nodelist = None 
     238 
     239    def render(self, context): 
     240        if self.nodelist: 
     241            return self.nodelist.render(context) 
     242        else: 
     243            return '' 
    238244 
    239245class IncludeNode(template.Node): 
     
    248254             return t.render(context) 
    249255         except Exception, e: 
    250              print e 
    251256             return '' # Fail silently for invalid included templates. 
    252257 
  • django/branches/new-admin/django/core/template_file.py

    r721 r796  
    55import os 
    66 
    7 def load_template_source(template_name, template_dirs=None): 
     7 
     8def find_template_source(template_name, template_dirs=None): 
     9    "Returns a tuple of (template_string, filepath)." 
    810    if not template_dirs: 
    911        template_dirs = TEMPLATE_DIRS 
     
    1214        filepath = os.path.join(template_dir, template_name) + TEMPLATE_FILE_EXTENSION 
    1315        try: 
    14             return open(filepath).read(
     16            return (open(filepath).read(), filepath
    1517        except IOError: 
    1618            tried.append(filepath) 
     
    2022        error_msg = "Your TEMPLATE_DIRS settings is empty. Change it to point to at least one template directory." 
    2123    raise TemplateDoesNotExist, error_msg 
     24 
     25 
     26def load_template_source(template_name, template_dirs=None): 
     27    return find_template_source(template_name, template_dirs)[0] 
  • django/branches/new-admin/django/core/template_loader.py

    r717 r796  
    11"Wrapper for loading templates from storage of some sort (e.g. files or db)" 
    22import template 
    3 from template_file import load_template_source 
     3from template_file import find_template_source 
    44 
    55class ExtendsError(Exception): 
     
    1111    handling template inheritance recursively. 
    1212    """ 
    13     return get_template_from_string(load_template_source(template_name)) 
     13    return get_template_from_string(*find_template_source(template_name)) 
    1414 
    15 def get_template_from_string(source): 
     15def get_template_from_string(source, filename=template.UNKNOWN_SOURCE): 
    1616    """ 
    1717    Returns a compiled template.Template object for the given template code, 
    1818    handling template inheritance recursively. 
    1919    """ 
    20     return template.Template(source
     20    return template.Template(source, filename
    2121 
    2222def render_to_string(template_name, dictionary=None, context_instance=None): 
     
    9191            raise template.TemplateSyntaxError, error_msg 
    9292        try: 
    93             return get_template_from_string(load_template_source(parent, self.template_dirs)) 
     93            return get_template_from_string(*find_template_source(parent, self.template_dirs)) 
    9494        except template.TemplateDoesNotExist: 
    9595            raise template.TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent 
     
    143143    This tag may be used in two ways: ``{% extends "base" %}`` (with quotes) 
    144144    uses the literal value "base" as the name of the parent template to extend, 
    145     or ``{% entends variable %}`` uses the value of ``variable`` as the name 
     145    or ``{% extends variable %}`` uses the value of ``variable`` as the name 
    146146    of the parent template to extend. 
    147147    """ 
  • django/branches/new-admin/django/core/template.py

    r791 r796  
    7575ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.' 
    7676 
     77#What to report as the origin of templates that come from non file sources (eg strings) 
     78UNKNOWN_SOURCE="<unknown source>" 
     79 
     80 
     81#match starts of lines 
     82newline_re = re.compile("^", re.M); 
     83 
    7784# match a variable or block tag and capture the entire tag, including start/end delimiters 
    7885tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), 
    7986                                          re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END))) 
    8087 
     88 
    8189# global dict used by register_tag; maps custom tags to callback functions 
    8290registered_tags = {} 
     
    103111 
    104112class Template: 
    105     def __init__(self, template_string): 
     113    def __init__(self, template_string, filename=UNKNOWN_SOURCE): 
    106114        "Compilation stage" 
    107         self.nodelist = compile_string(template_string
     115        self.nodelist = compile_string(template_string, filename
    108116 
    109117    def __iter__(self): 
     
    116124        return self.nodelist.render(context) 
    117125 
    118 def compile_string(template_string): 
     126def compile_string(template_string, filename): 
    119127    "Compiles template_string into NodeList ready for rendering" 
    120     tokens = tokenize(template_string
     128    tokens = tokenize(template_string, filename
    121129    parser = Parser(tokens) 
    122130    return parser.parse() 
     
    169177 
    170178class Token: 
    171     def __init__(self, token_type, contents): 
     179    def __init__(self, token_type, contents, source): 
    172180        "The token_type must be TOKEN_TEXT, TOKEN_VAR or TOKEN_BLOCK" 
    173181        self.token_type, self.contents = token_type, contents 
     182        self.source = source 
    174183 
    175184    def __str__(self): 
    176         return '<%s token: "%s...">' % ( 
     185        return '<%s token: "%s..." from %s, line %d>' % ( 
    177186            {TOKEN_TEXT:'Text', TOKEN_VAR:'Var', TOKEN_BLOCK:'Block'}[self.token_type], 
    178             self.contents[:20].replace('\n', '') 
     187            self.contents[:20].replace('\n', ''),  
     188            self.source[0], self.source[1] 
    179189            ) 
    180190 
    181 def tokenize(template_string): 
     191 
     192def tokenize(template_string, filename): 
    182193    "Return a list of tokens from a given template_string" 
    183194    # remove all empty strings, because the regex has a tendency to add them 
    184     bits = filter(None, tag_re.split(template_string)) 
    185     return map(create_token, bits) 
    186  
    187 def create_token(token_string): 
     195    linebreaks = [match.start() for match in newline_re.finditer(template_string)] 
     196    lastline = len(linebreaks) 
     197    token_tups = [] 
     198    upto = 0 
     199    line = 1 
     200     
     201    
     202    for match in tag_re.finditer(template_string): 
     203        start, end = match.span() 
     204        if start > upto: 
     205            token_tups.append( (template_string[upto:start], line) ) 
     206            upto = start 
     207            while linebreaks and line != lastline and linebreaks[line] <= upto: 
     208                line += 1 
     209         
     210        token_tups.append( (template_string[start:end], line) ) 
     211        upto = end 
     212         
     213        while linebreaks and line != lastline and linebreaks[line] <= upto: 
     214           line += 1 
     215  
     216    return [ create_token(tok, (filename, line)) for tok, line in token_tups] 
     217 
     218def create_token(token_string, source): 
    188219    "Convert the given token string into a new Token object and return it" 
    189220    if token_string.startswith(VARIABLE_TAG_START): 
    190         return Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip()
     221        return Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip(), source
    191222    elif token_string.startswith(BLOCK_TAG_START): 
    192         return Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip()
     223        return Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip(), source
    193224    else: 
    194         return Token(TOKEN_TEXT, token_string) 
     225        return Token(TOKEN_TEXT, token_string, source) 
     226 
    195227 
    196228class Parser: 
    197229    def __init__(self, tokens): 
    198230        self.tokens = tokens 
     231        self.command_stack = [] 
    199232 
    200233    def parse(self, parse_until=[]): 
     
    203236            token = self.next_token() 
    204237            if token.token_type == TOKEN_TEXT: 
    205                 nodelist.append(TextNode(token.contents)
     238                nodelist.append(TextNode(token.contents), token
    206239            elif token.token_type == TOKEN_VAR: 
    207240                if not token.contents: 
    208                     raise TemplateSyntaxError, "Empty variable tag" 
    209                 nodelist.append(VariableNode(token.contents)
     241                    raise TemplateSyntaxError, "Empty variable tag at %s, line %d" % (token.source[0], token.source[1]) 
     242                nodelist.append(VariableNode(token.contents), token
    210243            elif token.token_type == TOKEN_BLOCK: 
    211244                if token.contents in parse_until: 
     
    219252                try: 
    220253                    # execute callback function for this tag and append resulting node 
    221                     nodelist.append(registered_tags[command](self, token)) 
     254                    self.command_stack.append( (command, token.source) ) 
     255                    nodelist.append(registered_tags[command](self, token), token) 
     256                    self.command_stack.pop() 
    222257                except KeyError: 
    223                     raise TemplateSyntaxError, "Invalid block tag: '%s'" % command 
     258                    raise TemplateSyntaxError, "Invalid block tag: '%s' at %s, line %d" % (command, token.source[0], token.source[1]) 
    224259        if parse_until: 
    225             raise TemplateSyntaxError, "Unclosed tag(s): '%s'" % ', '.join(parse_until) 
     260            (command, (file,line)) = self.command_stack.pop() 
     261            msg = "Unclosed tag '%s' starting at %s, line %d. Looking for one of: %s " % \ 
     262                  (command, file, line, ', '.join(parse_until) )  
     263            raise TemplateSyntaxError, msg 
    226264        return nodelist 
    227265 
     
    435473            nodes.extend(self.nodelist.get_nodes_by_type(nodetype)) 
    436474        return nodes 
     475     
    437476 
    438477class NodeList(list): 
     
    452491            nodes.extend(node.get_nodes_by_type(nodetype)) 
    453492        return nodes 
     493     
     494    def append(self, node, token = None): 
     495        if token: 
     496            node.source = token.source 
     497        super(NodeList, self).append(node) 
    454498 
    455499class TextNode(Node): 
  • django/branches/new-admin/django/views/admin/main.py

    r791 r796  
    574574        if max([bool(f.errors()) for f in self.form_fields]): 
    575575            classes.append('error') 
    576         self.cell_class_attribute = ' '.join(classes)    
     576        if classes: 
     577            self.cell_class_attribute = ' class="%s" ' % ' '.join(classes) 
    577578        self._repr_filled = False 
    578579