summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorR. Tyler Ballance <tyler@monkeypox.org>2009-11-16 20:58:13 -0800
committerR. Tyler Ballance <tyler@monkeypox.org>2009-11-16 21:10:04 -0800
commit7c8e1d15feebb97d27d651c539bea9becdbfc15c (patch)
treeec3dc2de79650238cb203c1a42db709ecf4a1a7e
parente43765a679b84c52df875e9629d303e304af50a1 (diff)
downloadpython-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.py97
-rw-r--r--cheetah/Parser.py15
-rw-r--r--cheetah/SourceReader.py15
-rw-r--r--cheetah/Template.py4
-rw-r--r--cheetah/Tests/Analyzer.py29
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()
+