Skip to content

Instantly share code, notes, and snippets.

@starenka
Created August 13, 2012 20:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save starenka/3344003 to your computer and use it in GitHub Desktop.
Save starenka/3344003 to your computer and use it in GitHub Desktop.
Walks through django template directory and generates dot file you can use to generate template inheritance/block usage tree with GraphViz. http://junk.starenka.net/columbosvg.png
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Walks through django template directory and generates dot file
you can use to generate template inheritance/block usage tree
with GraphViz.
application/templates and custom template loaders are not (yet?) supported
"""
import os, sys, re
def make_dot_file(template_dir, parsed_tags, graph_settings, extensions='html'):
RE_BLOCK = re.compile(
r'{%%\s+(?P<tag>%s)\s+[\'"]?(?P<name>[/\.\-_a-zA-Z0-9]+)[\'"]?\s+%%}' % '|'.join(parsed_tags))
graph_settings = graph_settings or '''
ranksep=.2
node [shape="record" color="gray" fontname="sans-serif" fontsize=8]
edge [arrowhead="normal"]
'''
out = ['''
digraph TemplateViz {
%s
''' % graph_settings]
template_dir = template_dir if template_dir.endswith(os.sep) else '%s%s' % (template_dir, os.sep)
for dirname, dirnames, filenames in os.walk(template_dir):
for filename in filenames:
if not filename.endswith(extensions):
continue
file = os.path.join(dirname, filename)
short = file.replace(template_dir, '').replace('\\', '/')
with open(file, 'r') as f:
tags = dict((one, []) for one in parsed_tags)
for one in re.finditer(RE_BLOCK, f.read()):
tags[one.group('tag')].append(one.group('name'))
out.append('\t"%(template)s" [label = "{%(template)s%(blocks)s%(includes)s\l}"];' % {
'template': short,
'blocks': '|%s' % '\l'.join(tags['block']) if tags.get('block', []) else '',
'includes': '|%s' % '\l'.join(tags['include']) if tags.get('include', []) else '',
})
for one in tags.get('extends', []):
out.append('\t"%(template)s" -> "%(extends)s";' % {'template': short, 'extends': one})
for one in tags.get('include', []):
out.append('\t"%(include)s" [label="%(include)s"] [color="green"];' % {'include': one})
out.append('\t"%(include)s" -> "%(template)s";' % {'template': short, 'include': one})
for one in tags.get('includeblocks', []):
out.append('\t"%(include)s" [label="%(include)s"] [color="yellow"];' % {'include': one})
out.append('\t"%(include)s" -> "%(template)s";' % {'template': short, 'include': one})
out.append('}')
return '\n'.join(out)
if __name__ == '__main__':
ALL_TAGS = ('block', 'extends', 'include', 'includeblocks',)
from optparse import OptionParser
usage = 'USAGE: %s /path/to/template/dir [-x tpl] [-e block,include] [-s "graph_setting 1; graph_settings2 2;"] > out.dot; dot -Tsvg -Kdot out.dot > out.svg' % __file__
parser = OptionParser(usage)
parser.add_option('-x', '--template-extension', action='store', dest='extension', default='html',
help='template file extension')
parser.add_option('-e', '--exclude-tags', action='store', dest='exclude_tags', default='', help='excluded tags')
parser.add_option('-s', '--graph_settings', action='store', dest='graph_settings', default=None, help='graph settings')
(options, args) = parser.parse_args()
if len(sys.argv) < 2:
sys.exit(usage)
TAGS = set(ALL_TAGS) - set(map(lambda x: x.strip(), options.exclude_tags.split(',')))
print make_dot_file(sys.argv[1], parsed_tags=TAGS, extensions=options.extension, graph_settings=options.graph_settings)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment