Skip to content
This repository has been archived by the owner on Mar 8, 2018. It is now read-only.

Commit

Permalink
Introduce the DirectiveAnalyzer for processing templates for directiv…
Browse files Browse the repository at this point in the history
…e usage

Hoping to form this into a fully-fledged reporting tool so I can gauge
usage of directives to start cutting some out.
  • Loading branch information
R. Tyler Ballance committed Nov 17, 2009
1 parent e43765a commit 7c8e1d1
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 20 deletions.
97 changes: 97 additions & 0 deletions cheetah/DirectiveAnalyzer.py
@@ -0,0 +1,97 @@
#!/usr/bin/env python

import os
import pprint

try:
from functools import reduce
except ImportError:
# Assume we have reduce
pass

from Cheetah import Parser
from Cheetah import Compiler
from Cheetah import Template

class Analyzer(Parser.Parser):
def __init__(self, *args, **kwargs):
self.calls = {}
super(Analyzer, self).__init__(*args, **kwargs)

def eatDirective(self):
directive = self.matchDirective()
try:
self.calls[directive] += 1
except KeyError:
self.calls[directive] = 1
super(Analyzer, self).eatDirective()

class AnalysisCompiler(Compiler.ModuleCompiler):
parserClass = Analyzer


def analyze(source):
klass = Template.Template.compile(source, compilerClass=AnalysisCompiler)
return klass._CHEETAH_compilerClass._parser.calls

def main_file(f):
fd = open(f, 'r')
try:
calls = analyze(fd.read())
return calls
finally:
fd.close()


def _find_templates(directory, suffix):
for root, dirs, files in os.walk(directory):
for f in files:
if not f.endswith(suffix):
continue
yield root + os.path.sep + f

def _analyze_templates(iterable):
for template in iterable:
yield main_file(template)

def main_dir(opts):
results = _analyze_templates(_find_templates(opts.dir, opts.suffix))
totals = {}
for series in results:
if not series:
continue
for k, v in series.iteritems():
try:
totals[k] += v
except KeyError:
totals[k] = v
return totals


def main():
from optparse import OptionParser
op = OptionParser()
op.add_option('-f', '--file', dest='file', default=None,
help='Specify a single file to analyze')
op.add_option('-d', '--dir', dest='dir', default=None,
help='Specify a directory of templates to analyze')
op.add_option('--suffix', default='tmpl', dest='suffix',
help='Specify a custom template file suffix for the -d option (default: "tmpl")')
opts, args = op.parse_args()

if not opts.file and not opts.dir:
op.print_help()
return

results = None
if opts.file:
results = main_file(opts.file)
if opts.dir:
results = main_dir(opts)

pprint.pprint(results)


if __name__ == '__main__':
main()

15 changes: 9 additions & 6 deletions cheetah/Parser.py
Expand Up @@ -302,11 +302,14 @@ def report(self):

return report

class ForbiddenSyntax(ParseError): pass
class ForbiddenExpression(ForbiddenSyntax): pass
class ForbiddenDirective(ForbiddenSyntax): pass
class ForbiddenSyntax(ParseError):
pass
class ForbiddenExpression(ForbiddenSyntax):
pass
class ForbiddenDirective(ForbiddenSyntax):
pass

class CheetahVariable:
class CheetahVariable(object):
def __init__(self, nameChunks, useNameMapper=True, cacheToken=None,
rawSource=None):
self.nameChunks = nameChunks
Expand Down Expand Up @@ -1321,7 +1324,7 @@ class _HighLevelParser(_LowLevelParser):
Cheetah.Compiler.Compiler.
"""
def __init__(self, src, filename=None, breakPoint=None, compiler=None):
_LowLevelParser.__init__(self, src, filename=filename, breakPoint=breakPoint)
super(_HighLevelParser, self).__init__(src, filename=filename, breakPoint=breakPoint)
self.setSettingsManager(compiler)
self._compiler = compiler
self.setupState()
Expand All @@ -1342,7 +1345,7 @@ def cleanup(self):
self._macroDetails.clear()

def configureParser(self):
_LowLevelParser.configureParser(self)
super(_HighLevelParser, self).configureParser()
self._initDirectives()

def _initDirectives(self):
Expand Down
15 changes: 1 addition & 14 deletions cheetah/SourceReader.py
@@ -1,18 +1,5 @@
# $Id: SourceReader.py,v 1.15 2007/04/03 01:57:42 tavis_rudd Exp $
"""SourceReader class for Cheetah's Parser and CodeGenerator
Meta-Data
================================================================================
Author: Tavis Rudd <tavis@damnsimple.com>
License: This software is released for unlimited distribution under the
terms of the MIT license. See the LICENSE file.
Version: $Revision: 1.15 $
Start Date: 2001/09/19
Last Revision Date: $Date: 2007/04/03 01:57:42 $
"""
__author__ = "Tavis Rudd <tavis@damnsimple.com>"
__revision__ = "$Revision: 1.15 $"[11:-2]

import re
import sys

Expand All @@ -23,7 +10,7 @@
class Error(Exception):
pass

class SourceReader:
class SourceReader(object):
def __init__(self, src, filename=None, breakPoint=None, encoding=None):

## @@TR 2005-01-17: the following comes from a patch Terrel Shumway
Expand Down
4 changes: 4 additions & 0 deletions cheetah/Template.py
Expand Up @@ -723,6 +723,7 @@ def __str__(self): return self.respond()
#@@TR: should add some logging to this
pass
outputEncoding = 'ascii'
compiler = None
if useCache and cacheHash and cacheHash in klass._CHEETAH_compileCache:
cacheItem = klass._CHEETAH_compileCache[cacheHash]
generatedModuleCode = cacheItem.code
Expand Down Expand Up @@ -818,6 +819,9 @@ def __str__(self): return self.respond()
if keepRefToGeneratedCode or cacheCompilationResults:
templateClass._CHEETAH_generatedModuleCode = generatedModuleCode

# If we have a compiler object, let's set it to the compiler class
# to help the directive analyzer code
templateClass._CHEETAH_compilerClass = compiler or templateClass._CHEETAH_compilerClass
return templateClass

@classmethod
Expand Down
29 changes: 29 additions & 0 deletions cheetah/Tests/Analyzer.py
@@ -0,0 +1,29 @@
#!/usr/bin/env python

import unittest

from Cheetah import DirectiveAnalyzer


class AnalyzerTests(unittest.TestCase):
def test_set(self):
template = '''
#set $foo = "bar"
Hello ${foo}!
'''
calls = DirectiveAnalyzer.analyze(template)
self.assertEquals(1, calls.get('set'))

def test_compilersettings(self):
template = '''
#compiler-settings
useNameMapper = False
#end compiler-settings
'''
calls = DirectiveAnalyzer.analyze(template)
self.assertEquals(1, calls.get('compiler-settings'))


if __name__ == '__main__':
unittest.main()

0 comments on commit 7c8e1d1

Please sign in to comment.