Created
October 20, 2014 18:07
-
-
Save augustomen/e84eaf10f7184aff1074 to your computer and use it in GitHub Desktop.
templatetree - builds a inheritance tree of all templates in a Django project or folder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: utf-8 | |
__author__ = "Augusto Men" | |
__version__ = "$Revision: 0.1 $" | |
__date__ = "$Date: 2014/09/29 $" | |
DESCRIPTION = """Builds a tree of template files based on inheritance.""" | |
import os | |
import re | |
import sys | |
import argparse | |
from fnmatch import fnmatch | |
QUOTED = re.compile(r'^[\'\"](.*?)[\"\']$') | |
def unquote(s): | |
"""Returns the inner string if s is between quotes, or otherwise None.""" | |
match = QUOTED.match(s) | |
if match: | |
return match.group(1) | |
class TemplateItem(object): | |
PATTERN = re.compile(r'{% extends ([^%]+)%}') | |
TEMPLATE_SUBDIR = re.compile(r'templates/(.*)') | |
def __init__(self, filename): | |
# set the standard | |
self.filename = filename | |
self.children = [] | |
self.template_name = None | |
self.extends = None | |
self.extends_var = None | |
match = self.TEMPLATE_SUBDIR.search(filename.replace('\\', '/')) | |
if match: | |
self.template_name = match.group(1) | |
self.read() | |
def __repr__(self): | |
return 'TemplateItem(%s)' % self.filename | |
def read(self): | |
with open(self.filename, 'r') as f: | |
text = f.read(256) | |
match = self.PATTERN.search(text) | |
if match: | |
extends = match.group(1).strip() | |
template_name = unquote(extends) | |
if template_name: | |
self.extends = template_name | |
else: | |
self.extends_var = extends | |
def scan_path(path, mask='*'): | |
"""An iterable to the matched files (relative path).""" | |
for path, folders, files in os.walk(path): | |
for filename in files: | |
if fnmatch(filename, mask): | |
yield os.path.join(path, filename) | |
for folder in folders: | |
for filename in scan_path(os.path.join(path, folder), mask): | |
yield filename | |
def process_path(path, mask=None): | |
"""Locates template files and organizes them into a tree.""" | |
mask = mask or '*.html' | |
templates = {} | |
for filename in scan_path(path, mask): | |
item = TemplateItem(filename) | |
if item.template_name: | |
templates[item.template_name] = item | |
roots = [] | |
variables = [] | |
# Organize the templates in a tree | |
for template_name, item in templates.iteritems(): | |
if item.extends_var: | |
variables.append(item) | |
elif item.extends in templates: | |
templates[item.extends].children.append(item) | |
else: | |
roots.append(item) | |
roots = sorted(roots, key=lambda i: ( | |
i.extends or '', i.template_name or '')) | |
variables = sorted(variables, key=lambda i: ( | |
i.extends_var or '', i.template_name or '')) | |
return roots, variables | |
def write_output(output, roots, variables=None, level=0): | |
"""Writes the output in HTML list-style. | |
roots and variables must be a list of TemplateItem. | |
""" | |
if roots or variables: | |
output.write('%s<ul>\n' % (' ' * level)) | |
level += 1 | |
for item in roots: | |
output.write('%s<li>%s</li>\n' % (' ' * level, item.template_name)) | |
write_output(output, item.children, level=level + 1) | |
if variables: | |
output.write('<li>Templates descending from variables:</li>\n') | |
output.write('%s<ul>\n' % (' ' * level)) | |
level += 1 | |
for item in variables: | |
output.write('%s<li>%s = %s</li>\n' % ( | |
' ' * level, item.extends_var, item.template_name)) | |
level -= 1 | |
output.write('%s</ul>\n' % (' ' * level)) | |
level -= 1 | |
output.write('%s</ul>\n' % (' ' * level)) | |
def main(argv=[]): | |
parser = argparse.ArgumentParser(description=DESCRIPTION) | |
parser.add_argument( | |
'path', default='', | |
help="Path to be scanned. If not informed, current will be used.") | |
parser.add_argument( | |
'--mask', default='*.html', | |
help="HTML template mask. Default: *.html") | |
parser.add_argument( | |
'--output', metavar='FILENAME', default='', | |
help="Output file. Default: console") | |
args = parser.parse_args(argv) | |
path = args.path | |
if not path: | |
path = os.getcwd() | |
sys.stderr.write("Scanning...\n") | |
roots, variables = process_path(path, args.mask) | |
sys.stderr.write("Writing output...\n") | |
if args.output: | |
output = open(args.output, 'w') | |
else: | |
output = sys.stdout | |
write_output(output, roots, variables) | |
if __name__ == "__main__": | |
main(sys.argv[1:]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment