diff options
author | R. Tyler Ballance <tyler@monkeypox.org> | 2009-11-16 20:58:13 -0800 |
---|---|---|
committer | R. Tyler Ballance <tyler@monkeypox.org> | 2009-11-16 21:10:04 -0800 |
commit | 7c8e1d15feebb97d27d651c539bea9becdbfc15c (patch) | |
tree | ec3dc2de79650238cb203c1a42db709ecf4a1a7e | |
parent | e43765a679b84c52df875e9629d303e304af50a1 (diff) | |
download | python-cheetah-7c8e1d15feebb97d27d651c539bea9becdbfc15c.tar.gz |
Introduce the DirectiveAnalyzer for processing templates for directive usage
Hoping to form this into a fully-fledged reporting tool so I can gauge
usage of directives to start cutting some out.
-rw-r--r-- | cheetah/DirectiveAnalyzer.py | 97 | ||||
-rw-r--r-- | cheetah/Parser.py | 15 | ||||
-rw-r--r-- | cheetah/SourceReader.py | 15 | ||||
-rw-r--r-- | cheetah/Template.py | 4 | ||||
-rw-r--r-- | cheetah/Tests/Analyzer.py | 29 |
5 files changed, 140 insertions, 20 deletions
diff --git a/cheetah/DirectiveAnalyzer.py b/cheetah/DirectiveAnalyzer.py new file mode 100644 index 0000000..b297f67 --- /dev/null +++ b/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() + diff --git a/cheetah/Parser.py b/cheetah/Parser.py index 870c08a..4390705 100644 --- a/cheetah/Parser.py +++ b/cheetah/Parser.py @@ -302,11 +302,14 @@ class ParseError(ValueError): 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 @@ -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() @@ -1342,7 +1345,7 @@ class _HighLevelParser(_LowLevelParser): self._macroDetails.clear() def configureParser(self): - _LowLevelParser.configureParser(self) + super(_HighLevelParser, self).configureParser() self._initDirectives() def _initDirectives(self): diff --git a/cheetah/SourceReader.py b/cheetah/SourceReader.py index a1537f6..7a08837 100644 --- a/cheetah/SourceReader.py +++ b/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 @@ -23,7 +10,7 @@ ENCODINGsearch = re.compile("coding[=:]\s*([-\w.]+)").search 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 diff --git a/cheetah/Template.py b/cheetah/Template.py index 430a863..22d98f0 100644 --- a/cheetah/Template.py +++ b/cheetah/Template.py @@ -723,6 +723,7 @@ class Template(Servlet): #@@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 @@ -818,6 +819,9 @@ class Template(Servlet): 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 diff --git a/cheetah/Tests/Analyzer.py b/cheetah/Tests/Analyzer.py new file mode 100644 index 0000000..59f6c1c --- /dev/null +++ b/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() + |