Edgewall Software

Jinjachecker

Jinja2 is very helpful when it detects any kind of error, as you always end up with a meaningful backtrace. Nevertheless, it can be tedious to get the nesting of control structures right. To help with that problem, we wrote the contrib/jinjachecker.py tool.

It first analyzes the Jinja2 control structures, and tries to provide some helpful diagnostics in case of nesting or stylistic errors. It also adds curly braces to the statements, so if you have an editor which can do matching of brace pairs, you can quickly spot the origin of a nesting problem.

Finally, while the indentation of the statements is free in Jinja2 templates, being consistent with it also helps to ensure a proper nesting.

Example

The command:

python contrib/jinjachecker.py trac/wiki/templates/wiki_view.html

outputs:

# -- Jinja2 check for 'trac/wiki/templates/wiki_view.html'
   12    EXTENDS "jlayout.html"
   18          {BLOCK title
   19            {IF title:
   21            }IF
   22          }BLOCK title
   25        {BLOCK head
   26          SET modify_perm = 'WIKI_MODIFY' in perm(page.resource)
   27          SET is_not_latest = page.exists and page.version != latest_version
   31          {IF version or page.author == 'trac':
   33          }IF
   34          {IF modify_perm:
   40          }IF
   52        }BLOCK head
   56        {BLOCK content
   57          SET modify_perm = 'WIKI_MODIFY' in perm(page.resource)
   58          SET create_perm = 'WIKI_CREATE' in perm(page.resource)
   59          SET admin_perm = 'WIKI_ADMIN' in perm(page.resource)
   60          SET is_not_latest = page.exists and page.version != latest_version
   63          {IF version:
   67                {WITH
   68                  SET version = page.version
   69                  SET author = authorinfo(page.author)
   70                  SET date = pretty_dateinfo(page.time)
   71                  SET hef = href.wiki(page.name, action='diff', version=page.version)
   72                  {TRANS version, author, date, href
   77                  }TRANS
   78                }WITH
   84          }IF
   87            {IF page.exists:
   91              SET last_modification = (page.comment and
   97              {IF not version:
  100                {WITH
  101                  SET href = href.wiki(page.name, action='diff',
  103                  SET date = pretty_dateinfo(page.time)
  104                  {TRANS href, last_modification, date
  109                  }TRANS
  110                }WITH
  116              }IF
  117            ELSE:
  119              {TRANS name = name_of(page.resource)
  124              }TRANS
  126            }IF
  129          {WITH
  130            SET alist = attachments
  131            SET compact = True
  132            SET foldable = True
  133            INCLUDE "jlist_of_attachments.html"
  134          }WITH
  136          {WITH
  137            SET delete_perm = 'WIKI_DELETE' in perm(page.resource)
  138            SET rename_perm = 'WIKI_RENAME' in perm(page.resource)
  139            {IF modify_perm or create_perm or delete_perm:
  141              {IF modify_perm or create_perm:
  145                {IF is_not_latest and modify_perm:
  148                ELIF page.exists and modify_perm:
  151                ELIF not page.exists and create_perm:
  154                  {IF templates:
  161                    {FOR t in sorted(templates):
  165                    }FOR
  168                  }IF
  169                }IF
  173              {IF page.exists:
  174                {WITH alist = attachments
  175                  INCLUDE "jattach_file_form.html"
  176                }WITH
  177              }IF
  178            }IF
  180            {IF page.exists and rename_perm:
  187            }IF
  188            {IF page.exists and delete_perm:
  193                {IF page.version == latest_version:
  195                }IF
  199            }IF
  201          }IF
  202          }WITH
  204          {IF not page.exists and higher:
  208              {FOR markup in higher:
  210              }FOR
  213          }IF
  215          {IF not page.exists and related:
  219              {FOR markup in related:
  221              }FOR
  224          }IF
  230        }BLOCK content
# -- Jinja2 OK

and:

# -- HTML check for 'trac/wiki/templates/jwiki_view.html'
    1 
...
   13 
   14 <!DOCTYPE html>
   15 <html>
   16   <head>
   17     <title>
   18 
   19 
   20       ${title} ${ super() }
   21 
   22 
   23     </title>
   24 
   25 
   26 
   27 
   28 
   29     ${ super() }
   30 
...
   52 
   53   </head>
   54 
   55   <body>
   56 
   57 
   58 
   59 
   60 
   61     <div id="content" class="${classes('wiki', create=not page.exists)}">
   62 
...
  213 
  214 
  215     </div>
  216 
  217     ${ super() }
  218 
  219 
  220   </body>
  221 </html>
# -- HTML OK

The second part of this output is only generated if you have lxml installed. If that's the case, after having checked the Jinja2 syntax nesting in a first step, the jinjachecker tool removes the Jinja2 markup and performs a validation of the document using lxml in a second step.

Known Issues

The tool won't yet detect nesting mistakes when the Jinja2 control structure nesting and the HTML element nesting are both independently correct (e.g. errors like the one fixed in [68094c2ea/cboos.git] or r15508). This might be fixed in a 3rd pass, doing XML well-formedness check after having replaced the Jinja2 start/end keywords with "equivalent" XML elements.

Last modified 8 years ago Last modified on Jun 18, 2017, 6:52:02 PM
Note: See TracWiki for help on using the wiki.