Merge lp:~barry/ubuntu/natty/cheetah/py27-compat into lp:ubuntu/natty/cheetah

Proposed by Barry Warsaw
Status: Merged
Merge reported by: Barry Warsaw
Merged at revision: not available
Proposed branch: lp:~barry/ubuntu/natty/cheetah/py27-compat
Merge into: lp:ubuntu/natty/cheetah
Diff against target: 43600 lines (+19910/-22763)
152 files modified
CHANGES (+91/-3)
Cheetah.egg-info/PKG-INFO (+47/-0)
Cheetah.egg-info/SOURCES.txt (+80/-0)
Cheetah.egg-info/dependency_links.txt (+1/-0)
Cheetah.egg-info/requires.txt (+1/-0)
Cheetah.egg-info/top_level.txt (+1/-0)
MANIFEST.in (+3/-3)
PKG-INFO (+7/-864)
README (+0/-51)
README.markdown (+51/-0)
SetupConfig.py (+44/-61)
SetupTools.py (+60/-29)
TODO (+6/-14)
bin/cheetah (+2/-2)
bin/cheetah-analyze (+6/-0)
bin/cheetah-compile (+2/-4)
cachedCompile.py (+0/-11)
cheetah-mem.py (+0/-22)
cheetah/CacheRegion.py (+136/-0)
cheetah/CacheStore.py (+106/-0)
cheetah/CheetahWrapper.py (+632/-0)
cheetah/Compiler.py (+2002/-0)
cheetah/DirectiveAnalyzer.py (+98/-0)
cheetah/Django.py (+16/-0)
cheetah/DummyTransaction.py (+108/-0)
cheetah/ErrorCatchers.py (+62/-0)
cheetah/FileUtils.py (+357/-0)
cheetah/Filters.py (+212/-0)
cheetah/ImportHooks.py (+129/-0)
cheetah/ImportManager.py (+541/-0)
cheetah/Macros/I18n.py (+67/-0)
cheetah/Macros/__init__.py (+1/-0)
cheetah/NameMapper.py (+366/-0)
cheetah/Parser.py (+2661/-0)
cheetah/Servlet.py (+48/-0)
cheetah/SettingsManager.py (+284/-0)
cheetah/SourceReader.py (+267/-0)
cheetah/Template.py (+1941/-0)
cheetah/TemplateCmdLineIface.py (+107/-0)
cheetah/Templates/SkeletonPage.py (+272/-0)
cheetah/Templates/SkeletonPage.tmpl (+44/-0)
cheetah/Templates/_SkeletonPage.py (+215/-0)
cheetah/Templates/__init__.py (+1/-0)
cheetah/Tests/Analyzer.py (+29/-0)
cheetah/Tests/CheetahWrapper.py (+579/-0)
cheetah/Tests/Cheps.py (+39/-0)
cheetah/Tests/Filters.py (+70/-0)
cheetah/Tests/Misc.py (+20/-0)
cheetah/Tests/NameMapper.py (+548/-0)
cheetah/Tests/Parser.py (+49/-0)
cheetah/Tests/Performance.py (+243/-0)
cheetah/Tests/Regressions.py (+247/-0)
cheetah/Tests/SyntaxAndOutput.py (+3253/-0)
cheetah/Tests/Template.py (+363/-0)
cheetah/Tests/Test.py (+53/-0)
cheetah/Tests/Unicode.py (+237/-0)
cheetah/Tests/__init__.py (+1/-0)
cheetah/Tests/xmlrunner.py (+381/-0)
cheetah/Tools/CGITemplate.py (+77/-0)
cheetah/Tools/MondoReport.py (+464/-0)
cheetah/Tools/MondoReportDoc.txt (+391/-0)
cheetah/Tools/RecursiveNull.py (+28/-0)
cheetah/Tools/SiteHierarchy.py (+166/-0)
cheetah/Tools/__init__.py (+8/-0)
cheetah/Tools/turbocheetah/__init__.py (+5/-0)
cheetah/Tools/turbocheetah/cheetahsupport.py (+110/-0)
cheetah/Tools/turbocheetah/tests/__init__.py (+1/-0)
cheetah/Tools/turbocheetah/tests/test_template.py (+66/-0)
cheetah/Unspecified.py (+9/-0)
cheetah/Utils/Indenter.py (+123/-0)
cheetah/Utils/Misc.py (+67/-0)
cheetah/Utils/WebInputMixin.py (+102/-0)
cheetah/Utils/__init__.py (+1/-0)
cheetah/Utils/htmlDecode.py (+14/-0)
cheetah/Utils/htmlEncode.py (+21/-0)
cheetah/Utils/statprof.py (+304/-0)
cheetah/Version.py (+58/-0)
cheetah/__init__.py (+20/-0)
cheetah/c/Cheetah.h (+47/-0)
cheetah/c/_namemapper.c (+494/-0)
cheetah/c/cheetah.h (+78/-0)
cheetah/convertTmplPathToModuleName.py (+20/-0)
closure.py (+0/-199)
debian/changelog (+7/-0)
debian/patches/02_clean_modules.patch (+0/-280)
debian/patches/02_fedora_cheetah_2.7_compat.patch (+36/-0)
debian/patches/03_ptyhon-2.6-warning-fix.patch (+0/-25)
debian/rules (+1/-1)
ez_setup.py (+0/-164)
foo.py (+0/-151)
setup.cfg (+5/-0)
src/CacheRegion.py (+0/-138)
src/CacheStore.py (+0/-108)
src/CheetahWrapper.py (+0/-589)
src/Compiler.py (+0/-2009)
src/DummyTransaction.py (+0/-58)
src/ErrorCatchers.py (+0/-63)
src/FileUtils.py (+0/-374)
src/Filters.py (+0/-176)
src/ImportHooks.py (+0/-139)
src/ImportManager.py (+0/-561)
src/Macros/I18n.py (+0/-67)
src/Macros/__init__.py (+0/-1)
src/NameMapper.py (+0/-355)
src/Parser.py (+0/-2599)
src/Servlet.py (+0/-126)
src/SettingsManager.py (+0/-620)
src/SourceReader.py (+0/-304)
src/Template.py (+0/-1856)
src/TemplateCmdLineIface.py (+0/-108)
src/Templates/SkeletonPage.py (+0/-273)
src/Templates/SkeletonPage.tmpl (+0/-44)
src/Templates/_SkeletonPage.py (+0/-216)
src/Templates/__init__.py (+0/-1)
src/Tests/CheetahWrapper.py (+0/-596)
src/Tests/FileRefresh.py (+0/-55)
src/Tests/NameMapper.py (+0/-539)
src/Tests/SyntaxAndOutput.py (+0/-3230)
src/Tests/Template.py (+0/-312)
src/Tests/Test.py (+0/-70)
src/Tests/__init__.py (+0/-1)
src/Tests/unittest_local_copy.py (+0/-977)
src/Tools/CGITemplate.py (+0/-78)
src/Tools/MondoReport.py (+0/-464)
src/Tools/MondoReportDoc.txt (+0/-391)
src/Tools/RecursiveNull.py (+0/-23)
src/Tools/SiteHierarchy.py (+0/-183)
src/Tools/__init__.py (+0/-8)
src/Tools/turbocheetah/__init__.py (+0/-5)
src/Tools/turbocheetah/cheetahsupport.py (+0/-110)
src/Tools/turbocheetah/tests/__init__.py (+0/-1)
src/Tools/turbocheetah/tests/test_template.py (+0/-66)
src/Unspecified.py (+0/-9)
src/Utils/Indenter.py (+0/-135)
src/Utils/Misc.py (+0/-82)
src/Utils/VerifyType.py (+0/-82)
src/Utils/WebInputMixin.py (+0/-103)
src/Utils/__init__.py (+0/-1)
src/Utils/htmlDecode.py (+0/-14)
src/Utils/htmlEncode.py (+0/-21)
src/Utils/memcache.py (+0/-625)
src/Utils/optik/__init__.py (+0/-32)
src/Utils/optik/errors.py (+0/-52)
src/Utils/optik/option.py (+0/-354)
src/Utils/optik/option_parser.py (+0/-667)
src/Version.py (+0/-58)
src/__init__.py (+0/-27)
src/_namemapper.c (+0/-490)
src/convertTmplPathToModuleName.py (+0/-15)
syntaxtest.py (+0/-15)
test2.2.py (+0/-2)
timingTests.py (+0/-201)
To merge this branch: bzr merge lp:~barry/ubuntu/natty/cheetah/py27-compat
Reviewer Review Type Date Requested Status
Clint Byrum Pending
Ubuntu branches Pending
Review via email: mp+38883@code.launchpad.net

Description of the change

Merges upstream 2.4.3 and adds Python 2.7 compatibility patch from the Fedora project.

These changes would still need to be pushed to Debian.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CHANGES'
2--- CHANGES 2007-11-18 14:59:06 +0000
3+++ CHANGES 2010-10-19 20:12:51 +0000
4@@ -1,6 +1,94 @@
5-Please initial your changes (there's a key at bottom) and add a date for each
6-release
7-================================================================================
8+2.4.2 (February 8th, 2010)
9+ - Fix issue where subclasses of Template failed to pick up attributes in the
10+ searchlist
11+ - Remove old/outdated bundled memcached python client
12+ - Allow for #encoding directives to exist after a comment (i.e. not the first
13+ line in a module)
14+ - Remove support for WebWare servlets (which caused significant performance
15+ slowdowns on Mac OS X)
16+ - Old/stale code pruned in preparation for Python 3 support
17+
18+2.4.1 (December 19th, 2009)
19+ - --quiet flag added to `cheetah` to silence printing to stdout (abbeyj)
20+ - Refactoring to minimize the amount of forked code for Python3 (rtyler)
21+ - Template.compile() will no longer create class names with numerous leading
22+ underscores (rtyler; reported by Kirill Uhanov)
23+ - DirectiveAnalyzer (cheetah-analyze script) added to report directive usage in templates (rtyler)
24+ - Older LaTeX docs converted to rst for Sphinx (rtyler)
25+ - Prevent #raw blocks from evaluating $-placeholders and escaped strings (karmix0)
26+ - New tests added to verify PSP behavior and other untested internals (rtyler)
27+
28+2.4.0 (October 24th, 2009)
29+ - Fix a major performance regression in Template.__init__()
30+ - More graceful handling of unicode when calling .respond() to render a template
31+ - Minor code updates
32+ - Update the default filter (thanks mikeb!)
33+
34+2.3.0 (October 24th, 2009) (loosely equivalent to 2.4.0)
35+ - Fix a major performance regression in Template.__init__()
36+ - More graceful handling of unicode when calling .respond() to render a template
37+ - Minor code updates
38+ - Update the default filter (thanks mikeb!)
39+
40+2.2.2 (September 10th, 2009)
41+ - Prevent _namemapper.c from segfaulting when PyImport_ImportModule fails for some reason (Bogdano Arendartchuk <debogdano@gmail.com>)
42+ - Removal of the contrib/markdown module (in favor of a setuptools dependency)
43+ - Default setup.py to use setuptools by default, failing that, fall back to distutils
44+ - Improvements to setup.py to support building for Windows (thanks abbeyj!)
45+ - Improvements to C-based NameMapper for Windows
46+ - Fixes for a swath of unit tests on Windows
47+ - Re-enabling the EOL tests (whoops)
48+ - Fix for unicode/utf-8 dynamic compilation error (thanks mikeb!) (Test.Unicode.JBQ_UTF8_Test8)
49+ - 0000010: [Templates] Failure to execute templates on Google App Engine (rtyler)
50+ - 0000026: [Compiler] Support multiple inheritance (rtyler)
51+
52+
53+2.2.1 (June 1st, 2009)
54+ - 0000020: [Templates] Builtin support for using Cheetah with Django (rtyler)
55+ - 0000021: [Compiler] @static and @classmethod don't properly define the _filter local (rtyler)
56+ - 0000023: [Compiler] Update Template super calls to use super() (rtyler)
57+ - Update all references to communitycheetah.org to point back at cheetahtemplate.org
58+
59+2.2.0 (May 17th, 2009)
60+ - Switch all internal representations of template code to unicode objects instead of str() objects
61+ - Convert unicode compiled template to an utf8 char buffer when writing to a file (Jean-Baptiste Quenot <jbq@caraldi.com>)
62+ - 0000011: [Templates] Calling a function with arguments calls the function with None (rtyler)
63+ - 0000015: [Tests] Resolve test failures in 'next' branch (rtyler)
64+ - 0000019: [Templates] Properly warn when joining unicode and non-unicode objects in DummyTransaction (rtyler)
65+
66+2.1.2 (May 5, 2009)
67+ - 0000006: [Templates] Support @staticmethod and @classmethod (rtyler)
68+
69+2.1.1 (April 16, 2009)
70+ - Support __eq__() and __ne__() the way you might expect in src/Tools/RecursiveNull (patch suggested by Peter Warasin <peter@endian.com>)
71+ - Applied patch to avoid hitting the filesystem to get the file modification time everytime a #include directive is processed (Jean-Baptiste Quenot <jbq@caraldi.com>)
72+ - Applied patch to fix some annoying cases when Cheetah writes to stderr instead of propagating the exception (Jean-Baptiste Quenot <jbq@caraldi.com>)
73+ - Added KDE editor support
74+ - Applied patch to correct importHook behavior on Python 2.6 (reported/patched by Toshio Ernie Kuratomi <a.badger@gmail.com>)
75+ - Correct unicode issue when calling/embedding unicode templates inside of other templtes (testcase Tests.Unicode.JPQ_UTF8_Test3. reported by Jean-Baptiste Quenot <jbq@caraldi.com>)
76+ - Added --shbang option (e.g. "cheetah compile --shbang '#!/usr/bin/python2.6' ")
77+ - Removed dependency on optik OptionParser in favor of builtin Python optparse module
78+ - Introduction of the #transform directive for whole-document filtering
79+ - Introduction of Cheetah.contrib.markdown and Cheetah.Filters.Markdown for outputting a markdown processed template (meant for #transform)
80+ - Cheetah.Filters.CodeHighlighter, pygments-based code highlighting filter for use with #transform
81+ - Addition of "useLegacyImportMode" compiler setting (defaulted to True) to allow for older (read: broken) import behavior
82+
83+2.1.0.1 (March 27, 2009)
84+ - Fix inline import issue introduced in v2.1.0
85+
86+2.1.0 (March 16, 2009)
87+ - Quiet DeprecationWarnings being printed to stderr when using Cheetah on Python 2.6 and up. Patch suggested by Satoru SATOH <satoru.satoh@gmail.com>
88+ - Apply patch to support parallel compilation of templates courtesy of Evan Klitzke <evan@eklitzke.org>
89+ - Corrected issue when __getattr__ calls on searchList objects raise exceptions (tyler@slide.com)
90+ - make autocalling in valueForName correctly ignore newstyle classes and instances
91+ that are callable, as it does for oldstyle classes and instances. Patch
92+ from lucas@endian.com
93+ [TR]
94+ - made it possible to chain multiple decorators to a method #def [TR with
95+ patch from Graham Dennis]
96+ - fixed a bug in _eatMultiLineDef that Graham Dennis reported. [TR]
97+ - fixed 'module.__init__() argument 1 must be string, not unicode' bug in
98+ Template.py reported by Erwin Ambrosch [TR]
99
100 2.0.1 (Nov 16, 2007)
101 - fixed a deadlock Christoph Zwerschke found in Cheetah.ImportHooks.
102
103=== added directory 'Cheetah.egg-info'
104=== added file 'Cheetah.egg-info/PKG-INFO'
105--- Cheetah.egg-info/PKG-INFO 1970-01-01 00:00:00 +0000
106+++ Cheetah.egg-info/PKG-INFO 2010-10-19 20:12:51 +0000
107@@ -0,0 +1,47 @@
108+Metadata-Version: 1.0
109+Name: Cheetah
110+Version: 2.4.3
111+Summary: Cheetah is a template engine and code generation tool.
112+Home-page: http://www.cheetahtemplate.org/
113+Author: R. Tyler Ballance
114+Author-email: cheetahtemplate-discuss@lists.sf.net
115+License: UNKNOWN
116+Description: Cheetah is an open source template engine and code generation tool.
117+
118+ It can be used standalone or combined with other tools and frameworks. Web
119+ development is its principle use, but Cheetah is very flexible and is also being
120+ used to generate C++ game code, Java, sql, form emails and even Python code.
121+
122+ Documentation
123+ ================================================================================
124+ For a high-level introduction to Cheetah please refer to the User's Guide
125+ at http://www.cheetahtemplate.org/learn.html
126+
127+ Mailing list
128+ ================================================================================
129+ cheetahtemplate-discuss@lists.sourceforge.net
130+ Subscribe at http://lists.sourceforge.net/lists/listinfo/cheetahtemplate-discuss
131+
132+ Credits
133+ ================================================================================
134+ http://www.cheetahtemplate.org/credits.html
135+
136+ Recent Changes
137+ ================================================================================
138+ See http://www.cheetahtemplate.org/CHANGES.txt for full details
139+
140+
141+Platform: UNKNOWN
142+Classifier: Development Status :: 5 - Production/Stable
143+Classifier: Intended Audience :: Developers
144+Classifier: Intended Audience :: System Administrators
145+Classifier: License :: OSI Approved :: MIT License
146+Classifier: Operating System :: OS Independent
147+Classifier: Programming Language :: Python
148+Classifier: Topic :: Internet :: WWW/HTTP
149+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
150+Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
151+Classifier: Topic :: Software Development :: Code Generators
152+Classifier: Topic :: Software Development :: Libraries :: Python Modules
153+Classifier: Topic :: Software Development :: User Interfaces
154+Classifier: Topic :: Text Processing
155
156=== added file 'Cheetah.egg-info/SOURCES.txt'
157--- Cheetah.egg-info/SOURCES.txt 1970-01-01 00:00:00 +0000
158+++ Cheetah.egg-info/SOURCES.txt 2010-10-19 20:12:51 +0000
159@@ -0,0 +1,80 @@
160+CHANGES
161+LICENSE
162+MANIFEST.in
163+README.markdown
164+SetupConfig.py
165+SetupTools.py
166+TODO
167+setup.py
168+Cheetah.egg-info/PKG-INFO
169+Cheetah.egg-info/SOURCES.txt
170+Cheetah.egg-info/dependency_links.txt
171+Cheetah.egg-info/requires.txt
172+Cheetah.egg-info/top_level.txt
173+bin/cheetah
174+bin/cheetah-analyze
175+bin/cheetah-compile
176+cheetah/CacheRegion.py
177+cheetah/CacheStore.py
178+cheetah/CheetahWrapper.py
179+cheetah/Compiler.py
180+cheetah/DirectiveAnalyzer.py
181+cheetah/Django.py
182+cheetah/DummyTransaction.py
183+cheetah/ErrorCatchers.py
184+cheetah/FileUtils.py
185+cheetah/Filters.py
186+cheetah/ImportHooks.py
187+cheetah/ImportManager.py
188+cheetah/NameMapper.py
189+cheetah/Parser.py
190+cheetah/Servlet.py
191+cheetah/SettingsManager.py
192+cheetah/SourceReader.py
193+cheetah/Template.py
194+cheetah/TemplateCmdLineIface.py
195+cheetah/Unspecified.py
196+cheetah/Version.py
197+cheetah/__init__.py
198+cheetah/convertTmplPathToModuleName.py
199+cheetah/Macros/I18n.py
200+cheetah/Macros/__init__.py
201+cheetah/Templates/SkeletonPage.py
202+cheetah/Templates/SkeletonPage.tmpl
203+cheetah/Templates/_SkeletonPage.py
204+cheetah/Templates/__init__.py
205+cheetah/Tests/Analyzer.py
206+cheetah/Tests/CheetahWrapper.py
207+cheetah/Tests/Cheps.py
208+cheetah/Tests/Filters.py
209+cheetah/Tests/Misc.py
210+cheetah/Tests/NameMapper.py
211+cheetah/Tests/Parser.py
212+cheetah/Tests/Performance.py
213+cheetah/Tests/Regressions.py
214+cheetah/Tests/SyntaxAndOutput.py
215+cheetah/Tests/Template.py
216+cheetah/Tests/Test.py
217+cheetah/Tests/Unicode.py
218+cheetah/Tests/__init__.py
219+cheetah/Tests/xmlrunner.py
220+cheetah/Tools/CGITemplate.py
221+cheetah/Tools/MondoReport.py
222+cheetah/Tools/MondoReportDoc.txt
223+cheetah/Tools/RecursiveNull.py
224+cheetah/Tools/SiteHierarchy.py
225+cheetah/Tools/__init__.py
226+cheetah/Tools/turbocheetah/__init__.py
227+cheetah/Tools/turbocheetah/cheetahsupport.py
228+cheetah/Tools/turbocheetah/tests/__init__.py
229+cheetah/Tools/turbocheetah/tests/test_template.py
230+cheetah/Utils/Indenter.py
231+cheetah/Utils/Misc.py
232+cheetah/Utils/WebInputMixin.py
233+cheetah/Utils/__init__.py
234+cheetah/Utils/htmlDecode.py
235+cheetah/Utils/htmlEncode.py
236+cheetah/Utils/statprof.py
237+cheetah/c/Cheetah.h
238+cheetah/c/_namemapper.c
239+cheetah/c/cheetah.h
240\ No newline at end of file
241
242=== added file 'Cheetah.egg-info/dependency_links.txt'
243--- Cheetah.egg-info/dependency_links.txt 1970-01-01 00:00:00 +0000
244+++ Cheetah.egg-info/dependency_links.txt 2010-10-19 20:12:51 +0000
245@@ -0,0 +1,1 @@
246+
247
248=== added file 'Cheetah.egg-info/requires.txt'
249--- Cheetah.egg-info/requires.txt 1970-01-01 00:00:00 +0000
250+++ Cheetah.egg-info/requires.txt 2010-10-19 20:12:51 +0000
251@@ -0,0 +1,1 @@
252+Markdown >= 2.0.1
253\ No newline at end of file
254
255=== added file 'Cheetah.egg-info/top_level.txt'
256--- Cheetah.egg-info/top_level.txt 1970-01-01 00:00:00 +0000
257+++ Cheetah.egg-info/top_level.txt 2010-10-19 20:12:51 +0000
258@@ -0,0 +1,1 @@
259+Cheetah
260
261=== modified file 'MANIFEST.in'
262--- MANIFEST.in 2006-07-26 22:03:15 +0000
263+++ MANIFEST.in 2010-10-19 20:12:51 +0000
264@@ -1,7 +1,7 @@
265-include MANIFEST.in *.py *.cfg TODO CHANGES LICENSE README examples docs bin
266-recursive-include src *.py *.tmpl *.txt
267+include MANIFEST.in *.py *.cfg TODO CHANGES LICENSE README.markdown examples docs bin
268+recursive-include cheetah *.py *.tmpl *.txt *.h
269 recursive-include bin *
270 recursive-include docs *
271 recursive-include examples *
272-recursive-exclude src *.pyc *~ *.aux
273+recursive-exclude cheetah *.pyc *~ *.aux
274 recursive-exclude docs *~ *.aux
275
276=== modified file 'PKG-INFO'
277--- PKG-INFO 2007-11-18 14:59:06 +0000
278+++ PKG-INFO 2010-10-19 20:12:51 +0000
279@@ -1,9 +1,9 @@
280 Metadata-Version: 1.0
281 Name: Cheetah
282-Version: 2.0.1
283+Version: 2.4.3
284 Summary: Cheetah is a template engine and code generation tool.
285-Home-page: http://www.CheetahTemplate.org/
286-Author: Tavis Rudd
287+Home-page: http://www.cheetahtemplate.org/
288+Author: R. Tyler Ballance
289 Author-email: cheetahtemplate-discuss@lists.sf.net
290 License: UNKNOWN
291 Description: Cheetah is an open source template engine and code generation tool.
292@@ -15,7 +15,7 @@
293 Documentation
294 ================================================================================
295 For a high-level introduction to Cheetah please refer to the User's Guide
296- at http://cheetahtemplate.org/learn.html
297+ at http://www.cheetahtemplate.org/learn.html
298
299 Mailing list
300 ================================================================================
301@@ -24,869 +24,12 @@
302
303 Credits
304 ================================================================================
305- http://cheetahtemplate.org/credits.html
306-
307- Praise
308- ================================================================================
309- "I'm enamored with Cheetah" - Sam Ruby, senior member of IBM Emerging
310- Technologies Group & director of Apache Software Foundation
311-
312- "Give Cheetah a try. You won't regret it. ... Cheetah is a truly powerful
313- system. ... Cheetah is a serious contender for the 'best of breed' Python
314- templating." - Alex Martelli
315-
316- "People with a strong PHP background absolutely love Cheetah for being Smarty,
317- but much, much better." - Marek Baczynski
318-
319- "I am using Smarty and I know it very well, but compiled Cheetah Templates with
320- its inheritance approach is much powerful and easier to use than Smarty." -
321- Jaroslaw Zabiello
322-
323- "There is no better solution than Cheetah" - Wilk
324-
325- "A cheetah template can inherit from a python class, or a cheetah template, and
326- a Python class can inherit from a cheetah template. This brings the full power
327- of OO programming facilities to the templating system, and simply blows away
328- other templating systems" - Mike Meyer
329-
330- "Cheetah has successfully been introduced as a replacement for the overweight
331- XSL Templates for code generation. Despite the power of XSL (and notably XPath
332- expressions), code generation is better suited to Cheetah as templates are much
333- easier to implement and manage." - The FEAR development team
334- (http://fear.sourceforge.net/docs/latest/guide/Build.html#id2550573)
335-
336- "I've used Cheetah quite a bit and it's a very good package" - Kevin Dangoor,
337- lead developer of TurboGears.
338+ http://www.cheetahtemplate.org/credits.html
339
340 Recent Changes
341 ================================================================================
342- See http://cheetahtemplate.org/docs/CHANGES for full details.
343-
344- Please initial your changes (there's a key at bottom) and add a date for each
345- release
346- ================================================================================
347-
348- 2.0.1 (Nov 16, 2007)
349- - fixed a deadlock Christoph Zwerschke found in Cheetah.ImportHooks.
350- [TR]
351-
352- 2.0 (Oct 12, 2007)
353- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
354-
355- - fixed exception handling issue in the C implemenation of NameMapper
356- [patch from Eric Huss]
357-
358- - fixed filtering of #included subtemplates
359- [patch from Brian Bird]
360-
361- See the release notes from 2.0b1-5 and 2.0rc1-8 for other changes since
362- Cheetah 1.0.
363-
364-
365- 2.0rc8 (April 11, 2007)
366- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
367- Core Changes: [TR]
368-
369- - added a '#unicode <encoding>' directive to indicate that the output of the
370- template should be a unicode string even if the template source is a
371- normal byte string.
372-
373- - #unicode and #encoding are mutually exclusive. Use one or the other.
374- - #unicode must be on a line by itself.
375- - Strings in embedded code must be explictly marked as unicode if they
376- contain non-ascii chars:
377-
378- #unicode latin-1
379- $f(u"<some non-ascii char>") ## right
380- $f("<some non-ascii char>") ## wrong
381-
382- However, this works fine:
383-
384- #unicode latin-1
385- blah blah <some non-ascii char> blah blah
386-
387- - fixed several unicode bugs in the compiler.
388-
389- - fixed some unicode issues in the standard filters.
390-
391- - fixed a few minor bugs in code that never gets called. Thanks to
392- Alejandro Dubrovsky for pointing them out.
393-
394- - make RawOrEncodedUnicode the baseclass of all filters and remove some
395- unused/redudant filters
396-
397- - added new compiler setting 'addTimestampsToCompilerOutput'. See Brian
398- Bird's post about it. He stores his cheetah generated .py files in
399- subversion and needed to disable the timestamp code so svn wouldn't care
400- when he recompiles those .py modules.
401-
402- - added the #super directive, which calls the method from the parent class
403- which has the same as the current #def or #block method.
404-
405- #def foo
406- ... child output
407- #super ## includes output of super(<CurrentClass>, self).foo()
408- ... child output
409- #end def
410-
411-
412- #def bar(arg)
413- ... child output
414- #super(arg) ## includes output of super(<CurrentClass>, self).bar(arg)
415- ... child output
416- #end def
417-
418- - added some unit tests for the new directives
419-
420-
421- 2.0rc7 (July 4, 2006)
422- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
423- Core Changes: [TR]
424- - extended the #implements directive so an arguments list can be declared in
425- the same fashion as #def and #block.
426-
427- - made the parser raise ParseError when $*placeholder, $*5*placeholder,
428- $(placeholder), etc. are found within expressions. They are only valid in
429- top-level text.
430-
431- - tweaked the parser so it's possible to place a comment on the same line as
432- a directive without needing to explicitly close the directive first. This
433- works regardless of whether or not you added a colon.
434-
435- self.verify("#if 1:\n$aStr\n#end if\n",
436- "blarg\n")
437-
438- self.verify("#if 1: \n$aStr\n#end if\n",
439- "blarg\n")
440-
441- self.verify("#if 1: ##comment \n$aStr\n#end if\n",
442- "blarg\n")
443-
444- self.verify("#if 1 ##comment \n$aStr\n#end if\n",
445- "blarg\n")
446-
447- Previously, that last test would have required an extra # to close the #if
448- directive before the comment directive started:
449- self.verify("#if 1 ###comment \n$aStr\n#end if\n",
450- "blarg\n")
451-
452- Code that makes use of explicit directive close tokens immediately followed by
453- another directive will still work as expected:
454- #if test##for i in range(10)# foo $i#end for##end if
455-
456- - safer handling of the baseclass arg to Template.compile(). It now does
457- the right thing if the user passes in an instance rather than a class.
458-
459- ImportHooks: [TR]
460- - made it possible to specify a list of template filename extentions that are
461- looped through while searching for template modules. E.g.:
462- import Cheetah.ImportHooks
463- Cheetah.ImportHooks.install(templateFileExtensions=('.tmpl','.cheetah'))
464-
465- Core changes by MO:
466- - Filters are now new-style classes.
467- - WebSafe and the other optional filters in Filters.py now use
468- RawOrEncodedUnicode instead of Filter as a base class. This allows them
469- to work with Unicode values containing non-ASCII characters.
470- User-written custom filters should inherit from
471- RawOrEncodedUnicode and call the superclass .filter() instead of str().
472- str() as of Python 2.4.2 still converts Unicode to string using
473- ASCII codec, which raises UnicodeEncodeError if it contains non-ASCII
474- characters.
475-
476- 2.0rc6 (Feb 4, 2006)
477- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
478- Core Changes: [TR]
479- - added a Cheetah version dependency check that raises an assertion if a
480- template was compiled with a previous version of Cheetah whose templates
481- must be recompiled.
482-
483- - made the Cheetah compilation metadata accessible via class attributes in
484- addition to module globals
485-
486- - major improvement to exception reporting in cases where bad Python syntax
487- slips past the Cheetah parser:
488- """
489- File "/usr/lib/python2.4/site-packages/Cheetah/Template.py", line 792, in compile
490- raise parseError
491- Cheetah.Parser.ParseError:
492-
493- Error in the Python code which Cheetah generated for this template:
494- ================================================================================
495-
496- invalid syntax (DynamicallyCompiledCheetahTemplate.py, line 86)
497-
498- Line|Python Code
499- ----|-------------------------------------------------------------
500- 84 |
501- 85 | write('\n\n')
502- 86 | for i an range(10): # generated from line 4, col 1
503- ^
504- 87 | _v = i # '$i' on line 5, col 3
505- 88 | if _v is not None: write(_filter(_v, rawExpr='$i')) # from line 5, col 3.
506- 89 | write('\n')
507-
508- ================================================================================
509-
510- Here is the corresponding Cheetah code:
511-
512- Line 4, column 1
513-
514- Line|Cheetah Code
515- ----|-------------------------------------------------------------
516- 2 |#compiler useNameMapper=False
517- 3 |
518- 4 |#for i an range(10)
519- ^
520- 5 | $i
521- 6 |#end for
522- 7 |
523- """
524-
525- 2.0rc5 (Feb 3, 2006)
526- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
527- Core Changes: [TR]
528- - fixed a memory leak in Template.compile(), reported by Andrea Arcangeli
529- - simplified concurrency locking and compile caching in Template.compile()
530-
531- The command line tool (CheetahWrapper.py):
532- - added new option --settings for supplying compiler settings
533- - added new option --templateAPIClass to replace the environment var
534- CHEETAH_TEMPLATE_CLASS lookup I added in 2.0b1
535-
536- 2.0rc4 (Jan 31, 2006)
537- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
538- Core Changes: [TR]
539- - fixed a typo-bug in the compile hashing code in Template.compile()
540- - improved the macros framework and made it possible to implement macros in
541- Python code so they can be shared between templates
542- - more work on the #i18n directive. It's now a macro directive.
543- - added new Cheetah.Macros package
544- - more tests
545-
546- 2.0rc3 (Jan 29, 2006)
547- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
548- Core Changes: [TR]
549- - added short-form single line versions of all directives that have an #end
550- tag, except for #errorCatcher:
551- #if, #else, #elif, #unless,
552- #for, #while, #repeat,
553- #try, #except, #finally,
554- #cache, #raw
555- #call, #capture
556-
557- The #def and #block directives already had single-line versions.
558- #if cond: foo
559- #elif cond2: bar
560- #else: blarg
561-
562- #for i, val in enumerate(vals): $i-$val
563-
564- Note that if you accidentally leave a colon at the end of one of these
565- directives but nothing else follows it, aside from whitespace, the parser
566- will treat it as a normal multi-line directive.
567-
568- The first leading space after the colon is discarded. Any additional
569- spaces will be included in the output.
570-
571- Also note, if you use the short form versions of #if/#else/#elif you must
572- it for all three. The following is not valid:
573- #if cond: foo
574- #elif cond2
575- bar
576- #else: blarg
577-
578- - added support for $!silentModePlaceholders
579- This is the same as quiet mode in Velocity:
580- http://jakarta.apache.org/velocity/docs/user-guide.html#Quiet%20Reference%20Notation
581-
582- - added support for function/method @decorators. It also works with blocks.
583- As in vanilla Python, the @decorator statement must be followed with a
584- function/method definition (i.e. #def or #block).
585-
586- #from xxx import aDecorator
587- ...
588- ...
589- #@aDecorator
590- #def func
591- foo
592- #end def
593-
594- #@aDecorator
595- #def singleLineShortFormfunc: foo
596-
597- #@aDecorator
598- #block func2
599- bar
600- #end block
601-
602- - added a new callback hook 'handlerForExtendsDirective' to the compiler settings. It
603- can be used to customize the handling of #extends directives. The
604- callback can dynamically add import statements or rewrite the baseclass'
605- name if needed:
606- baseClassName = handler(compiler, baseClassName)
607- See the discussion on the mailing list on Jan 25th for more details.
608-
609- - changed the default filter to the one that doesn't try to encode Unicode
610- It was 'EncodeUnicode' and is now 'RawOrEncodedUnicode'.
611-
612- - added optional support for parsing whitespace between the directive start
613- token (#) and directive names, per Christophe Eymard's request. For the
614- argument behind this see the mailing list archives for Jan 29th. This is
615- off by default. You must turn it on using the compiler setting
616- allowWhitespaceAfterDirectiveStartToken=True
617-
618- #for $something in $another
619- # for $somethin2 in $another2
620- blahblah $something in $something2
621- # end for
622- #end for
623-
624- - made the handling of Template.compile()'s preprocessors arg simpler and
625- fixed a bug in it.
626-
627- - fixed attribute name bug in the .compile() method (it affected the feature
628- that allows generated module files to be cached for better exception
629- tracebacks)
630-
631- - refactored the #cache/CacheRegions code to support abitrary backend cache
632- data stores.
633-
634- - added MemcachedCacheStore, which allows cache data to be stored in a
635- memcached backend. See http://www.linuxjournal.com/article/7451 and
636- http://www.danga.com/memcached/. This is only appropriate for systems
637- running many Python server processes that need to share cached data to
638- reduce memory requirements. Don't bother with this unless you actually
639- need it. If you have a limited number of Python server processes it is
640- much faster, simpler, and more secure to just cache in the memory of each
641- process.
642-
643- KEEP MEMCACHED'S LIMITED SECURITY IN MIND!! It has no authentication or
644- encryption and will introduce a gaping hole in your defenses unless you
645- are careful. If you are caching sensitive data you should take measures
646- to ensure that a) untrusted local system users cannot connect to memcached
647- server, b) untrusted external servers cannot connect, and c) untrusted
648- users on trusted external servers cannot connect. Case (a) can be dealt
649- with via iptable's owner match module for one way to do this: "iptables -A
650- ... -m owner ..." Cases (b) and (c) can be handled by tunnelling
651- memcached network connections over stunnel and implementing stunnel
652- authentication with mandatory peer/client certs.
653-
654- - some under-the-hood refactoring of the parser
655-
656- - made it possible to add custom directives, or customize the
657- parsing/handling of existing ones, via the compiler settings
658- 'directiveNamesAndParsers' and 'endDirectiveNamesAndHandlers'
659-
660- - added a compile-time macro facility to Cheetah. These macros are very
661- similar to macros in Lisp:
662- http://www.apl.jhu.edu/~hall/Lisp-Notes/Macros.html.
663-
664- As with Lisp macros, they take source code (Cheetah source) as input and
665- return source code (again Cheetah source) as output. They are executed at
666- compile-time, just like in Lisp and C. The resultant code
667- gets executed at run-time.
668-
669- The new #defmacro directive allows users to create macros inside the
670- source of their templates. Macros can also be provided via the compiler
671- setting 'macroDirectives'. The 'macroDirectives' setting allows you to
672- share common macros between templates.
673-
674- The syntax for the opening tag of #defmacro is the same as for #def and
675- #block. It expects a macro name followed by an optional argument list in
676- brackets. A `src` argument is automatically added to the beginning of
677- every macro's argument list. The value of the `src` is the block of
678- input source code that is provided during a macro call (see below).
679-
680- #defmacro <macroname>[(argspec)]
681- <macrobody>
682- #end defmacro
683-
684- All of Cheetah's syntax is available for use inside macros, but the
685- placeholderStartToken is @ instead of $ and the
686- directiveStartToken/EndToken is % instead of #. Any syntax using the
687- standard $/# tokens will be treated as plain text and included in the output
688- of the macro.
689-
690- Here are some examples:
691- #defmacro addHeaderFooter
692- header
693- @src
694- footer
695- #end defmacro
696-
697- #defmacro addHeaderFooter(header='h', footer='f')
698- @header
699- @src
700- @footer
701- #end defmacro
702-
703- There is a single-line short form like for other directives:
704-
705- #defmacro addHeaderFooter: header @src footer
706- #defmacro addHeaderFooter(header='h', footer='f'): @header @src @footer
707-
708- The syntax for calling a macro is similar to the simplest usage of the
709- #call directive:
710-
711- #addHeaderFooter
712- Source $code to wrap
713- #end addHeaderFooter
714-
715- #addHeaderFooter: Source $code to wrap
716-
717- #addHeaderFooter header='header', footer='footer: Source $code to wrap
718-
719-
720- In Elisp you write
721- (defmacro inc (var)
722- (list 'setq var (list '1+ var)))
723- to define the macro `inc` and write
724- (inc x)
725- which expands to
726- (setq x (1+ x))
727-
728- In Cheetah you'd write
729- #defmacro inc: #set @src +=1
730- #inc: $i
731- which expands to
732- #set $i += 1
733-
734- print Template("""\
735- #defmacro inc: #set @src +=1
736- #set i = 1
737- #inc: $i
738- $i""").strip()==2
739-
740- - fixed some bugs related to advanced usage of Template.compile(). These
741- were found via new unit tests. No one had actually run into them yet.
742-
743- - added the initial bits of an #i18n directive. It has the same semantics
744- as
745- #call self.handleI18n
746- Some $var cheetah source
747- #end call
748- but has a simpler syntax:
749- #i18n
750- Some $var cheetah source
751- #end i18n
752-
753- ## single-line short form:
754- #i18n: Some $var cheetah source
755-
756- The method it calls, self.handleI18n, is just a stub at the moment, but it
757- will soon be a wrapper around gettext. It currently has one required
758- positional argument `message`. I anticipate supporting the following
759- optional arguments:
760-
761- id = msgid in the translation catalog
762- domain = translation domain
763- source = source lang
764- target = a specific target lang
765- comment = a comment to the translation team
766-
767- plural = the plural form of the message
768- n = a sized argument to distinguish between single and plural forms
769-
770- #i18n is executed at runtime, but it can also be used in conjunction with
771- a Cheetah preprocessor or macro (see above) to support compile time
772- translation of strings that don't have to deal with plural forms.
773-
774- - added Cheetah.Utils.htmlEncode and Cheetah.Utils.htmlDecode
775-
776- - more docstring text
777-
778- Unit tests: [TR]
779- - extended the caching tests
780- - added tests for the various calling styles of Template.compile()
781- - added copies of all the SyntaxAndOutput tests that use a template
782- baseclass other than `Template`. This ensures that all syntax & core
783- features work with 2.0's support for arbitrary baseclasses.
784- - added tests for all the new directives and the new single-line short forms
785-
786- 2.0rc2 (Jan 13th, 2006)
787- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
788- Core Changes: [TR]
789- - fixed some python 2.4isms that slipped in. All the tests pass with Python
790- 2.2 now
791- - added lots more docstring content in the Template class
792- - made multiline comments gobble whitespace like other directives, per JJ's
793- request. The rather longwinded compiler setting
794- gobbleWhitespaceAroundMultiLineComments can be used to go back to the old
795- non-gobbling behaviour if needed.
796- - added #capture directive to complement the #call directive.
797- #call executes a region of Cheetah code and passes its output into a function call
798- #capture executes a region of Cheetah code and assigns its output to a variable
799- - extended the compile caching code in Template.compile so it works with the
800- 'file' arg.
801- - added cacheModuleFilesForTracebacks and cacheDirForModuleFiles args to
802- Template.compile(). See the docstring for details.
803- - misc internal refactoring in the parser
804- - improved handling of keyword args in the __init__ method and fixed a
805- potential clash between the namespaces and searchList args
806-
807- WWW: [TR]
808- - added the source for the new Cheetah website layout/content
809-
810- 2.0rc1 (Jan 10, 2006)
811- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
812- Core Changes: [TR]
813- - made it possible nest #filter directives
814- - added lots more docstring content in the Template class
815- - added Template.subclass() classmethod for quickly creating subclasses of
816- existing Cheetah template classes. It takes the same args as the
817- .compile() classmethod and returns a template that is a subclass of the
818- template .subclass() is called from:
819- T1 = Template.compile(' foo - $meth1 - bar\n#def meth1: this is T1.meth1')
820- T2 = T1.subclass('#implements meth1\n this is T2.meth1')
821-
822- - added baseclass arg to Template.compile(). It simplifies the reuse of
823- dynamically compiled templates:
824- # example 1, quickly subclassing a normal Python class and using its
825- # __init__ call signature:
826- dictTemplate = Template.compile('hello $name from $caller', baseclass=dict)
827- print dictTemplate(name='world', caller='me')
828-
829- # example 2, mixing a Cheetah method into a class definition:
830- class Foo(dict):
831- def meth1(self):
832- return 'foo'
833- def meth2(self):
834- return 'bar'
835- Foo = Template.compile('#implements meth3\nhello $name from $caller',
836- baseclass=Foo)
837- print Foo(name='world', caller='me')
838-
839- A side-benefit is the possibility to use the same Cheetah source with
840- several baseclass, as the baseclass is orthogonal to the source code,
841- unlike the #extends directive.
842-
843- - added 'namespaces' as an alias for 'searchList' in Template.__init__
844- - made it possible to pass in a single namespace to 'searchList', which will
845- automatically be converted into a list.
846- - fixed issue with buffering and use of #call when template is used as a
847- webkit servlet
848- - added Cheetah.Utils.htmlEncode and htmlDecode
849-
850- The command line tool (CheetahWrapper.py):
851- - changed insertion order for the --env and --pickle options so they match the
852- commandline UI of the compiled template modules themselves [TR]
853-
854- 2.0b5 (Jan 7, 2006)
855- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
856- Core Changes: [TR]
857- - made Cheetah.Template a new-style class by inserting 'object' into its'
858- inheritance tree. Templates can now use super(), properties and all the
859- other goodies that come with new-style classes.
860- - removed the WebInputMixin by placing its one method directly in the
861- Template class.
862- - removed the SettingsManager Mixin. It wasn't being used by anything
863- anymore.
864- - added a framework for caching the results of compilations in
865- Template.compile(). This is on by default and protects against bad
866- performance issues that are due to programmers misguidedly compiling
867- templates inside tight loops. It also saves on memory usage.
868- - misc attr name changes to avoid namespace pollution
869- - more + improved docstrings
870- - replaced the oldstyle dynamic compile hacks with a wrapper around
871- Template.compile(). The old usage pattern Template(src) now benefits from
872- most of the recent changes.
873- Template(src).__class__ == Template.compile(src)
874- - removed all the extra imports required by oldstyle dynamic compile hacks
875- - converted the cheetah #include mechanism to newstyle compilation and made it
876- more flexible
877- - made the #include mechanism work with file objects in addition to file names
878- - made the handling of args to Template.compile() more flexible. You can now
879- provide defaults via class attributes.
880- - made preprocessors for Template.compile() work with file arguments
881- - added support for specifying a __metaclass__ on cheetah template classes
882- - refactored both the class and instance initialization processes
883- - improved the handling of __str__ in _assignRequiredMethodsToClass
884-
885- The command line tool (CheetahWrapper.py): [TR]
886- - improved error output in CheetahWrapper
887- - switched fill command over to new style compile usage
888-
889- Unit tests: [TR]
890- - fixed format string bug in unittest_local_copy.py
891-
892- 2.0b4 (Jan 6, 2006)
893- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
894- Core Changes: [TR]
895- - fixed up parsing of target lists in for loops. This was previously limited
896- to fairly simple target lists.
897- #for ($i, $j) in [('aa','bb'),('cc','dd')]
898- $i.upper,$j.upper
899- #end for"
900- #for (i, j) in [('aa','bb'),('cc','dd')]
901- $i.upper,$j.upper
902- #end for"
903- #for i,(j, k) in enumerate([('aa','bb'),('cc','dd')])
904- $j.upper,$k.upper
905- #end for"
906- - refactored the class initialization process
907- - improved handling of target lists in #set directive. This was previously
908- limited to fairly simple target lists.
909- #set i,j = [1,2] ... #set $i,$j = [1,2]
910- #set (i,j) = [1,2] ... #set ($i,$j) = [1,2]
911- #set i, (j,k) = [1,(2,3)] ... #set $i, ($j,$k) = [1,(2,3)]
912-
913- - made it possible for the expressionFilter hooks to modify the code chunks
914- they are fed. Also documented the hooks in a docstring. Thus the hooks
915- can be used as preprocessors for expressions, 'restricted execution', or
916- even enforcement of style guidelines.
917-
918- - removed cheetah junk from docstrings and placed it all in comments or
919- __moduleVars__. Per JJ's suggestion.
920-
921- - made it possible to nest #cache directives to any level
922- - made it possible to nest #call directives to any level
923-
924- Unit Tests [TR]
925- - extended tests for #for directive
926- - expanded tests for #set directive
927- - expanded tests for #call directive
928- - expanded tests for #cache directive
929- - added basic tests for the new $placeholder string expressions:
930- c'text $placeholder text'
931-
932- 2.0b3 (Jan 5, 2006)
933- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
934- Core Changes: [TR]
935- - added #yield statement
936- - added ability to create nested scopes/functions via nested #def statements
937- - added new #call directive and related #arg directive, per Ian Bicking's
938- suggestion.
939- - added new expression syntax c"text $placeholder text"
940-
941- for those basic function calling cases where you just need to pass in a
942- small bit of cheetah output as an argument:
943-
944- c'a string with $placeholders',
945- c'''a string with $placeholders''',
946- c"a string with $placeholders",
947- c"""a string with $placeholders"""
948-
949- - They can't contain #directives, but accept any valid $placeholder syntax
950- except caching placeholders. Caching placeholders don't make any sense in
951- this context.
952- - They can be used *any* place where a python expression is expected.
953- - They can be nested to any depth.
954-
955- $func(c'<li>$var1-$var2</li>')
956- $func(c'<li>$var1-$var2</li>', doSomething=True)
957- $func(content=c'<li>$var1-$var2</li>', doSomething=True)
958- $func(lambda x,y: c'<li>$x-$y</li>')
959- $func(callback=lambda x,y: c'<li>$x-$y</li>')
960- $func(lambda x,y: c'<li>$x-$y-$varInSearchList</li>')
961- $func(c'<li>$var1-$var2-$(var3*10)-$(94.3*58)</li>')
962- $func(c'<li>$var1-$var2-$func2(c"a nested expr $var99")</li>')
963- #if $cond then c'<li>$var1-$var2</li>' else c'<p>$var1-$var2</p>'
964- #def foo(arg1=c'$var1<span class="foo">$var2</span>'): blah $arg1 blah
965- $foo(c'$var1<i>$var2</i>')
966-
967- - added preprocessor hooks to Template.compile()
968- can be used for partial completion or 'compile-time-caching'
969- ... more details and examples coming. It's very useful, but takes a bit
970- of explaining.
971- - added '#set module varName = expr' for adding module globals. JJ's suggestion
972- - improved generated docstring notes about cached vars
973- - fixed silly bug related to """ in docstring comments and statements like
974- this '#def foo: $str("""foo""")'. Reported by JJ.
975- - changed the handling of single-line defs so that
976- '#def xxx:<just whitespace>\n' will be treated as a multi-line #def.
977- The same applies to #block. There's a compiler setting to turn this off
978- if you really need empty single-line #def:'s.
979- JJ reported that this was causing great confusion with beginners.
980- - improved error message for unclosed directives, per Mike Orr's suggestion.
981- - added optional support for passing the trans arg to methods via **KWS rather
982- than trans=None. See the discussion on the mailing list Jan 4th (JJ's post) for
983- details. The purpose is to avoid a positional argument clash that
984- apparently is very confusing for beginners.
985-
986- Note that any existing client code that passing the trans arg in
987- positionally rather than as a keyword will break as a result. WebKit
988- does this with the .respond method so I've kept the old style there.
989- You can also turn this new behaviour off by either manually including
990- the trans arg in your method signature (see the example below) or by
991- using the compiler setting 'useKWsDictArgForPassingTrans'=False.
992-
993- #def manualOverride(arg1, trans=None)
994- foo $arg1
995- #end def
996-
997- ImportHooks:
998- - made the ImportHook more robust against compilation errors during import [TR]
999-
1000- Install scripts: [TR]
1001- - added optional support for pje's setuptools
1002- - added cheeseshop classifiers
1003- - removed out of date install instructions in __init__.py
1004-
1005- Servlet Base Class For Webkit: [TR]
1006- - disabled assignment of self.application (was a webware hack)
1007-
1008- Unit Tests: [TR]
1009- - unit tests for most of the new syntax elements
1010- - tidied up some old tests
1011- - misc refactoring
1012-
1013- 2.0b2 (Dec 30, 2005)
1014- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
1015-
1016- Core Changes:
1017- - In previous versions of Cheetah tracebacks from exceptions that were raised
1018- inside dynamically compiled Cheetah templates were opaque because
1019- Python didn't have access to a python source file to use in the traceback:
1020-
1021- File "xxxx.py", line 192, in getTextiledContent
1022- content = str(template(searchList=searchList))
1023- File "cheetah_yyyy.py", line 202, in __str__
1024- File "cheetah_yyyy.py", line 187, in respond
1025- File "cheetah_yyyy.py", line 139, in writeBody
1026- ZeroDivisionError: integer division or modulo by zero
1027-
1028- It is now possible to keep the generated source code from the python
1029- classes returned by Template.compile() in a cache dir. Having these files
1030- around allows Python to include the actual source lines in tracebacks and
1031- makes them much easier to understand:
1032-
1033- File "/usr/local/unsnarl/lib/python/us/ui/views/WikiPageRenderer.py", line 192, in getTextiledContent
1034- content = str(template(searchList=searchList))
1035- File "/tmp/CheetahCacheDir/cheetah_yyyy.py", line 202, in __str__
1036- def __str__(self): return self.respond()
1037- File "/tmp/CheetahCacheDir/cheetah_yyyy.py", line 187, in respond
1038- self.writeBody(trans=trans)
1039- File "/tmp/CheetahCacheDir/cheetah_yyyy.py", line 139, in writeBody
1040- __v = 0/0 # $(0/0)
1041- ZeroDivisionError: integer division or modulo by zero
1042-
1043- This is turned off by default. To turn it on, do this:
1044-
1045- class NiceTracebackTemplate(Template):
1046- _CHEETAH_cacheModuleFilesForTracebacks = True
1047- _CHEETAH_cacheDirForModuleFiles = '/tmp/CheetahCacheDir' # change to a dirname
1048-
1049- templateClass = NiceTracebackTemplate.compile(src)
1050-
1051- # or
1052- templateClass = Template.compile(src,
1053- cacheModuleFilesForTracebacks=True, cacheDirForModuleFiles='/tmp/CheetahCacheDir')
1054-
1055-
1056- This only works with the new Template.compile(src) usage style!
1057-
1058- Note, Cheetah generated modules that are compiled on the command line have
1059- never been affected by this issue. [TR]
1060-
1061- - added an extra comment per $placeholder to generated python code so it is
1062- easier to grok. [TR]
1063-
1064- 2.0b1 (Dec 29, 2005)
1065- !!!THIS RELEASE REQUIRES RECOMPILATION OF ALL COMPILED CHEETAH TEMPLATES!!!
1066-
1067- Core Changes:
1068- - enabled use of any expression in ${placeholders}. See the examples I posted to
1069- the email list on Dec 12th. All use cases of the #echo directive can now
1070- be handled with ${placeholders}. This came from a suggestion by Mike
1071- Orr. [TR]
1072-
1073- - made it possible for templates to #extend (aka subclass) any arbitrary
1074- baseclass, including Python's new style classes. You must either compile
1075- your classes on the command line or use the new classmethod
1076- Template.compile() as described below. The old Template(src) interface
1077- still works, provided you don't try to use this new arbitrary baseclass
1078- stuff. See my messages to the email list for more details. [TR]
1079-
1080- - made it possible to create template classes dynamically, rather than just
1081- instances. See the new classmethod Template.compile(). See my messages
1082- to the email list for more details. [TR]
1083-
1084- klass = Template.compile(src)
1085-
1086- - made it easier to work with custom compiler settings, particularly from
1087- the command line tool. You can now define a subclass of Template which
1088- will compile your templates using custom compilerSettings, or even a
1089- custom compiler class, without requiring you to manually pass in your
1090- compilerSettings each time or define them in the template src itself via
1091- the #compiler directive. You can make the command line tool use your
1092- subclass by defining the environment variable CHEETAH_TEMPLATE_CLASS. It
1093- should be in the form 'package.module:class'. See my messages
1094- to the email list for more details. [TR]
1095-
1096- - made it possible to pass the searchList in as an argument to #def'ined
1097- methods. This makes all lookup that occur within the scope of that method
1098- use the provided searchList rather than self._searchList. This does not
1099- carry over to other methods called within the top method, unless they
1100- explicitly accept the searchList in their signature AND you pass it to
1101- them when calling them. This behaviour can be turned off with the
1102- corresponding compilerSetting 'allowSearchListAsMethArg' [TR]
1103-
1104- - added hooks for filtering / restricting dangerous stuff in cheetah source
1105- code at compile time. These hooks can be used to enable Cheetah template
1106- authoring by untrusted users. See my messages to the email list for more
1107- details. Note, it filters expressions at parse/compile time, unlike Python's
1108- old rexec module which restricted the Python environment at runtime. [TR]
1109-
1110- # Here are the relevant compiler settings:
1111- # use lower case keys here!!
1112- 'disabledDirectives':[], # list of directive keys, without the start token
1113- 'enabledDirectives':[], # list of directive keys, without the start token
1114-
1115- 'disabledDirectiveHooks':[], # callable(parser, directiveKey),
1116- # called when a disabled directive is found, prior to raising an exception
1117-
1118- 'preparseDirectiveHooks':[], # callable(parser, directiveKey)
1119- 'postparseDirectiveHooks':[], # callable(parser, directiveKey)
1120-
1121- 'preparsePlaceholderHooks':[], # callable(parser)
1122- 'postparsePlaceholderHooks':[], # callable(parser)
1123-
1124- 'expressionFilterHooks':[],
1125- # callable(parser, expr, exprType, rawExpr=None, startPos=None)
1126- # exprType is the name of the directive, 'psp', or 'placeholder'.
1127- #all lowercase
1128-
1129- - added support for a short EOLSlurpToken to supplement the #slurp
1130- directive. It's currently re.compile('#\s*\n') (i.e # followed by
1131- arbitrary whitespace and a new line), but this is not set in stone. One
1132- other suggestion was the backslash char, but I believe Python's own
1133- interpretation of backslashes will lead to confusion. The compiler
1134- setting 'EOLSlurpToken' controls this. You can turn it off completely by
1135- setting 'EOLSlurpToken' to None. See the email list for more details. [TR]
1136-
1137- - added '_CHEETAH_' prefix to all instance attribute names in compiled
1138- templates. This is related to the arbitrary baseclass change. [TR]
1139-
1140- - shifted instance attribute setup to _initCheetahAttributes() method. This
1141- is related to the arbitrary baseclass change. [TR]
1142-
1143- - made it possible to use full expressions in the #extends directive, rather
1144- than just dotted names. This allows you to do things like this:
1145-
1146- #from xx.TemplateRepository import getTemplateClass
1147- #extends getTemplateClass('someName')
1148-
1149- I don't expect this to be used much. I needed it for a wiki system in
1150- which the baseclasses for the templates are dynamically compiled at run
1151- time and are not available via simple imports. [TR]
1152-
1153- - added compiler setting autoImportForExtendDirective=True, so this existing
1154- default behaviour can be turned off when needed. [TR]
1155-
1156- - fixed a bug in the parsing of single-line #def's and #block's when they
1157- are enclosed within #if ... #end if. Reported by Marcin Gajda [TR]
1158-
1159- - tweak to remove needless write('') calls in generated code [TR]
1160-
1161- The command line tool (CheetahWrapper.py):
1162- - added code to cleanup trailing slashes on path arguments (code originally
1163- from Mike Orr) [TR]
1164- - turned on the ImportHooks by default for the 'cheetah fill' command. See the
1165- discussion on the email list [TR]
1166-
1167- ImportHooks:
1168- - fixed a name error bug in the ImportHooks [TR]
1169+ See http://www.cheetahtemplate.org/CHANGES.txt for full details
1170+
1171
1172 Platform: UNKNOWN
1173 Classifier: Development Status :: 5 - Production/Stable
1174
1175=== removed file 'README'
1176--- README 2006-07-26 22:03:15 +0000
1177+++ README 1970-01-01 00:00:00 +0000
1178@@ -1,51 +0,0 @@
1179-Cheetah is an open source template engine and code generation tool.
1180-
1181-It can be used standalone or combined with other tools and frameworks. Web
1182-development is its principle use, but Cheetah is very flexible and is also being
1183-used to generate C++ game code, Java, sql, form emails and even Python code.
1184-
1185-Documentation
1186-================================================================================
1187-For a high-level introduction to Cheetah please refer to the User\'s Guide
1188-at http://cheetahtemplate.org/learn.html
1189-
1190-Mailing list
1191-================================================================================
1192-cheetahtemplate-discuss@lists.sourceforge.net
1193-Subscribe at http://lists.sourceforge.net/lists/listinfo/cheetahtemplate-discuss
1194-
1195-Credits
1196-================================================================================
1197-http://cheetahtemplate.org/credits.html
1198-
1199-Praise
1200-================================================================================
1201-"I\'m enamored with Cheetah" - Sam Ruby, senior member of IBM Emerging
1202-Technologies Group & director of Apache Software Foundation
1203-
1204-"Give Cheetah a try. You won\'t regret it. ... Cheetah is a truly powerful
1205-system. ... Cheetah is a serious contender for the 'best of breed' Python
1206-templating." - Alex Martelli
1207-
1208-"People with a strong PHP background absolutely love Cheetah for being Smarty,
1209-but much, much better." - Marek Baczynski
1210-
1211-"I am using Smarty and I know it very well, but compiled Cheetah Templates with
1212-its inheritance approach is much powerful and easier to use than Smarty." -
1213-Jaroslaw Zabiello
1214-
1215-"There is no better solution than Cheetah" - Wilk
1216-
1217-"A cheetah template can inherit from a python class, or a cheetah template, and
1218-a Python class can inherit from a cheetah template. This brings the full power
1219-of OO programming facilities to the templating system, and simply blows away
1220-other templating systems" - Mike Meyer
1221-
1222-"Cheetah has successfully been introduced as a replacement for the overweight
1223-XSL Templates for code generation. Despite the power of XSL (and notably XPath
1224-expressions), code generation is better suited to Cheetah as templates are much
1225-easier to implement and manage." - The FEAR development team
1226- (http://fear.sourceforge.net/docs/latest/guide/Build.html#id2550573)
1227-
1228-"I\'ve used Cheetah quite a bit and it\'s a very good package" - Kevin Dangoor,
1229-lead developer of TurboGears.
1230
1231=== added file 'README.markdown'
1232--- README.markdown 1970-01-01 00:00:00 +0000
1233+++ README.markdown 2010-10-19 20:12:51 +0000
1234@@ -0,0 +1,51 @@
1235+Cheetah is an open source template engine and code generation tool.
1236+
1237+It can be used standalone or combined with other tools and frameworks. Web
1238+development is its principle use, but Cheetah is very flexible and is also being
1239+used to generate C++ game code, Java, sql, form emails and even Python code.
1240+
1241+Documentation
1242+================================================================================
1243+For a high-level introduction to Cheetah please refer to the User\'s Guide
1244+at http://cheetahtemplate.org/learn.html
1245+
1246+Mailing list
1247+================================================================================
1248+cheetahtemplate-discuss@lists.sourceforge.net
1249+Subscribe at http://lists.sourceforge.net/lists/listinfo/cheetahtemplate-discuss
1250+
1251+Credits
1252+================================================================================
1253+http://cheetahtemplate.org/credits.html
1254+
1255+Praise
1256+================================================================================
1257+"I\'m enamored with Cheetah" - Sam Ruby, senior member of IBM Emerging
1258+Technologies Group & director of Apache Software Foundation
1259+
1260+"Give Cheetah a try. You won\'t regret it. ... Cheetah is a truly powerful
1261+system. ... Cheetah is a serious contender for the 'best of breed' Python
1262+templating." - Alex Martelli
1263+
1264+"People with a strong PHP background absolutely love Cheetah for being Smarty,
1265+but much, much better." - Marek Baczynski
1266+
1267+"I am using Smarty and I know it very well, but compiled Cheetah Templates with
1268+its inheritance approach is much powerful and easier to use than Smarty." -
1269+Jaroslaw Zabiello
1270+
1271+"There is no better solution than Cheetah" - Wilk
1272+
1273+"A cheetah template can inherit from a python class, or a cheetah template, and
1274+a Python class can inherit from a cheetah template. This brings the full power
1275+of OO programming facilities to the templating system, and simply blows away
1276+other templating systems" - Mike Meyer
1277+
1278+"Cheetah has successfully been introduced as a replacement for the overweight
1279+XSL Templates for code generation. Despite the power of XSL (and notably XPath
1280+expressions), code generation is better suited to Cheetah as templates are much
1281+easier to implement and manage." - The FEAR development team
1282+ (http://fear.sourceforge.net/docs/latest/guide/Build.html#id2550573)
1283+
1284+"I\'ve used Cheetah quite a bit and it\'s a very good package" - Kevin Dangoor,
1285+lead developer of TurboGears.
1286
1287=== modified file 'SetupConfig.py'
1288--- SetupConfig.py 2006-07-26 22:03:15 +0000
1289+++ SetupConfig.py 2010-10-19 20:12:51 +0000
1290@@ -1,17 +1,18 @@
1291 #-------Main Package Settings-----------#
1292-name = "Cheetah"
1293-from src.Version import Version as version
1294-maintainer = "Tavis Rudd"
1295+import sys
1296+
1297+name = 'Cheetah'
1298+from cheetah.Version import Version as version
1299+maintainer = "R. Tyler Ballance"
1300 author = "Tavis Rudd"
1301 author_email = "cheetahtemplate-discuss@lists.sf.net"
1302-url = "http://www.CheetahTemplate.org/"
1303+url = "http://www.cheetahtemplate.org/"
1304 packages = ['Cheetah',
1305 'Cheetah.Macros',
1306 'Cheetah.Templates',
1307 'Cheetah.Tests',
1308 'Cheetah.Tools',
1309 'Cheetah.Utils',
1310- 'Cheetah.Utils.optik',
1311 ]
1312 classifiers = [line.strip() for line in '''\
1313 #Development Status :: 4 - Beta
1314@@ -28,30 +29,50 @@
1315 Topic :: Software Development :: Libraries :: Python Modules
1316 Topic :: Software Development :: User Interfaces
1317 Topic :: Text Processing'''.splitlines() if not line.strip().startswith('#')]
1318-del line
1319
1320-package_dir = {'Cheetah':'src'}
1321+package_dir = {'Cheetah':'cheetah'}
1322
1323 import os
1324 import os.path
1325 from distutils.core import Extension
1326
1327-## we only assume the presence of a c compiler on Posix systems, NT people will
1328-# have to enable this manually.
1329-if os.name == 'posix':
1330- ext_modules=[Extension("Cheetah._namemapper", [os.path.join("src" ,"_namemapper.c")]
1331- )
1332- ]
1333-else:
1334- ext_modules=[]
1335-
1336+ext_modules=[
1337+ Extension("Cheetah._namemapper",
1338+ [os.path.join('cheetah', 'c', '_namemapper.c')]),
1339+ # Extension("Cheetah._verifytype",
1340+ # [os.path.join('cheetah', 'c', '_verifytype.c')]),
1341+ # Extension("Cheetah._filters",
1342+ # [os.path.join('cheetah', 'c', '_filters.c')]),
1343+ # Extension('Cheetah._template',
1344+ # [os.path.join('cheetah', 'c', '_template.c')]),
1345+ ]
1346
1347 ## Data Files and Scripts
1348-scripts = ['bin/cheetah-compile',
1349+scripts = ('bin/cheetah-compile',
1350 'bin/cheetah',
1351- ]
1352-data_files = ['recursive: src *.tmpl *.txt LICENSE README TODO CHANGES',
1353- ]
1354+ 'bin/cheetah-analyze',
1355+ )
1356+
1357+data_files = ['recursive: cheetah *.tmpl *.txt LICENSE README TODO CHANGES',]
1358+
1359+if not os.getenv('CHEETAH_INSTALL_WITHOUT_SETUPTOOLS'):
1360+ try:
1361+ from setuptools import setup
1362+ install_requires = [
1363+ "Markdown >= 2.0.1",
1364+ ]
1365+ if sys.platform == 'win32':
1366+ # use 'entry_points' instead of 'scripts'
1367+ del scripts
1368+ entry_points = {
1369+ 'console_scripts': [
1370+ 'cheetah = Cheetah.CheetahWrapper:_cheetah',
1371+ 'cheetah-compile = Cheetah.CheetahWrapper:_cheetah_compile',
1372+ ]
1373+ }
1374+ except ImportError:
1375+ print('Not using setuptools, so we cannot install the Markdown dependency')
1376+
1377
1378 description = "Cheetah is a template engine and code generation tool."
1379
1380@@ -64,7 +85,7 @@
1381 Documentation
1382 ================================================================================
1383 For a high-level introduction to Cheetah please refer to the User\'s Guide
1384-at http://cheetahtemplate.org/learn.html
1385+at http://www.cheetahtemplate.org/learn.html
1386
1387 Mailing list
1388 ================================================================================
1389@@ -73,48 +94,10 @@
1390
1391 Credits
1392 ================================================================================
1393-http://cheetahtemplate.org/credits.html
1394-
1395-Praise
1396-================================================================================
1397-"I\'m enamored with Cheetah" - Sam Ruby, senior member of IBM Emerging
1398-Technologies Group & director of Apache Software Foundation
1399-
1400-"Give Cheetah a try. You won\'t regret it. ... Cheetah is a truly powerful
1401-system. ... Cheetah is a serious contender for the 'best of breed' Python
1402-templating." - Alex Martelli
1403-
1404-"People with a strong PHP background absolutely love Cheetah for being Smarty,
1405-but much, much better." - Marek Baczynski
1406-
1407-"I am using Smarty and I know it very well, but compiled Cheetah Templates with
1408-its inheritance approach is much powerful and easier to use than Smarty." -
1409-Jaroslaw Zabiello
1410-
1411-"There is no better solution than Cheetah" - Wilk
1412-
1413-"A cheetah template can inherit from a python class, or a cheetah template, and
1414-a Python class can inherit from a cheetah template. This brings the full power
1415-of OO programming facilities to the templating system, and simply blows away
1416-other templating systems" - Mike Meyer
1417-
1418-"Cheetah has successfully been introduced as a replacement for the overweight
1419-XSL Templates for code generation. Despite the power of XSL (and notably XPath
1420-expressions), code generation is better suited to Cheetah as templates are much
1421-easier to implement and manage." - The FEAR development team
1422- (http://fear.sourceforge.net/docs/latest/guide/Build.html#id2550573)
1423-
1424-"I\'ve used Cheetah quite a bit and it\'s a very good package" - Kevin Dangoor,
1425-lead developer of TurboGears.
1426+http://www.cheetahtemplate.org/credits.html
1427
1428 Recent Changes
1429 ================================================================================
1430-See http://cheetahtemplate.org/docs/CHANGES for full details.
1431+See http://www.cheetahtemplate.org/CHANGES.txt for full details
1432
1433 '''
1434-try:
1435- recentChanges = open('CHANGES').read().split('\n1.0')[0]
1436- long_description += recentChanges
1437- del recentChanges
1438-except:
1439- pass
1440
1441=== modified file 'SetupTools.py'
1442--- SetupTools.py 2007-11-18 14:59:06 +0000
1443+++ SetupTools.py 2010-10-19 20:12:51 +0000
1444@@ -1,44 +1,61 @@
1445 #!/usr/bin/env python
1446-# $Id: SetupTools.py,v 1.9 2007/11/03 19:44:38 tavis_rudd Exp $
1447-"""Some tools for extending and working with distutils
1448-
1449-CREDITS: This module borrows code and ideas from M.A. Lemburg's excellent setup
1450-tools for the mxBase package.
1451-
1452-"""
1453-
1454-__author__ = "Tavis Rudd <tavis@damnsimple.com>"
1455-__version__ = "$Revision: 1.9 $"[11:-2]
1456-
1457 import os
1458 from os import listdir
1459 import os.path
1460 from os.path import exists, isdir, isfile, join, splitext
1461+import sys
1462 import types
1463 import glob
1464 import string
1465 import traceback
1466
1467 from distutils.core import setup
1468-if 'CHEETAH_USE_SETUPTOOLS' in os.environ:
1469- # @@TR: Please note that this is for testing purposes only! PEAK setuptools
1470- # is not required or recommended for installing Cheetah. Downstream
1471- # package managers (linux distros, etc.) should *not* enable this.
1472+if not os.getenv('CHEETAH_INSTALL_WITHOUT_SETUPTOOLS'):
1473 try:
1474- # use http://peak.telecommunity.com/DevCenter/setuptools if it's installed
1475- # requires Py >=2.3
1476 from setuptools import setup
1477 except ImportError:
1478 from distutils.core import setup
1479
1480 from distutils.core import Command
1481+from distutils.command.build_ext import build_ext
1482 from distutils.command.install_data import install_data
1483+from distutils.errors import CCompilerError, DistutilsExecError, \
1484+ DistutilsPlatformError
1485
1486 #imports from Cheetah ...
1487-from src.FileUtils import findFiles
1488+from cheetah.FileUtils import findFiles
1489+
1490+if sys.platform == 'win32' and sys.version_info > (2, 6):
1491+ # 2.6's distutils.msvc9compiler can raise an IOError when failing to
1492+ # find the compiler
1493+ ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError,
1494+ IOError)
1495+else:
1496+ ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
1497
1498 ##################################################
1499 ## CLASSES ##
1500+
1501+class BuildFailed(Exception):
1502+ pass
1503+
1504+class mod_build_ext(build_ext):
1505+ """A modified version of the distutils build_ext command that raises an
1506+ exception when building of the extension fails.
1507+ """
1508+
1509+ def run(self):
1510+ try:
1511+ build_ext.run(self)
1512+ except DistutilsPlatformError, x:
1513+ raise BuildFailed(x)
1514+
1515+ def build_extension(self, ext):
1516+ try:
1517+ build_ext.build_extension(self, ext)
1518+ except ext_errors, x:
1519+ raise BuildFailed(x)
1520+
1521
1522 class mod_install_data(install_data):
1523 """A modified version of the disutils install_data command that allows data
1524@@ -60,8 +77,8 @@
1525 data_files = self.get_inputs()
1526
1527 for entry in data_files:
1528- if type(entry) != types.StringType:
1529- raise ValueError, 'The entries in "data_files" must be strings'
1530+ if not isinstance(entry, basestring):
1531+ raise ValueError('The entries in "data_files" must be strings')
1532
1533 entry = string.join(string.split(entry, '/'), os.sep)
1534 # entry is a filename or glob pattern
1535@@ -117,25 +134,39 @@
1536 """
1537 # Build parameter dictionary
1538 kws = {}
1539+ newkws = {}
1540 for configuration in configurations:
1541 kws.update(vars(configuration))
1542 for name, value in kws.items():
1543- if name[:1] == '_' or \
1544- type(value) not in (types.StringType,
1545- types.ListType,
1546- types.TupleType,
1547- types.DictType,
1548- types.IntType,
1549- ):
1550- del kws[name]
1551+ if name[:1] == '_':
1552+ continue
1553+ if not isinstance(value, (basestring, list, tuple, dict, int)):
1554+ continue
1555+ newkws[name] = value
1556+ kws = newkws
1557
1558 # Add setup extensions
1559 cmdclasses = {
1560+ 'build_ext': mod_build_ext,
1561 'install_data': mod_install_data,
1562 }
1563
1564 kws['cmdclass'] = cmdclasses
1565
1566 # Invoke distutils setup
1567- apply(setup, (), kws)
1568+ try:
1569+ setup(**kws)
1570+ except BuildFailed, x:
1571+ print("One or more C extensions failed to build.")
1572+ print("Details: %s" % x)
1573+ if os.environ.get('CHEETAH_C_EXTENSIONS_REQUIRED'):
1574+ raise x
1575+ print("Retrying without C extensions enabled.")
1576+
1577+ del kws['ext_modules']
1578+ setup(**kws)
1579+
1580+ print("One or more C extensions failed to build.")
1581+ print("Performance enhancements will not be available.")
1582+ print("Pure Python installation succeeded.")
1583
1584
1585=== modified file 'TODO'
1586--- TODO 2007-07-11 14:45:50 +0000
1587+++ TODO 2010-10-19 20:12:51 +0000
1588@@ -1,16 +1,9 @@
1589-Cheetah TODO list
1590------------------
1591-* If you are working on a task please put your initials at the end of the
1592- description
1593-* When a task is completed please remember to note it in the CHANGES file
1594-* Unresolved bugs are listed in the BUGS file. Resolved bugs are be listed
1595- in the CHANGES file if the bug is considered significant enough and it
1596- affected a released version of Cheetah.
1597-
1598-Required for Cheetah 2.0
1599-========================
1600-- Replace Optik with Python's optparse. Optik license has been removed from
1601- Users' Guide.
1602+NOTE: Please see http://bugs.cheetahtemplate.org
1603+ for future feature requests/bugs/TODO
1604+
1605+
1606+===============================================================================
1607+===============================================================================
1608
1609 Desired for Cheetah 2.0
1610 =======================
1611@@ -38,7 +31,6 @@
1612 leak from one fill to the next.
1613
1614 - CheetahWrapper stuff: (MO)
1615- * "cheetah compile --shbang '#!/usr/bin/python2.2'"
1616 * "cheetah preview [options] [FILES]" print template-specific portion of main
1617 method(s) to stdout, with line numbers based on the .py template module.
1618 Make a Template method to do the same thing, a la .generatedModuleCode().
1619
1620=== modified file 'bin/cheetah'
1621--- bin/cheetah 2004-03-30 18:47:41 +0000
1622+++ bin/cheetah 2010-10-19 20:12:51 +0000
1623@@ -1,3 +1,3 @@
1624 #!/usr/bin/env python
1625-from Cheetah.CheetahWrapper import CheetahWrapper
1626-CheetahWrapper().main()
1627+from Cheetah.CheetahWrapper import _cheetah
1628+_cheetah()
1629
1630=== added file 'bin/cheetah-analyze'
1631--- bin/cheetah-analyze 1970-01-01 00:00:00 +0000
1632+++ bin/cheetah-analyze 2010-10-19 20:12:51 +0000
1633@@ -0,0 +1,6 @@
1634+#!/usr/bin/env python
1635+
1636+from Cheetah import DirectiveAnalyzer
1637+
1638+if __name__ == '__main__':
1639+ DirectiveAnalyzer.main()
1640
1641=== modified file 'bin/cheetah-compile'
1642--- bin/cheetah-compile 2004-03-30 18:47:41 +0000
1643+++ bin/cheetah-compile 2010-10-19 20:12:51 +0000
1644@@ -1,5 +1,3 @@
1645 #!/usr/bin/env python
1646-import sys
1647-from Cheetah.CheetahWrapper import CheetahWrapper
1648-sys.argv.insert(1, "compile")
1649-CheetahWrapper().main()
1650+from Cheetah.CheetahWrapper import _cheetah_compile
1651+_cheetah_compile()
1652
1653=== removed file 'cachedCompile.py'
1654--- cachedCompile.py 2006-07-26 22:03:15 +0000
1655+++ cachedCompile.py 1970-01-01 00:00:00 +0000
1656@@ -1,11 +0,0 @@
1657-from Cheetah.Template import Template
1658-source = file('/home/tavis/cvs_working/Cheetah/src/Templates/SkeletonPage.tmpl').read()
1659-##klass = Template.compile(source,
1660-## cacheCompilationResults=1,
1661-## useCache=1,
1662-## )
1663-for i in range(2000):
1664- klass = Template.compile(source,
1665- cacheCompilationResults=0,
1666- useCache=0,
1667- )
1668
1669=== added directory 'cheetah'
1670=== removed file 'cheetah-mem.py'
1671--- cheetah-mem.py 2006-07-26 22:03:15 +0000
1672+++ cheetah-mem.py 1970-01-01 00:00:00 +0000
1673@@ -1,22 +0,0 @@
1674-from Cheetah.Template import Template
1675-import gc
1676-
1677-src = open('/tmp/z.py').read()
1678-tclass = Template.compile(src)
1679-#t = Template(src)
1680-nr = 0
1681-while True:
1682- #tclass = Template.compile(src)
1683- #t = tclass()
1684- t = Template(src)
1685-
1686- output = t.respond()
1687-
1688- nr += 1
1689- if not nr % 10000:
1690- print
1691- #print 'collect'
1692- #gc.collect()
1693-
1694- print 'tclass id', id(t.__class__)
1695- print 'cache size', len(Template._CHEETAH_compileCache.keys())
1696
1697=== added file 'cheetah/CacheRegion.py'
1698--- cheetah/CacheRegion.py 1970-01-01 00:00:00 +0000
1699+++ cheetah/CacheRegion.py 2010-10-19 20:12:51 +0000
1700@@ -0,0 +1,136 @@
1701+# $Id: CacheRegion.py,v 1.3 2006/01/28 04:19:30 tavis_rudd Exp $
1702+'''
1703+Cache holder classes for Cheetah:
1704+
1705+Cache regions are defined using the #cache Cheetah directive. Each
1706+cache region can be viewed as a dictionary (keyed by cacheRegionID)
1707+handling at least one cache item (the default one). It's possible to add
1708+cacheItems in a region by using the `varyBy` #cache directive parameter as
1709+in the following example::
1710+ #def getArticle
1711+ this is the article content.
1712+ #end def
1713+
1714+ #cache varyBy=$getArticleID()
1715+ $getArticle($getArticleID())
1716+ #end cache
1717+
1718+The code above will generate a CacheRegion and add new cacheItem for each value
1719+of $getArticleID().
1720+'''
1721+
1722+try:
1723+ from hashlib import md5
1724+except ImportError:
1725+ from md5 import md5
1726+
1727+import time
1728+import Cheetah.CacheStore
1729+
1730+class CacheItem(object):
1731+ '''
1732+ A CacheItem is a container storing:
1733+
1734+ - cacheID (string)
1735+ - refreshTime (timestamp or None) : last time the cache was refreshed
1736+ - data (string) : the content of the cache
1737+ '''
1738+
1739+ def __init__(self, cacheItemID, cacheStore):
1740+ self._cacheItemID = cacheItemID
1741+ self._cacheStore = cacheStore
1742+ self._refreshTime = None
1743+ self._expiryTime = 0
1744+
1745+ def hasExpired(self):
1746+ return (self._expiryTime and time.time() > self._expiryTime)
1747+
1748+ def setExpiryTime(self, time):
1749+ self._expiryTime = time
1750+
1751+ def getExpiryTime(self):
1752+ return self._expiryTime
1753+
1754+ def setData(self, data):
1755+ self._refreshTime = time.time()
1756+ self._cacheStore.set(self._cacheItemID, data, self._expiryTime)
1757+
1758+ def getRefreshTime(self):
1759+ return self._refreshTime
1760+
1761+ def getData(self):
1762+ assert self._refreshTime
1763+ return self._cacheStore.get(self._cacheItemID)
1764+
1765+ def renderOutput(self):
1766+ """Can be overridden to implement edge-caching"""
1767+ return self.getData() or ""
1768+
1769+ def clear(self):
1770+ self._cacheStore.delete(self._cacheItemID)
1771+ self._refreshTime = None
1772+
1773+class _CacheDataStoreWrapper(object):
1774+ def __init__(self, dataStore, keyPrefix):
1775+ self._dataStore = dataStore
1776+ self._keyPrefix = keyPrefix
1777+
1778+ def get(self, key):
1779+ return self._dataStore.get(self._keyPrefix+key)
1780+
1781+ def delete(self, key):
1782+ self._dataStore.delete(self._keyPrefix+key)
1783+
1784+ def set(self, key, val, time=0):
1785+ self._dataStore.set(self._keyPrefix+key, val, time=time)
1786+
1787+class CacheRegion(object):
1788+ '''
1789+ A `CacheRegion` stores some `CacheItem` instances.
1790+
1791+ This implementation stores the data in the memory of the current process.
1792+ If you need a more advanced data store, create a cacheStore class that works
1793+ with Cheetah's CacheStore protocol and provide it as the cacheStore argument
1794+ to __init__. For example you could use
1795+ Cheetah.CacheStore.MemcachedCacheStore, a wrapper around the Python
1796+ memcached API (http://www.danga.com/memcached).
1797+ '''
1798+ _cacheItemClass = CacheItem
1799+
1800+ def __init__(self, regionID, templateCacheIdPrefix='', cacheStore=None):
1801+ self._isNew = True
1802+ self._regionID = regionID
1803+ self._templateCacheIdPrefix = templateCacheIdPrefix
1804+ if not cacheStore:
1805+ cacheStore = Cheetah.CacheStore.MemoryCacheStore()
1806+ self._cacheStore = cacheStore
1807+ self._wrappedCacheDataStore = _CacheDataStoreWrapper(
1808+ cacheStore, keyPrefix=templateCacheIdPrefix+':'+regionID+':')
1809+ self._cacheItems = {}
1810+
1811+ def isNew(self):
1812+ return self._isNew
1813+
1814+ def clear(self):
1815+ " drop all the caches stored in this cache region "
1816+ for cacheItemId in self._cacheItems.keys():
1817+ cacheItem = self._cacheItems[cacheItemId]
1818+ cacheItem.clear()
1819+ del self._cacheItems[cacheItemId]
1820+
1821+ def getCacheItem(self, cacheItemID):
1822+ """ Lazy access to a cacheItem
1823+
1824+ Try to find a cache in the stored caches. If it doesn't
1825+ exist, it's created.
1826+
1827+ Returns a `CacheItem` instance.
1828+ """
1829+ cacheItemID = md5(str(cacheItemID)).hexdigest()
1830+
1831+ if cacheItemID not in self._cacheItems:
1832+ cacheItem = self._cacheItemClass(
1833+ cacheItemID=cacheItemID, cacheStore=self._wrappedCacheDataStore)
1834+ self._cacheItems[cacheItemID] = cacheItem
1835+ self._isNew = False
1836+ return self._cacheItems[cacheItemID]
1837
1838=== added file 'cheetah/CacheStore.py'
1839--- cheetah/CacheStore.py 1970-01-01 00:00:00 +0000
1840+++ cheetah/CacheStore.py 2010-10-19 20:12:51 +0000
1841@@ -0,0 +1,106 @@
1842+'''
1843+Provides several CacheStore backends for Cheetah's caching framework. The
1844+methods provided by these classes have the same semantics as those in the
1845+python-memcached API, except for their return values:
1846+
1847+set(key, val, time=0)
1848+ set the value unconditionally
1849+add(key, val, time=0)
1850+ set only if the server doesn't already have this key
1851+replace(key, val, time=0)
1852+ set only if the server already have this key
1853+get(key, val)
1854+ returns val or raises a KeyError
1855+delete(key)
1856+ deletes or raises a KeyError
1857+'''
1858+import time
1859+
1860+class Error(Exception):
1861+ pass
1862+
1863+class AbstractCacheStore(object):
1864+
1865+ def set(self, key, val, time=None):
1866+ raise NotImplementedError
1867+
1868+ def add(self, key, val, time=None):
1869+ raise NotImplementedError
1870+
1871+ def replace(self, key, val, time=None):
1872+ raise NotImplementedError
1873+
1874+ def delete(self, key):
1875+ raise NotImplementedError
1876+
1877+ def get(self, key):
1878+ raise NotImplementedError
1879+
1880+class MemoryCacheStore(AbstractCacheStore):
1881+ def __init__(self):
1882+ self._data = {}
1883+
1884+ def set(self, key, val, time=0):
1885+ self._data[key] = (val, time)
1886+
1887+ def add(self, key, val, time=0):
1888+ if key in self._data:
1889+ raise Error('a value for key %r is already in the cache'%key)
1890+ self._data[key] = (val, time)
1891+
1892+ def replace(self, key, val, time=0):
1893+ if key in self._data:
1894+ raise Error('a value for key %r is already in the cache'%key)
1895+ self._data[key] = (val, time)
1896+
1897+ def delete(self, key):
1898+ del self._data[key]
1899+
1900+ def get(self, key):
1901+ (val, exptime) = self._data[key]
1902+ if exptime and time.time() > exptime:
1903+ del self._data[key]
1904+ raise KeyError(key)
1905+ else:
1906+ return val
1907+
1908+ def clear(self):
1909+ self._data.clear()
1910+
1911+class MemcachedCacheStore(AbstractCacheStore):
1912+ servers = ('127.0.0.1:11211')
1913+ def __init__(self, servers=None, debug=False):
1914+ if servers is None:
1915+ servers = self.servers
1916+ from memcache import Client as MemcachedClient
1917+ self._client = MemcachedClient(servers, debug)
1918+
1919+ def set(self, key, val, time=0):
1920+ self._client.set(key, val, time)
1921+
1922+ def add(self, key, val, time=0):
1923+ res = self._client.add(key, val, time)
1924+ if not res:
1925+ raise Error('a value for key %r is already in the cache'%key)
1926+ self._data[key] = (val, time)
1927+
1928+ def replace(self, key, val, time=0):
1929+ res = self._client.replace(key, val, time)
1930+ if not res:
1931+ raise Error('a value for key %r is already in the cache'%key)
1932+ self._data[key] = (val, time)
1933+
1934+ def delete(self, key):
1935+ res = self._client.delete(key, time=0)
1936+ if not res:
1937+ raise KeyError(key)
1938+
1939+ def get(self, key):
1940+ val = self._client.get(key)
1941+ if val is None:
1942+ raise KeyError(key)
1943+ else:
1944+ return val
1945+
1946+ def clear(self):
1947+ self._client.flush_all()
1948
1949=== added file 'cheetah/CheetahWrapper.py'
1950--- cheetah/CheetahWrapper.py 1970-01-01 00:00:00 +0000
1951+++ cheetah/CheetahWrapper.py 2010-10-19 20:12:51 +0000
1952@@ -0,0 +1,632 @@
1953+# $Id: CheetahWrapper.py,v 1.26 2007/10/02 01:22:04 tavis_rudd Exp $
1954+"""Cheetah command-line interface.
1955+
1956+2002-09-03 MSO: Total rewrite.
1957+2002-09-04 MSO: Bugfix, compile command was using wrong output ext.
1958+2002-11-08 MSO: Another rewrite.
1959+
1960+Meta-Data
1961+================================================================================
1962+Author: Tavis Rudd <tavis@damnsimple.com> and Mike Orr <sluggoster@gmail.com>>
1963+Version: $Revision: 1.26 $
1964+Start Date: 2001/03/30
1965+Last Revision Date: $Date: 2007/10/02 01:22:04 $
1966+"""
1967+__author__ = "Tavis Rudd <tavis@damnsimple.com> and Mike Orr <sluggoster@gmail.com>"
1968+__revision__ = "$Revision: 1.26 $"[11:-2]
1969+
1970+import getopt, glob, os, pprint, re, shutil, sys
1971+import cPickle as pickle
1972+from optparse import OptionParser
1973+
1974+from Cheetah.Version import Version
1975+from Cheetah.Template import Template, DEFAULT_COMPILER_SETTINGS
1976+from Cheetah.Utils.Misc import mkdirsWithPyInitFiles
1977+
1978+optionDashesRE = re.compile( R"^-{1,2}" )
1979+moduleNameRE = re.compile( R"^[a-zA-Z_][a-zA-Z_0-9]*$" )
1980+
1981+def fprintfMessage(stream, format, *args):
1982+ if format[-1:] == '^':
1983+ format = format[:-1]
1984+ else:
1985+ format += '\n'
1986+ if args:
1987+ message = format % args
1988+ else:
1989+ message = format
1990+ stream.write(message)
1991+
1992+class Error(Exception):
1993+ pass
1994+
1995+
1996+class Bundle:
1997+ """Wrap the source, destination and backup paths in one neat little class.
1998+ Used by CheetahWrapper.getBundles().
1999+ """
2000+ def __init__(self, **kw):
2001+ self.__dict__.update(kw)
2002+
2003+ def __repr__(self):
2004+ return "<Bundle %r>" % self.__dict__
2005+
2006+
2007+##################################################
2008+## USAGE FUNCTION & MESSAGES
2009+
2010+def usage(usageMessage, errorMessage="", out=sys.stderr):
2011+ """Write help text, an optional error message, and abort the program.
2012+ """
2013+ out.write(WRAPPER_TOP)
2014+ out.write(usageMessage)
2015+ exitStatus = 0
2016+ if errorMessage:
2017+ out.write('\n')
2018+ out.write("*** USAGE ERROR ***: %s\n" % errorMessage)
2019+ exitStatus = 1
2020+ sys.exit(exitStatus)
2021+
2022+
2023+WRAPPER_TOP = """\
2024+ __ ____________ __
2025+ \ \/ \/ /
2026+ \/ * * \/ CHEETAH %(Version)s Command-Line Tool
2027+ \ | /
2028+ \ ==----== / by Tavis Rudd <tavis@damnsimple.com>
2029+ \__________/ and Mike Orr <sluggoster@gmail.com>
2030+
2031+""" % globals()
2032+
2033+
2034+HELP_PAGE1 = """\
2035+USAGE:
2036+------
2037+ cheetah compile [options] [FILES ...] : Compile template definitions
2038+ cheetah fill [options] [FILES ...] : Fill template definitions
2039+ cheetah help : Print this help message
2040+ cheetah options : Print options help message
2041+ cheetah test [options] : Run Cheetah's regression tests
2042+ : (same as for unittest)
2043+ cheetah version : Print Cheetah version number
2044+
2045+You may abbreviate the command to the first letter; e.g., 'h' == 'help'.
2046+If FILES is a single "-", read standard input and write standard output.
2047+Run "cheetah options" for the list of valid options.
2048+"""
2049+
2050+##################################################
2051+## CheetahWrapper CLASS
2052+
2053+class CheetahWrapper(object):
2054+ MAKE_BACKUPS = True
2055+ BACKUP_SUFFIX = ".bak"
2056+ _templateClass = None
2057+ _compilerSettings = None
2058+
2059+ def __init__(self):
2060+ self.progName = None
2061+ self.command = None
2062+ self.opts = None
2063+ self.pathArgs = None
2064+ self.sourceFiles = []
2065+ self.searchList = []
2066+ self.parser = None
2067+
2068+ ##################################################
2069+ ## MAIN ROUTINE
2070+
2071+ def main(self, argv=None):
2072+ """The main program controller."""
2073+
2074+ if argv is None:
2075+ argv = sys.argv
2076+
2077+ # Step 1: Determine the command and arguments.
2078+ try:
2079+ self.progName = progName = os.path.basename(argv[0])
2080+ self.command = command = optionDashesRE.sub("", argv[1])
2081+ if command == 'test':
2082+ self.testOpts = argv[2:]
2083+ else:
2084+ self.parseOpts(argv[2:])
2085+ except IndexError:
2086+ usage(HELP_PAGE1, "not enough command-line arguments")
2087+
2088+ # Step 2: Call the command
2089+ meths = (self.compile, self.fill, self.help, self.options,
2090+ self.test, self.version)
2091+ for meth in meths:
2092+ methName = meth.__name__
2093+ # Or meth.im_func.func_name
2094+ # Or meth.func_name (Python >= 2.1 only, sometimes works on 2.0)
2095+ methInitial = methName[0]
2096+ if command in (methName, methInitial):
2097+ sys.argv[0] += (" " + methName)
2098+ # @@MO: I don't necessarily agree sys.argv[0] should be
2099+ # modified.
2100+ meth()
2101+ return
2102+ # If none of the commands matched.
2103+ usage(HELP_PAGE1, "unknown command '%s'" % command)
2104+
2105+ def parseOpts(self, args):
2106+ C, D, W = self.chatter, self.debug, self.warn
2107+ self.isCompile = isCompile = self.command[0] == 'c'
2108+ defaultOext = isCompile and ".py" or ".html"
2109+ self.parser = OptionParser()
2110+ pao = self.parser.add_option
2111+ pao("--idir", action="store", dest="idir", default='', help='Input directory (defaults to current directory)')
2112+ pao("--odir", action="store", dest="odir", default="", help='Output directory (defaults to current directory)')
2113+ pao("--iext", action="store", dest="iext", default=".tmpl", help='File input extension (defaults: compile: .tmpl, fill: .tmpl)')
2114+ pao("--oext", action="store", dest="oext", default=defaultOext, help='File output extension (defaults: compile: .py, fill: .html)')
2115+ pao("-R", action="store_true", dest="recurse", default=False, help='Recurse through subdirectories looking for input files')
2116+ pao("--stdout", "-p", action="store_true", dest="stdout", default=False, help='Send output to stdout instead of writing to a file')
2117+ pao("--quiet", action="store_false", dest="verbose", default=True, help='Do not print informational messages to stdout')
2118+ pao("--debug", action="store_true", dest="debug", default=False, help='Print diagnostic/debug information to stderr')
2119+ pao("--env", action="store_true", dest="env", default=False, help='Pass the environment into the search list')
2120+ pao("--pickle", action="store", dest="pickle", default="", help='Unpickle FILE and pass it through in the search list')
2121+ pao("--flat", action="store_true", dest="flat", default=False, help='Do not build destination subdirectories')
2122+ pao("--nobackup", action="store_true", dest="nobackup", default=False, help='Do not make backup files when generating new ones')
2123+ pao("--settings", action="store", dest="compilerSettingsString", default=None, help='String of compiler settings to pass through, e.g. --settings="useNameMapper=False,useFilters=False"')
2124+ pao('--print-settings', action='store_true', dest='print_settings', help='Print out the list of available compiler settings')
2125+ pao("--templateAPIClass", action="store", dest="templateClassName", default=None, help='Name of a subclass of Cheetah.Template.Template to use for compilation, e.g. MyTemplateClass')
2126+ pao("--parallel", action="store", type="int", dest="parallel", default=1, help='Compile/fill templates in parallel, e.g. --parallel=4')
2127+ pao('--shbang', dest='shbang', default='#!/usr/bin/env python', help='Specify the shbang to place at the top of compiled templates, e.g. --shbang="#!/usr/bin/python2.6"')
2128+
2129+ opts, files = self.parser.parse_args(args)
2130+ self.opts = opts
2131+ if sys.platform == "win32":
2132+ new_files = []
2133+ for spec in files:
2134+ file_list = glob.glob(spec)
2135+ if file_list:
2136+ new_files.extend(file_list)
2137+ else:
2138+ new_files.append(spec)
2139+ files = new_files
2140+ self.pathArgs = files
2141+
2142+ D("""\
2143+cheetah compile %s
2144+Options are
2145+%s
2146+Files are %s""", args, pprint.pformat(vars(opts)), files)
2147+
2148+
2149+ if opts.print_settings:
2150+ print()
2151+ print('>> Available Cheetah compiler settings:')
2152+ from Cheetah.Compiler import _DEFAULT_COMPILER_SETTINGS
2153+ listing = _DEFAULT_COMPILER_SETTINGS
2154+ listing.sort(key=lambda l: l[0][0].lower())
2155+
2156+ for l in listing:
2157+ print('\t%s (default: "%s")\t%s' % l)
2158+ sys.exit(0)
2159+
2160+ #cleanup trailing path separators
2161+ seps = [sep for sep in [os.sep, os.altsep] if sep]
2162+ for attr in ['idir', 'odir']:
2163+ for sep in seps:
2164+ path = getattr(opts, attr, None)
2165+ if path and path.endswith(sep):
2166+ path = path[:-len(sep)]
2167+ setattr(opts, attr, path)
2168+ break
2169+
2170+ self._fixExts()
2171+ if opts.env:
2172+ self.searchList.insert(0, os.environ)
2173+ if opts.pickle:
2174+ f = open(opts.pickle, 'rb')
2175+ unpickled = pickle.load(f)
2176+ f.close()
2177+ self.searchList.insert(0, unpickled)
2178+
2179+ ##################################################
2180+ ## COMMAND METHODS
2181+
2182+ def compile(self):
2183+ self._compileOrFill()
2184+
2185+ def fill(self):
2186+ from Cheetah.ImportHooks import install
2187+ install()
2188+ self._compileOrFill()
2189+
2190+ def help(self):
2191+ usage(HELP_PAGE1, "", sys.stdout)
2192+
2193+ def options(self):
2194+ return self.parser.print_help()
2195+
2196+ def test(self):
2197+ # @@MO: Ugly kludge.
2198+ TEST_WRITE_FILENAME = 'cheetah_test_file_creation_ability.tmp'
2199+ try:
2200+ f = open(TEST_WRITE_FILENAME, 'w')
2201+ except:
2202+ sys.exit("""\
2203+Cannot run the tests because you don't have write permission in the current
2204+directory. The tests need to create temporary files. Change to a directory
2205+you do have write permission to and re-run the tests.""")
2206+ else:
2207+ f.close()
2208+ os.remove(TEST_WRITE_FILENAME)
2209+ # @@MO: End ugly kludge.
2210+ from Cheetah.Tests import Test
2211+ import unittest
2212+ verbosity = 1
2213+ if '-q' in self.testOpts:
2214+ verbosity = 0
2215+ if '-v' in self.testOpts:
2216+ verbosity = 2
2217+ runner = unittest.TextTestRunner(verbosity=verbosity)
2218+ runner.run(unittest.TestSuite(Test.suites))
2219+
2220+ def version(self):
2221+ print(Version)
2222+
2223+ # If you add a command, also add it to the 'meths' variable in main().
2224+
2225+ ##################################################
2226+ ## LOGGING METHODS
2227+
2228+ def chatter(self, format, *args):
2229+ """Print a verbose message to stdout. But don't if .opts.stdout is
2230+ true or .opts.verbose is false.
2231+ """
2232+ if self.opts.stdout or not self.opts.verbose:
2233+ return
2234+ fprintfMessage(sys.stdout, format, *args)
2235+
2236+
2237+ def debug(self, format, *args):
2238+ """Print a debugging message to stderr, but don't if .debug is
2239+ false.
2240+ """
2241+ if self.opts.debug:
2242+ fprintfMessage(sys.stderr, format, *args)
2243+
2244+ def warn(self, format, *args):
2245+ """Always print a warning message to stderr.
2246+ """
2247+ fprintfMessage(sys.stderr, format, *args)
2248+
2249+ def error(self, format, *args):
2250+ """Always print a warning message to stderr and exit with an error code.
2251+ """
2252+ fprintfMessage(sys.stderr, format, *args)
2253+ sys.exit(1)
2254+
2255+ ##################################################
2256+ ## HELPER METHODS
2257+
2258+
2259+ def _fixExts(self):
2260+ assert self.opts.oext, "oext is empty!"
2261+ iext, oext = self.opts.iext, self.opts.oext
2262+ if iext and not iext.startswith("."):
2263+ self.opts.iext = "." + iext
2264+ if oext and not oext.startswith("."):
2265+ self.opts.oext = "." + oext
2266+
2267+
2268+
2269+ def _compileOrFill(self):
2270+ C, D, W = self.chatter, self.debug, self.warn
2271+ opts, files = self.opts, self.pathArgs
2272+ if files == ["-"]:
2273+ self._compileOrFillStdin()
2274+ return
2275+ elif not files and opts.recurse:
2276+ which = opts.idir and "idir" or "current"
2277+ C("Drilling down recursively from %s directory.", which)
2278+ sourceFiles = []
2279+ dir = os.path.join(self.opts.idir, os.curdir)
2280+ os.path.walk(dir, self._expandSourceFilesWalk, sourceFiles)
2281+ elif not files:
2282+ usage(HELP_PAGE1, "Neither files nor -R specified!")
2283+ else:
2284+ sourceFiles = self._expandSourceFiles(files, opts.recurse, True)
2285+ sourceFiles = [os.path.normpath(x) for x in sourceFiles]
2286+ D("All source files found: %s", sourceFiles)
2287+ bundles = self._getBundles(sourceFiles)
2288+ D("All bundles: %s", pprint.pformat(bundles))
2289+ if self.opts.flat:
2290+ self._checkForCollisions(bundles)
2291+
2292+ # In parallel mode a new process is forked for each template
2293+ # compilation, out of a pool of size self.opts.parallel. This is not
2294+ # really optimal in all cases (e.g. probably wasteful for small
2295+ # templates), but seems to work well in real life for me.
2296+ #
2297+ # It also won't work for Windows users, but I'm not going to lose any
2298+ # sleep over that.
2299+ if self.opts.parallel > 1:
2300+ bad_child_exit = 0
2301+ pid_pool = set()
2302+
2303+ def child_wait():
2304+ pid, status = os.wait()
2305+ pid_pool.remove(pid)
2306+ return os.WEXITSTATUS(status)
2307+
2308+ while bundles:
2309+ b = bundles.pop()
2310+ pid = os.fork()
2311+ if pid:
2312+ pid_pool.add(pid)
2313+ else:
2314+ self._compileOrFillBundle(b)
2315+ sys.exit(0)
2316+
2317+ if len(pid_pool) == self.opts.parallel:
2318+ bad_child_exit = child_wait()
2319+ if bad_child_exit:
2320+ break
2321+
2322+ while pid_pool:
2323+ child_exit = child_wait()
2324+ if not bad_child_exit:
2325+ bad_child_exit = child_exit
2326+
2327+ if bad_child_exit:
2328+ sys.exit("Child process failed, exited with code %d" % bad_child_exit)
2329+
2330+ else:
2331+ for b in bundles:
2332+ self._compileOrFillBundle(b)
2333+
2334+ def _checkForCollisions(self, bundles):
2335+ """Check for multiple source paths writing to the same destination
2336+ path.
2337+ """
2338+ C, D, W = self.chatter, self.debug, self.warn
2339+ isError = False
2340+ dstSources = {}
2341+ for b in bundles:
2342+ if b.dst in dstSources:
2343+ dstSources[b.dst].append(b.src)
2344+ else:
2345+ dstSources[b.dst] = [b.src]
2346+ keys = sorted(dstSources.keys())
2347+ for dst in keys:
2348+ sources = dstSources[dst]
2349+ if len(sources) > 1:
2350+ isError = True
2351+ sources.sort()
2352+ fmt = "Collision: multiple source files %s map to one destination file %s"
2353+ W(fmt, sources, dst)
2354+ if isError:
2355+ what = self.isCompile and "Compilation" or "Filling"
2356+ sys.exit("%s aborted due to collisions" % what)
2357+
2358+
2359+ def _expandSourceFilesWalk(self, arg, dir, files):
2360+ """Recursion extension for .expandSourceFiles().
2361+ This method is a callback for os.path.walk().
2362+ 'arg' is a list to which successful paths will be appended.
2363+ """
2364+ iext = self.opts.iext
2365+ for f in files:
2366+ path = os.path.join(dir, f)
2367+ if path.endswith(iext) and os.path.isfile(path):
2368+ arg.append(path)
2369+ elif os.path.islink(path) and os.path.isdir(path):
2370+ os.path.walk(path, self._expandSourceFilesWalk, arg)
2371+ # If is directory, do nothing; 'walk' will eventually get it.
2372+
2373+
2374+ def _expandSourceFiles(self, files, recurse, addIextIfMissing):
2375+ """Calculate source paths from 'files' by applying the
2376+ command-line options.
2377+ """
2378+ C, D, W = self.chatter, self.debug, self.warn
2379+ idir = self.opts.idir
2380+ iext = self.opts.iext
2381+ files = []
2382+ for f in self.pathArgs:
2383+ oldFilesLen = len(files)
2384+ D("Expanding %s", f)
2385+ path = os.path.join(idir, f)
2386+ pathWithExt = path + iext # May or may not be valid.
2387+ if os.path.isdir(path):
2388+ if recurse:
2389+ os.path.walk(path, self._expandSourceFilesWalk, files)
2390+ else:
2391+ raise Error("source file '%s' is a directory" % path)
2392+ elif os.path.isfile(path):
2393+ files.append(path)
2394+ elif (addIextIfMissing and not path.endswith(iext) and
2395+ os.path.isfile(pathWithExt)):
2396+ files.append(pathWithExt)
2397+ # Do not recurse directories discovered by iext appending.
2398+ elif os.path.exists(path):
2399+ W("Skipping source file '%s', not a plain file.", path)
2400+ else:
2401+ W("Skipping source file '%s', not found.", path)
2402+ if len(files) > oldFilesLen:
2403+ D(" ... found %s", files[oldFilesLen:])
2404+ return files
2405+
2406+
2407+ def _getBundles(self, sourceFiles):
2408+ flat = self.opts.flat
2409+ idir = self.opts.idir
2410+ iext = self.opts.iext
2411+ nobackup = self.opts.nobackup
2412+ odir = self.opts.odir
2413+ oext = self.opts.oext
2414+ idirSlash = idir + os.sep
2415+ bundles = []
2416+ for src in sourceFiles:
2417+ # 'base' is the subdirectory plus basename.
2418+ base = src
2419+ if idir and src.startswith(idirSlash):
2420+ base = src[len(idirSlash):]
2421+ if iext and base.endswith(iext):
2422+ base = base[:-len(iext)]
2423+ basename = os.path.basename(base)
2424+ if flat:
2425+ dst = os.path.join(odir, basename + oext)
2426+ else:
2427+ dbn = basename
2428+ if odir and base.startswith(os.sep):
2429+ odd = odir
2430+ while odd != '':
2431+ idx = base.find(odd)
2432+ if idx == 0:
2433+ dbn = base[len(odd):]
2434+ if dbn[0] == '/':
2435+ dbn = dbn[1:]
2436+ break
2437+ odd = os.path.dirname(odd)
2438+ if odd == '/':
2439+ break
2440+ dst = os.path.join(odir, dbn + oext)
2441+ else:
2442+ dst = os.path.join(odir, base + oext)
2443+ bak = dst + self.BACKUP_SUFFIX
2444+ b = Bundle(src=src, dst=dst, bak=bak, base=base, basename=basename)
2445+ bundles.append(b)
2446+ return bundles
2447+
2448+
2449+ def _getTemplateClass(self):
2450+ C, D, W = self.chatter, self.debug, self.warn
2451+ modname = None
2452+ if self._templateClass:
2453+ return self._templateClass
2454+
2455+ modname = self.opts.templateClassName
2456+
2457+ if not modname:
2458+ return Template
2459+ p = modname.rfind('.')
2460+ if ':' not in modname:
2461+ self.error('The value of option --templateAPIClass is invalid\n'
2462+ 'It must be in the form "module:class", '
2463+ 'e.g. "Cheetah.Template:Template"')
2464+
2465+ modname, classname = modname.split(':')
2466+
2467+ C('using --templateAPIClass=%s:%s'%(modname, classname))
2468+
2469+ if p >= 0:
2470+ mod = getattr(__import__(modname[:p], {}, {}, [modname[p+1:]]), modname[p+1:])
2471+ else:
2472+ mod = __import__(modname, {}, {}, [])
2473+
2474+ klass = getattr(mod, classname, None)
2475+ if klass:
2476+ self._templateClass = klass
2477+ return klass
2478+ else:
2479+ self.error('**Template class specified in option --templateAPIClass not found\n'
2480+ '**Falling back on Cheetah.Template:Template')
2481+
2482+
2483+ def _getCompilerSettings(self):
2484+ if self._compilerSettings:
2485+ return self._compilerSettings
2486+
2487+ def getkws(**kws):
2488+ return kws
2489+ if self.opts.compilerSettingsString:
2490+ try:
2491+ exec('settings = getkws(%s)'%self.opts.compilerSettingsString)
2492+ except:
2493+ self.error("There's an error in your --settings option."
2494+ "It must be valid Python syntax.\n"
2495+ +" --settings='%s'\n"%self.opts.compilerSettingsString
2496+ +" %s: %s"%sys.exc_info()[:2]
2497+ )
2498+
2499+ validKeys = DEFAULT_COMPILER_SETTINGS.keys()
2500+ if [k for k in settings.keys() if k not in validKeys]:
2501+ self.error(
2502+ 'The --setting "%s" is not a valid compiler setting name.'%k)
2503+
2504+ self._compilerSettings = settings
2505+ return settings
2506+ else:
2507+ return {}
2508+
2509+ def _compileOrFillStdin(self):
2510+ TemplateClass = self._getTemplateClass()
2511+ compilerSettings = self._getCompilerSettings()
2512+ if self.isCompile:
2513+ pysrc = TemplateClass.compile(file=sys.stdin,
2514+ compilerSettings=compilerSettings,
2515+ returnAClass=False)
2516+ output = pysrc
2517+ else:
2518+ output = str(TemplateClass(file=sys.stdin, compilerSettings=compilerSettings))
2519+ sys.stdout.write(output)
2520+
2521+ def _compileOrFillBundle(self, b):
2522+ C, D, W = self.chatter, self.debug, self.warn
2523+ TemplateClass = self._getTemplateClass()
2524+ compilerSettings = self._getCompilerSettings()
2525+ src = b.src
2526+ dst = b.dst
2527+ base = b.base
2528+ basename = b.basename
2529+ dstDir = os.path.dirname(dst)
2530+ what = self.isCompile and "Compiling" or "Filling"
2531+ C("%s %s -> %s^", what, src, dst) # No trailing newline.
2532+ if os.path.exists(dst) and not self.opts.nobackup:
2533+ bak = b.bak
2534+ C(" (backup %s)", bak) # On same line as previous message.
2535+ else:
2536+ bak = None
2537+ C("")
2538+ if self.isCompile:
2539+ if not moduleNameRE.match(basename):
2540+ tup = basename, src
2541+ raise Error("""\
2542+%s: base name %s contains invalid characters. It must
2543+be named according to the same rules as Python modules.""" % tup)
2544+ pysrc = TemplateClass.compile(file=src, returnAClass=False,
2545+ moduleName=basename,
2546+ className=basename,
2547+ commandlineopts=self.opts,
2548+ compilerSettings=compilerSettings)
2549+ output = pysrc
2550+ else:
2551+ #output = str(TemplateClass(file=src, searchList=self.searchList))
2552+ tclass = TemplateClass.compile(file=src, compilerSettings=compilerSettings)
2553+ output = str(tclass(searchList=self.searchList))
2554+
2555+ if bak:
2556+ shutil.copyfile(dst, bak)
2557+ if dstDir and not os.path.exists(dstDir):
2558+ if self.isCompile:
2559+ mkdirsWithPyInitFiles(dstDir)
2560+ else:
2561+ os.makedirs(dstDir)
2562+ if self.opts.stdout:
2563+ sys.stdout.write(output)
2564+ else:
2565+ f = open(dst, 'w')
2566+ f.write(output)
2567+ f.close()
2568+
2569+
2570+# Called when invoked as `cheetah`
2571+def _cheetah():
2572+ CheetahWrapper().main()
2573+
2574+# Called when invoked as `cheetah-compile`
2575+def _cheetah_compile():
2576+ sys.argv.insert(1, "compile")
2577+ CheetahWrapper().main()
2578+
2579+
2580+##################################################
2581+## if run from the command line
2582+if __name__ == '__main__': CheetahWrapper().main()
2583+
2584+# vim: shiftwidth=4 tabstop=4 expandtab
2585
2586=== added file 'cheetah/Compiler.py'
2587--- cheetah/Compiler.py 1970-01-01 00:00:00 +0000
2588+++ cheetah/Compiler.py 2010-10-19 20:12:51 +0000
2589@@ -0,0 +1,2002 @@
2590+'''
2591+ Compiler classes for Cheetah:
2592+ ModuleCompiler aka 'Compiler'
2593+ ClassCompiler
2594+ MethodCompiler
2595+
2596+ If you are trying to grok this code start with ModuleCompiler.__init__,
2597+ ModuleCompiler.compile, and ModuleCompiler.__getattr__.
2598+'''
2599+
2600+import sys
2601+import os
2602+import os.path
2603+from os.path import getmtime, exists
2604+import re
2605+import types
2606+import time
2607+import random
2608+import warnings
2609+import copy
2610+
2611+from Cheetah.Version import Version, VersionTuple
2612+from Cheetah.SettingsManager import SettingsManager
2613+from Cheetah.Utils.Indenter import indentize # an undocumented preprocessor
2614+from Cheetah import ErrorCatchers
2615+from Cheetah import NameMapper
2616+from Cheetah.Parser import Parser, ParseError, specialVarRE, \
2617+ STATIC_CACHE, REFRESH_CACHE, SET_LOCAL, SET_GLOBAL, SET_MODULE, \
2618+ unicodeDirectiveRE, encodingDirectiveRE, escapedNewlineRE
2619+
2620+from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList
2621+VFFSL=valueFromFrameOrSearchList
2622+VFSL=valueFromSearchList
2623+VFN=valueForName
2624+currentTime=time.time
2625+
2626+class Error(Exception): pass
2627+
2628+# Settings format: (key, default, docstring)
2629+_DEFAULT_COMPILER_SETTINGS = [
2630+ ('useNameMapper', True, 'Enable NameMapper for dotted notation and searchList support'),
2631+ ('useSearchList', True, 'Enable the searchList, requires useNameMapper=True, if disabled, first portion of the $variable is a global, builtin, or local variable that doesn\'t need looking up in the searchList'),
2632+ ('allowSearchListAsMethArg', True, ''),
2633+ ('useAutocalling', True, 'Detect and call callable objects in searchList, requires useNameMapper=True'),
2634+ ('useStackFrames', True, 'Used for NameMapper.valueFromFrameOrSearchList rather than NameMapper.valueFromSearchList'),
2635+ ('useErrorCatcher', False, 'Turn on the #errorCatcher directive for catching NameMapper errors, etc'),
2636+ ('alwaysFilterNone', True, 'Filter out None prior to calling the #filter'),
2637+ ('useFilters', True, 'If False, pass output through str()'),
2638+ ('includeRawExprInFilterArgs', True, ''),
2639+ ('useLegacyImportMode', True, 'All #import statements are relocated to the top of the generated Python module'),
2640+ ('prioritizeSearchListOverSelf', False, 'When iterating the searchList, look into the searchList passed into the initializer instead of Template members first'),
2641+
2642+ ('autoAssignDummyTransactionToSelf', False, ''),
2643+ ('useKWsDictArgForPassingTrans', True, ''),
2644+
2645+ ('commentOffset', 1, ''),
2646+ ('outputRowColComments', True, ''),
2647+ ('includeBlockMarkers', False, 'Wrap #block\'s in a comment in the template\'s output'),
2648+ ('blockMarkerStart', ('\n<!-- START BLOCK: ', ' -->\n'), ''),
2649+ ('blockMarkerEnd', ('\n<!-- END BLOCK: ', ' -->\n'), ''),
2650+ ('defDocStrMsg', 'Autogenerated by Cheetah: The Python-Powered Template Engine', ''),
2651+ ('setup__str__method', False, ''),
2652+ ('mainMethodName', 'respond', ''),
2653+ ('mainMethodNameForSubclasses', 'writeBody', ''),
2654+ ('indentationStep', ' ' * 4, ''),
2655+ ('initialMethIndentLevel', 2, ''),
2656+ ('monitorSrcFile', False, ''),
2657+ ('outputMethodsBeforeAttributes', True, ''),
2658+ ('addTimestampsToCompilerOutput', True, ''),
2659+
2660+ ## Customizing the #extends directive
2661+ ('autoImportForExtendsDirective', True, ''),
2662+ ('handlerForExtendsDirective', None, ''),
2663+
2664+ ('disabledDirectives', [], 'List of directive keys to disable (without starting "#")'),
2665+ ('enabledDirectives', [], 'List of directive keys to enable (without starting "#")'),
2666+ ('disabledDirectiveHooks', [], 'callable(parser, directiveKey)'),
2667+ ('preparseDirectiveHooks', [], 'callable(parser, directiveKey)'),
2668+ ('postparseDirectiveHooks', [], 'callable(parser, directiveKey)'),
2669+ ('preparsePlaceholderHooks', [], 'callable(parser)'),
2670+ ('postparsePlaceholderHooks', [], 'callable(parser)'),
2671+ ('expressionFilterHooks', [], '''callable(parser, expr, exprType, rawExpr=None, startPos=None), exprType is the name of the directive, "psp" or "placeholder" The filters *must* return the expr or raise an expression, they can modify the expr if needed'''),
2672+ ('templateMetaclass', None, 'Strictly optional, only will work with new-style basecalsses as well'),
2673+ ('i18NFunctionName', 'self.i18n', ''),
2674+
2675+ ('cheetahVarStartToken', '$', ''),
2676+ ('commentStartToken', '##', ''),
2677+ ('multiLineCommentStartToken', '#*', ''),
2678+ ('multiLineCommentEndToken', '*#', ''),
2679+ ('gobbleWhitespaceAroundMultiLineComments', True, ''),
2680+ ('directiveStartToken', '#', ''),
2681+ ('directiveEndToken', '#', ''),
2682+ ('allowWhitespaceAfterDirectiveStartToken', False, ''),
2683+ ('PSPStartToken', '<%', ''),
2684+ ('PSPEndToken', '%>', ''),
2685+ ('EOLSlurpToken', '#', ''),
2686+ ('gettextTokens', ["_", "N_", "ngettext"], ''),
2687+ ('allowExpressionsInExtendsDirective', False, ''),
2688+ ('allowEmptySingleLineMethods', False, ''),
2689+ ('allowNestedDefScopes', True, ''),
2690+ ('allowPlaceholderFilterArgs', True, ''),
2691+]
2692+
2693+DEFAULT_COMPILER_SETTINGS = dict([(v[0], v[1]) for v in _DEFAULT_COMPILER_SETTINGS])
2694+
2695+
2696+
2697+class GenUtils(object):
2698+ """An abstract baseclass for the Compiler classes that provides methods that
2699+ perform generic utility functions or generate pieces of output code from
2700+ information passed in by the Parser baseclass. These methods don't do any
2701+ parsing themselves.
2702+ """
2703+
2704+ def genTimeInterval(self, timeString):
2705+ ##@@ TR: need to add some error handling here
2706+ if timeString[-1] == 's':
2707+ interval = float(timeString[:-1])
2708+ elif timeString[-1] == 'm':
2709+ interval = float(timeString[:-1])*60
2710+ elif timeString[-1] == 'h':
2711+ interval = float(timeString[:-1])*60*60
2712+ elif timeString[-1] == 'd':
2713+ interval = float(timeString[:-1])*60*60*24
2714+ elif timeString[-1] == 'w':
2715+ interval = float(timeString[:-1])*60*60*24*7
2716+ else: # default to minutes
2717+ interval = float(timeString)*60
2718+ return interval
2719+
2720+ def genCacheInfo(self, cacheTokenParts):
2721+ """Decipher a placeholder cachetoken
2722+ """
2723+ cacheInfo = {}
2724+ if cacheTokenParts['REFRESH_CACHE']:
2725+ cacheInfo['type'] = REFRESH_CACHE
2726+ cacheInfo['interval'] = self.genTimeInterval(cacheTokenParts['interval'])
2727+ elif cacheTokenParts['STATIC_CACHE']:
2728+ cacheInfo['type'] = STATIC_CACHE
2729+ return cacheInfo # is empty if no cache
2730+
2731+ def genCacheInfoFromArgList(self, argList):
2732+ cacheInfo = {'type':REFRESH_CACHE}
2733+ for key, val in argList:
2734+ if val[0] in '"\'':
2735+ val = val[1:-1]
2736+
2737+ if key == 'timer':
2738+ key = 'interval'
2739+ val = self.genTimeInterval(val)
2740+
2741+ cacheInfo[key] = val
2742+ return cacheInfo
2743+
2744+ def genCheetahVar(self, nameChunks, plain=False):
2745+ if nameChunks[0][0] in self.setting('gettextTokens'):
2746+ self.addGetTextVar(nameChunks)
2747+ if self.setting('useNameMapper') and not plain:
2748+ return self.genNameMapperVar(nameChunks)
2749+ else:
2750+ return self.genPlainVar(nameChunks)
2751+
2752+ def addGetTextVar(self, nameChunks):
2753+ """Output something that gettext can recognize.
2754+
2755+ This is a harmless side effect necessary to make gettext work when it
2756+ is scanning compiled templates for strings marked for translation.
2757+
2758+ @@TR: another marginally more efficient approach would be to put the
2759+ output in a dummy method that is never called.
2760+ """
2761+ # @@TR: this should be in the compiler not here
2762+ self.addChunk("if False:")
2763+ self.indent()
2764+ self.addChunk(self.genPlainVar(nameChunks[:]))
2765+ self.dedent()
2766+
2767+ def genPlainVar(self, nameChunks):
2768+ """Generate Python code for a Cheetah $var without using NameMapper
2769+ (Unified Dotted Notation with the SearchList).
2770+ """
2771+ nameChunks.reverse()
2772+ chunk = nameChunks.pop()
2773+ pythonCode = chunk[0] + chunk[2]
2774+ while nameChunks:
2775+ chunk = nameChunks.pop()
2776+ pythonCode = (pythonCode + '.' + chunk[0] + chunk[2])
2777+ return pythonCode
2778+
2779+ def genNameMapperVar(self, nameChunks):
2780+ """Generate valid Python code for a Cheetah $var, using NameMapper
2781+ (Unified Dotted Notation with the SearchList).
2782+
2783+ nameChunks = list of var subcomponents represented as tuples
2784+ [ (name,useAC,remainderOfExpr),
2785+ ]
2786+ where:
2787+ name = the dotted name base
2788+ useAC = where NameMapper should use autocalling on namemapperPart
2789+ remainderOfExpr = any arglist, index, or slice
2790+
2791+ If remainderOfExpr contains a call arglist (e.g. '(1234)') then useAC
2792+ is False, otherwise it defaults to True. It is overridden by the global
2793+ setting 'useAutocalling' if this setting is False.
2794+
2795+ EXAMPLE
2796+ ------------------------------------------------------------------------
2797+ if the raw Cheetah Var is
2798+ $a.b.c[1].d().x.y.z
2799+
2800+ nameChunks is the list
2801+ [ ('a.b.c',True,'[1]'), # A
2802+ ('d',False,'()'), # B
2803+ ('x.y.z',True,''), # C
2804+ ]
2805+
2806+ When this method is fed the list above it returns
2807+ VFN(VFN(VFFSL(SL, 'a.b.c',True)[1], 'd',False)(), 'x.y.z',True)
2808+ which can be represented as
2809+ VFN(B`, name=C[0], executeCallables=(useAC and C[1]))C[2]
2810+ where:
2811+ VFN = NameMapper.valueForName
2812+ VFFSL = NameMapper.valueFromFrameOrSearchList
2813+ VFSL = NameMapper.valueFromSearchList # optionally used instead of VFFSL
2814+ SL = self.searchList()
2815+ useAC = self.setting('useAutocalling') # True in this example
2816+
2817+ A = ('a.b.c',True,'[1]')
2818+ B = ('d',False,'()')
2819+ C = ('x.y.z',True,'')
2820+
2821+ C` = VFN( VFN( VFFSL(SL, 'a.b.c',True)[1],
2822+ 'd',False)(),
2823+ 'x.y.z',True)
2824+ = VFN(B`, name='x.y.z', executeCallables=True)
2825+
2826+ B` = VFN(A`, name=B[0], executeCallables=(useAC and B[1]))B[2]
2827+ A` = VFFSL(SL, name=A[0], executeCallables=(useAC and A[1]))A[2]
2828+
2829+
2830+ Note, if the compiler setting useStackFrames=False (default is true)
2831+ then
2832+ A` = VFSL([locals()]+SL+[globals(), __builtin__], name=A[0], executeCallables=(useAC and A[1]))A[2]
2833+ This option allows Cheetah to be used with Psyco, which doesn't support
2834+ stack frame introspection.
2835+ """
2836+ defaultUseAC = self.setting('useAutocalling')
2837+ useSearchList = self.setting('useSearchList')
2838+
2839+ nameChunks.reverse()
2840+ name, useAC, remainder = nameChunks.pop()
2841+
2842+ if not useSearchList:
2843+ firstDotIdx = name.find('.')
2844+ if firstDotIdx != -1 and firstDotIdx < len(name):
2845+ beforeFirstDot, afterDot = name[:firstDotIdx], name[firstDotIdx+1:]
2846+ pythonCode = ('VFN(' + beforeFirstDot +
2847+ ',"' + afterDot +
2848+ '",' + repr(defaultUseAC and useAC) + ')'
2849+ + remainder)
2850+ else:
2851+ pythonCode = name+remainder
2852+ elif self.setting('useStackFrames'):
2853+ pythonCode = ('VFFSL(SL,'
2854+ '"'+ name + '",'
2855+ + repr(defaultUseAC and useAC) + ')'
2856+ + remainder)
2857+ else:
2858+ pythonCode = ('VFSL([locals()]+SL+[globals(), builtin],'
2859+ '"'+ name + '",'
2860+ + repr(defaultUseAC and useAC) + ')'
2861+ + remainder)
2862+ ##
2863+ while nameChunks:
2864+ name, useAC, remainder = nameChunks.pop()
2865+ pythonCode = ('VFN(' + pythonCode +
2866+ ',"' + name +
2867+ '",' + repr(defaultUseAC and useAC) + ')'
2868+ + remainder)
2869+ return pythonCode
2870+
2871+##################################################
2872+## METHOD COMPILERS
2873+
2874+class MethodCompiler(GenUtils):
2875+ def __init__(self, methodName, classCompiler,
2876+ initialMethodComment=None,
2877+ decorators=None):
2878+ self._settingsManager = classCompiler
2879+ self._classCompiler = classCompiler
2880+ self._moduleCompiler = classCompiler._moduleCompiler
2881+ self._methodName = methodName
2882+ self._initialMethodComment = initialMethodComment
2883+ self._setupState()
2884+ self._decorators = decorators or []
2885+
2886+ def setting(self, key):
2887+ return self._settingsManager.setting(key)
2888+
2889+ def _setupState(self):
2890+ self._indent = self.setting('indentationStep')
2891+ self._indentLev = self.setting('initialMethIndentLevel')
2892+ self._pendingStrConstChunks = []
2893+ self._methodSignature = None
2894+ self._methodDef = None
2895+ self._docStringLines = []
2896+ self._methodBodyChunks = []
2897+
2898+ self._cacheRegionsStack = []
2899+ self._callRegionsStack = []
2900+ self._captureRegionsStack = []
2901+ self._filterRegionsStack = []
2902+
2903+ self._isErrorCatcherOn = False
2904+
2905+ self._hasReturnStatement = False
2906+ self._isGenerator = False
2907+
2908+
2909+ def cleanupState(self):
2910+ """Called by the containing class compiler instance
2911+ """
2912+ pass
2913+
2914+ def methodName(self):
2915+ return self._methodName
2916+
2917+ def setMethodName(self, name):
2918+ self._methodName = name
2919+
2920+ ## methods for managing indentation
2921+
2922+ def indentation(self):
2923+ return self._indent * self._indentLev
2924+
2925+ def indent(self):
2926+ self._indentLev +=1
2927+
2928+ def dedent(self):
2929+ if self._indentLev:
2930+ self._indentLev -=1
2931+ else:
2932+ raise Error('Attempt to dedent when the indentLev is 0')
2933+
2934+ ## methods for final code wrapping
2935+
2936+ def methodDef(self):
2937+ if self._methodDef:
2938+ return self._methodDef
2939+ else:
2940+ return self.wrapCode()
2941+
2942+ __str__ = methodDef
2943+ __unicode__ = methodDef
2944+
2945+ def wrapCode(self):
2946+ self.commitStrConst()
2947+ methodDefChunks = (
2948+ self.methodSignature(),
2949+ '\n',
2950+ self.docString(),
2951+ self.methodBody() )
2952+ methodDef = ''.join(methodDefChunks)
2953+ self._methodDef = methodDef
2954+ return methodDef
2955+
2956+ def methodSignature(self):
2957+ return self._indent + self._methodSignature + ':'
2958+
2959+ def setMethodSignature(self, signature):
2960+ self._methodSignature = signature
2961+
2962+ def methodBody(self):
2963+ return ''.join( self._methodBodyChunks )
2964+
2965+ def docString(self):
2966+ if not self._docStringLines:
2967+ return ''
2968+
2969+ ind = self._indent*2
2970+ docStr = (ind + '"""\n' + ind +
2971+ ('\n' + ind).join([ln.replace('"""', "'''") for ln in self._docStringLines]) +
2972+ '\n' + ind + '"""\n')
2973+ return docStr
2974+
2975+ ## methods for adding code
2976+ def addMethDocString(self, line):
2977+ self._docStringLines.append(line.replace('%', '%%'))
2978+
2979+ def addChunk(self, chunk):
2980+ self.commitStrConst()
2981+ chunk = "\n" + self.indentation() + chunk
2982+ self._methodBodyChunks.append(chunk)
2983+
2984+ def appendToPrevChunk(self, appendage):
2985+ self._methodBodyChunks[-1] = self._methodBodyChunks[-1] + appendage
2986+
2987+ def addWriteChunk(self, chunk):
2988+ self.addChunk('write(' + chunk + ')')
2989+
2990+ def addFilteredChunk(self, chunk, filterArgs=None, rawExpr=None, lineCol=None):
2991+ if filterArgs is None:
2992+ filterArgs = ''
2993+ if self.setting('includeRawExprInFilterArgs') and rawExpr:
2994+ filterArgs += ', rawExpr=%s'%repr(rawExpr)
2995+
2996+ if self.setting('alwaysFilterNone'):
2997+ if rawExpr and rawExpr.find('\n')==-1 and rawExpr.find('\r')==-1:
2998+ self.addChunk("_v = %s # %r"%(chunk, rawExpr))
2999+ if lineCol:
3000+ self.appendToPrevChunk(' on line %s, col %s'%lineCol)
3001+ else:
3002+ self.addChunk("_v = %s"%chunk)
3003+
3004+ if self.setting('useFilters'):
3005+ self.addChunk("if _v is not None: write(_filter(_v%s))"%filterArgs)
3006+ else:
3007+ self.addChunk("if _v is not None: write(str(_v))")
3008+ else:
3009+ if self.setting('useFilters'):
3010+ self.addChunk("write(_filter(%s%s))"%(chunk, filterArgs))
3011+ else:
3012+ self.addChunk("write(str(%s))"%chunk)
3013+
3014+ def _appendToPrevStrConst(self, strConst):
3015+ if self._pendingStrConstChunks:
3016+ self._pendingStrConstChunks.append(strConst)
3017+ else:
3018+ self._pendingStrConstChunks = [strConst]
3019+
3020+ def commitStrConst(self):
3021+ """Add the code for outputting the pending strConst without chopping off
3022+ any whitespace from it.
3023+ """
3024+ if not self._pendingStrConstChunks:
3025+ return
3026+
3027+ strConst = ''.join(self._pendingStrConstChunks)
3028+ self._pendingStrConstChunks = []
3029+ if not strConst:
3030+ return
3031+
3032+ reprstr = repr(strConst)
3033+ i = 0
3034+ out = []
3035+ if reprstr.startswith('u'):
3036+ i = 1
3037+ out = ['u']
3038+ body = escapedNewlineRE.sub('\\1\n', reprstr[i+1:-1])
3039+
3040+ if reprstr[i]=="'":
3041+ out.append("'''")
3042+ out.append(body)
3043+ out.append("'''")
3044+ else:
3045+ out.append('"""')
3046+ out.append(body)
3047+ out.append('"""')
3048+ self.addWriteChunk(''.join(out))
3049+
3050+ def handleWSBeforeDirective(self):
3051+ """Truncate the pending strCont to the beginning of the current line.
3052+ """
3053+ if self._pendingStrConstChunks:
3054+ src = self._pendingStrConstChunks[-1]
3055+ BOL = max(src.rfind('\n')+1, src.rfind('\r')+1, 0)
3056+ if BOL < len(src):
3057+ self._pendingStrConstChunks[-1] = src[:BOL]
3058+
3059+
3060+
3061+ def isErrorCatcherOn(self):
3062+ return self._isErrorCatcherOn
3063+
3064+ def turnErrorCatcherOn(self):
3065+ self._isErrorCatcherOn = True
3066+
3067+ def turnErrorCatcherOff(self):
3068+ self._isErrorCatcherOn = False
3069+
3070+ # @@TR: consider merging the next two methods into one
3071+ def addStrConst(self, strConst):
3072+ self._appendToPrevStrConst(strConst)
3073+
3074+ def addRawText(self, text):
3075+ self.addStrConst(text)
3076+
3077+ def addMethComment(self, comm):
3078+ offSet = self.setting('commentOffset')
3079+ self.addChunk('#' + ' '*offSet + comm)
3080+
3081+ def addPlaceholder(self, expr, filterArgs, rawPlaceholder,
3082+ cacheTokenParts, lineCol,
3083+ silentMode=False):
3084+ cacheInfo = self.genCacheInfo(cacheTokenParts)
3085+ if cacheInfo:
3086+ cacheInfo['ID'] = repr(rawPlaceholder)[1:-1]
3087+ self.startCacheRegion(cacheInfo, lineCol, rawPlaceholder=rawPlaceholder)
3088+
3089+ if self.isErrorCatcherOn():
3090+ methodName = self._classCompiler.addErrorCatcherCall(
3091+ expr, rawCode=rawPlaceholder, lineCol=lineCol)
3092+ expr = 'self.' + methodName + '(localsDict=locals())'
3093+
3094+ if silentMode:
3095+ self.addChunk('try:')
3096+ self.indent()
3097+ self.addFilteredChunk(expr, filterArgs, rawPlaceholder, lineCol=lineCol)
3098+ self.dedent()
3099+ self.addChunk('except NotFound: pass')
3100+ else:
3101+ self.addFilteredChunk(expr, filterArgs, rawPlaceholder, lineCol=lineCol)
3102+
3103+ if self.setting('outputRowColComments'):
3104+ self.appendToPrevChunk(' # from line %s, col %s' % lineCol + '.')
3105+ if cacheInfo:
3106+ self.endCacheRegion()
3107+
3108+ def addSilent(self, expr):
3109+ self.addChunk( expr )
3110+
3111+ def addEcho(self, expr, rawExpr=None):
3112+ self.addFilteredChunk(expr, rawExpr=rawExpr)
3113+
3114+ def addSet(self, expr, exprComponents, setStyle):
3115+ if setStyle is SET_GLOBAL:
3116+ (LVALUE, OP, RVALUE) = (exprComponents.LVALUE,
3117+ exprComponents.OP,
3118+ exprComponents.RVALUE)
3119+ # we need to split the LVALUE to deal with globalSetVars
3120+ splitPos1 = LVALUE.find('.')
3121+ splitPos2 = LVALUE.find('[')
3122+ if splitPos1 > 0 and splitPos2==-1:
3123+ splitPos = splitPos1
3124+ elif splitPos1 > 0 and splitPos1 < max(splitPos2, 0):
3125+ splitPos = splitPos1
3126+ else:
3127+ splitPos = splitPos2
3128+
3129+ if splitPos >0:
3130+ primary = LVALUE[:splitPos]
3131+ secondary = LVALUE[splitPos:]
3132+ else:
3133+ primary = LVALUE
3134+ secondary = ''
3135+ LVALUE = 'self._CHEETAH__globalSetVars["' + primary + '"]' + secondary
3136+ expr = LVALUE + ' ' + OP + ' ' + RVALUE.strip()
3137+
3138+ if setStyle is SET_MODULE:
3139+ self._moduleCompiler.addModuleGlobal(expr)
3140+ else:
3141+ self.addChunk(expr)
3142+
3143+ def addInclude(self, sourceExpr, includeFrom, isRaw):
3144+ self.addChunk('self._handleCheetahInclude(' + sourceExpr +
3145+ ', trans=trans, ' +
3146+ 'includeFrom="' + includeFrom + '", raw=' +
3147+ repr(isRaw) + ')')
3148+
3149+ def addWhile(self, expr, lineCol=None):
3150+ self.addIndentingDirective(expr, lineCol=lineCol)
3151+
3152+ def addFor(self, expr, lineCol=None):
3153+ self.addIndentingDirective(expr, lineCol=lineCol)
3154+
3155+ def addRepeat(self, expr, lineCol=None):
3156+ #the _repeatCount stuff here allows nesting of #repeat directives
3157+ self._repeatCount = getattr(self, "_repeatCount", -1) + 1
3158+ self.addFor('for __i%s in range(%s)' % (self._repeatCount, expr), lineCol=lineCol)
3159+
3160+ def addIndentingDirective(self, expr, lineCol=None):
3161+ if expr and not expr[-1] == ':':
3162+ expr = expr + ':'
3163+ self.addChunk( expr )
3164+ if lineCol:
3165+ self.appendToPrevChunk(' # generated from line %s, col %s'%lineCol )
3166+ self.indent()
3167+
3168+ def addReIndentingDirective(self, expr, dedent=True, lineCol=None):
3169+ self.commitStrConst()
3170+ if dedent:
3171+ self.dedent()
3172+ if not expr[-1] == ':':
3173+ expr = expr + ':'
3174+
3175+ self.addChunk( expr )
3176+ if lineCol:
3177+ self.appendToPrevChunk(' # generated from line %s, col %s'%lineCol )
3178+ self.indent()
3179+
3180+ def addIf(self, expr, lineCol=None):
3181+ """For a full #if ... #end if directive
3182+ """
3183+ self.addIndentingDirective(expr, lineCol=lineCol)
3184+
3185+ def addOneLineIf(self, expr, lineCol=None):
3186+ """For a full #if ... #end if directive
3187+ """
3188+ self.addIndentingDirective(expr, lineCol=lineCol)
3189+
3190+ def addTernaryExpr(self, conditionExpr, trueExpr, falseExpr, lineCol=None):
3191+ """For a single-lie #if ... then .... else ... directive
3192+ <condition> then <trueExpr> else <falseExpr>
3193+ """
3194+ self.addIndentingDirective(conditionExpr, lineCol=lineCol)
3195+ self.addFilteredChunk(trueExpr)
3196+ self.dedent()
3197+ self.addIndentingDirective('else')
3198+ self.addFilteredChunk(falseExpr)
3199+ self.dedent()
3200+
3201+ def addElse(self, expr, dedent=True, lineCol=None):
3202+ expr = re.sub(r'else[ \f\t]+if', 'elif', expr)
3203+ self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol)
3204+
3205+ def addElif(self, expr, dedent=True, lineCol=None):
3206+ self.addElse(expr, dedent=dedent, lineCol=lineCol)
3207+
3208+ def addUnless(self, expr, lineCol=None):
3209+ self.addIf('if not (' + expr + ')')
3210+
3211+ def addClosure(self, functionName, argsList, parserComment):
3212+ argStringChunks = []
3213+ for arg in argsList:
3214+ chunk = arg[0]
3215+ if not arg[1] == None:
3216+ chunk += '=' + arg[1]
3217+ argStringChunks.append(chunk)
3218+ signature = "def " + functionName + "(" + ','.join(argStringChunks) + "):"
3219+ self.addIndentingDirective(signature)
3220+ self.addChunk('#'+parserComment)
3221+
3222+ def addTry(self, expr, lineCol=None):
3223+ self.addIndentingDirective(expr, lineCol=lineCol)
3224+
3225+ def addExcept(self, expr, dedent=True, lineCol=None):
3226+ self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol)
3227+
3228+ def addFinally(self, expr, dedent=True, lineCol=None):
3229+ self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol)
3230+
3231+ def addReturn(self, expr):
3232+ assert not self._isGenerator
3233+ self.addChunk(expr)
3234+ self._hasReturnStatement = True
3235+
3236+ def addYield(self, expr):
3237+ assert not self._hasReturnStatement
3238+ self._isGenerator = True
3239+ if expr.replace('yield', '').strip():
3240+ self.addChunk(expr)
3241+ else:
3242+ self.addChunk('if _dummyTrans:')
3243+ self.indent()
3244+ self.addChunk('yield trans.response().getvalue()')
3245+ self.addChunk('trans = DummyTransaction()')
3246+ self.addChunk('write = trans.response().write')
3247+ self.dedent()
3248+ self.addChunk('else:')
3249+ self.indent()
3250+ self.addChunk(
3251+ 'raise TypeError("This method cannot be called with a trans arg")')
3252+ self.dedent()
3253+
3254+
3255+ def addPass(self, expr):
3256+ self.addChunk(expr)
3257+
3258+ def addDel(self, expr):
3259+ self.addChunk(expr)
3260+
3261+ def addAssert(self, expr):
3262+ self.addChunk(expr)
3263+
3264+ def addRaise(self, expr):
3265+ self.addChunk(expr)
3266+
3267+ def addBreak(self, expr):
3268+ self.addChunk(expr)
3269+
3270+ def addContinue(self, expr):
3271+ self.addChunk(expr)
3272+
3273+ def addPSP(self, PSP):
3274+ self.commitStrConst()
3275+ autoIndent = False
3276+ if PSP[0] == '=':
3277+ PSP = PSP[1:]
3278+ if PSP:
3279+ self.addWriteChunk('_filter(' + PSP + ')')
3280+ return
3281+
3282+ elif PSP.lower() == 'end':
3283+ self.dedent()
3284+ return
3285+ elif PSP[-1] == '$':
3286+ autoIndent = True
3287+ PSP = PSP[:-1]
3288+ elif PSP[-1] == ':':
3289+ autoIndent = True
3290+
3291+ for line in PSP.splitlines():
3292+ self.addChunk(line)
3293+
3294+ if autoIndent:
3295+ self.indent()
3296+
3297+ def nextCacheID(self):
3298+ return ('_'+str(random.randrange(100, 999))
3299+ + str(random.randrange(10000, 99999)))
3300+
3301+ def startCacheRegion(self, cacheInfo, lineCol, rawPlaceholder=None):
3302+
3303+ # @@TR: we should add some runtime logging to this
3304+
3305+ ID = self.nextCacheID()
3306+ interval = cacheInfo.get('interval', None)
3307+ test = cacheInfo.get('test', None)
3308+ customID = cacheInfo.get('id', None)
3309+ if customID:
3310+ ID = customID
3311+ varyBy = cacheInfo.get('varyBy', repr(ID))
3312+ self._cacheRegionsStack.append(ID) # attrib of current methodCompiler
3313+
3314+ # @@TR: add this to a special class var as well
3315+ self.addChunk('')
3316+
3317+ self.addChunk('## START CACHE REGION: ID='+ID+
3318+ '. line %s, col %s'%lineCol + ' in the source.')
3319+
3320+ self.addChunk('_RECACHE_%(ID)s = False'%locals())
3321+ self.addChunk('_cacheRegion_%(ID)s = self.getCacheRegion(regionID='%locals()
3322+ + repr(ID)
3323+ + ', cacheInfo=%r'%cacheInfo
3324+ + ')')
3325+ self.addChunk('if _cacheRegion_%(ID)s.isNew():'%locals())
3326+ self.indent()
3327+ self.addChunk('_RECACHE_%(ID)s = True'%locals())
3328+ self.dedent()
3329+
3330+ self.addChunk('_cacheItem_%(ID)s = _cacheRegion_%(ID)s.getCacheItem('%locals()
3331+ +varyBy+')')
3332+
3333+ self.addChunk('if _cacheItem_%(ID)s.hasExpired():'%locals())
3334+ self.indent()
3335+ self.addChunk('_RECACHE_%(ID)s = True'%locals())
3336+ self.dedent()
3337+
3338+ if test:
3339+ self.addChunk('if ' + test + ':')
3340+ self.indent()
3341+ self.addChunk('_RECACHE_%(ID)s = True'%locals())
3342+ self.dedent()
3343+
3344+ self.addChunk('if (not _RECACHE_%(ID)s) and _cacheItem_%(ID)s.getRefreshTime():'%locals())
3345+ self.indent()
3346+ #self.addChunk('print "DEBUG"+"-"*50')
3347+ self.addChunk('try:')
3348+ self.indent()
3349+ self.addChunk('_output = _cacheItem_%(ID)s.renderOutput()'%locals())
3350+ self.dedent()
3351+ self.addChunk('except KeyError:')
3352+ self.indent()
3353+ self.addChunk('_RECACHE_%(ID)s = True'%locals())
3354+ #self.addChunk('print "DEBUG"+"*"*50')
3355+ self.dedent()
3356+ self.addChunk('else:')
3357+ self.indent()
3358+ self.addWriteChunk('_output')
3359+ self.addChunk('del _output')
3360+ self.dedent()
3361+
3362+ self.dedent()
3363+
3364+ self.addChunk('if _RECACHE_%(ID)s or not _cacheItem_%(ID)s.getRefreshTime():'%locals())
3365+ self.indent()
3366+ self.addChunk('_orig_trans%(ID)s = trans'%locals())
3367+ self.addChunk('trans = _cacheCollector_%(ID)s = DummyTransaction()'%locals())
3368+ self.addChunk('write = _cacheCollector_%(ID)s.response().write'%locals())
3369+ if interval:
3370+ self.addChunk(("_cacheItem_%(ID)s.setExpiryTime(currentTime() +"%locals())
3371+ + str(interval) + ")")
3372+
3373+ def endCacheRegion(self):
3374+ ID = self._cacheRegionsStack.pop()
3375+ self.addChunk('trans = _orig_trans%(ID)s'%locals())
3376+ self.addChunk('write = trans.response().write')
3377+ self.addChunk('_cacheData = _cacheCollector_%(ID)s.response().getvalue()'%locals())
3378+ self.addChunk('_cacheItem_%(ID)s.setData(_cacheData)'%locals())
3379+ self.addWriteChunk('_cacheData')
3380+ self.addChunk('del _cacheData')
3381+ self.addChunk('del _cacheCollector_%(ID)s'%locals())
3382+ self.addChunk('del _orig_trans%(ID)s'%locals())
3383+ self.dedent()
3384+ self.addChunk('## END CACHE REGION: '+ID)
3385+ self.addChunk('')
3386+
3387+ def nextCallRegionID(self):
3388+ return self.nextCacheID()
3389+
3390+ def startCallRegion(self, functionName, args, lineCol, regionTitle='CALL'):
3391+ class CallDetails(object):
3392+ pass
3393+ callDetails = CallDetails()
3394+ callDetails.ID = ID = self.nextCallRegionID()
3395+ callDetails.functionName = functionName
3396+ callDetails.args = args
3397+ callDetails.lineCol = lineCol
3398+ callDetails.usesKeywordArgs = False
3399+ self._callRegionsStack.append((ID, callDetails)) # attrib of current methodCompiler
3400+
3401+ self.addChunk('## START %(regionTitle)s REGION: '%locals()
3402+ +ID
3403+ +' of '+functionName
3404+ +' at line %s, col %s'%lineCol + ' in the source.')
3405+ self.addChunk('_orig_trans%(ID)s = trans'%locals())
3406+ self.addChunk('_wasBuffering%(ID)s = self._CHEETAH__isBuffering'%locals())
3407+ self.addChunk('self._CHEETAH__isBuffering = True')
3408+ self.addChunk('trans = _callCollector%(ID)s = DummyTransaction()'%locals())
3409+ self.addChunk('write = _callCollector%(ID)s.response().write'%locals())
3410+
3411+ def setCallArg(self, argName, lineCol):
3412+ ID, callDetails = self._callRegionsStack[-1]
3413+ argName = str(argName)
3414+ if callDetails.usesKeywordArgs:
3415+ self._endCallArg()
3416+ else:
3417+ callDetails.usesKeywordArgs = True
3418+ self.addChunk('_callKws%(ID)s = {}'%locals())
3419+ self.addChunk('_currentCallArgname%(ID)s = %(argName)r'%locals())
3420+ callDetails.currentArgname = argName
3421+
3422+ def _endCallArg(self):
3423+ ID, callDetails = self._callRegionsStack[-1]
3424+ currCallArg = callDetails.currentArgname
3425+ self.addChunk(('_callKws%(ID)s[%(currCallArg)r] ='
3426+ ' _callCollector%(ID)s.response().getvalue()')%locals())
3427+ self.addChunk('del _callCollector%(ID)s'%locals())
3428+ self.addChunk('trans = _callCollector%(ID)s = DummyTransaction()'%locals())
3429+ self.addChunk('write = _callCollector%(ID)s.response().write'%locals())
3430+
3431+ def endCallRegion(self, regionTitle='CALL'):
3432+ ID, callDetails = self._callRegionsStack[-1]
3433+ functionName, initialKwArgs, lineCol = (
3434+ callDetails.functionName, callDetails.args, callDetails.lineCol)
3435+
3436+ def reset(ID=ID):
3437+ self.addChunk('trans = _orig_trans%(ID)s'%locals())
3438+ self.addChunk('write = trans.response().write')
3439+ self.addChunk('self._CHEETAH__isBuffering = _wasBuffering%(ID)s '%locals())
3440+ self.addChunk('del _wasBuffering%(ID)s'%locals())
3441+ self.addChunk('del _orig_trans%(ID)s'%locals())
3442+
3443+ if not callDetails.usesKeywordArgs:
3444+ reset()
3445+ self.addChunk('_callArgVal%(ID)s = _callCollector%(ID)s.response().getvalue()'%locals())
3446+ self.addChunk('del _callCollector%(ID)s'%locals())
3447+ if initialKwArgs:
3448+ initialKwArgs = ', '+initialKwArgs
3449+ self.addFilteredChunk('%(functionName)s(_callArgVal%(ID)s%(initialKwArgs)s)'%locals())
3450+ self.addChunk('del _callArgVal%(ID)s'%locals())
3451+ else:
3452+ if initialKwArgs:
3453+ initialKwArgs = initialKwArgs+', '
3454+ self._endCallArg()
3455+ reset()
3456+ self.addFilteredChunk('%(functionName)s(%(initialKwArgs)s**_callKws%(ID)s)'%locals())
3457+ self.addChunk('del _callKws%(ID)s'%locals())
3458+ self.addChunk('## END %(regionTitle)s REGION: '%locals()
3459+ +ID
3460+ +' of '+functionName
3461+ +' at line %s, col %s'%lineCol + ' in the source.')
3462+ self.addChunk('')
3463+ self._callRegionsStack.pop() # attrib of current methodCompiler
3464+
3465+ def nextCaptureRegionID(self):
3466+ return self.nextCacheID()
3467+
3468+ def startCaptureRegion(self, assignTo, lineCol):
3469+ class CaptureDetails: pass
3470+ captureDetails = CaptureDetails()
3471+ captureDetails.ID = ID = self.nextCaptureRegionID()
3472+ captureDetails.assignTo = assignTo
3473+ captureDetails.lineCol = lineCol
3474+
3475+ self._captureRegionsStack.append((ID, captureDetails)) # attrib of current methodCompiler
3476+ self.addChunk('## START CAPTURE REGION: '+ID
3477+ +' '+assignTo
3478+ +' at line %s, col %s'%lineCol + ' in the source.')
3479+ self.addChunk('_orig_trans%(ID)s = trans'%locals())
3480+ self.addChunk('_wasBuffering%(ID)s = self._CHEETAH__isBuffering'%locals())
3481+ self.addChunk('self._CHEETAH__isBuffering = True')
3482+ self.addChunk('trans = _captureCollector%(ID)s = DummyTransaction()'%locals())
3483+ self.addChunk('write = _captureCollector%(ID)s.response().write'%locals())
3484+
3485+ def endCaptureRegion(self):
3486+ ID, captureDetails = self._captureRegionsStack.pop()
3487+ assignTo, lineCol = (captureDetails.assignTo, captureDetails.lineCol)
3488+ self.addChunk('trans = _orig_trans%(ID)s'%locals())
3489+ self.addChunk('write = trans.response().write')
3490+ self.addChunk('self._CHEETAH__isBuffering = _wasBuffering%(ID)s '%locals())
3491+ self.addChunk('%(assignTo)s = _captureCollector%(ID)s.response().getvalue()'%locals())
3492+ self.addChunk('del _orig_trans%(ID)s'%locals())
3493+ self.addChunk('del _captureCollector%(ID)s'%locals())
3494+ self.addChunk('del _wasBuffering%(ID)s'%locals())
3495+
3496+ def setErrorCatcher(self, errorCatcherName):
3497+ self.turnErrorCatcherOn()
3498+
3499+ self.addChunk('if self._CHEETAH__errorCatchers.has_key("' + errorCatcherName + '"):')
3500+ self.indent()
3501+ self.addChunk('self._CHEETAH__errorCatcher = self._CHEETAH__errorCatchers["' +
3502+ errorCatcherName + '"]')
3503+ self.dedent()
3504+ self.addChunk('else:')
3505+ self.indent()
3506+ self.addChunk('self._CHEETAH__errorCatcher = self._CHEETAH__errorCatchers["'
3507+ + errorCatcherName + '"] = ErrorCatchers.'
3508+ + errorCatcherName + '(self)'
3509+ )
3510+ self.dedent()
3511+
3512+ def nextFilterRegionID(self):
3513+ return self.nextCacheID()
3514+
3515+ def setTransform(self, transformer, isKlass):
3516+ self.addChunk('trans = TransformerTransaction()')
3517+ self.addChunk('trans._response = trans.response()')
3518+ self.addChunk('trans._response._filter = %s' % transformer)
3519+ self.addChunk('write = trans._response.write')
3520+
3521+ def setFilter(self, theFilter, isKlass):
3522+ class FilterDetails:
3523+ pass
3524+ filterDetails = FilterDetails()
3525+ filterDetails.ID = ID = self.nextFilterRegionID()
3526+ filterDetails.theFilter = theFilter
3527+ filterDetails.isKlass = isKlass
3528+ self._filterRegionsStack.append((ID, filterDetails)) # attrib of current methodCompiler
3529+
3530+ self.addChunk('_orig_filter%(ID)s = _filter'%locals())
3531+ if isKlass:
3532+ self.addChunk('_filter = self._CHEETAH__currentFilter = ' + theFilter.strip() +
3533+ '(self).filter')
3534+ else:
3535+ if theFilter.lower() == 'none':
3536+ self.addChunk('_filter = self._CHEETAH__initialFilter')
3537+ else:
3538+ # is string representing the name of a builtin filter
3539+ self.addChunk('filterName = ' + repr(theFilter))
3540+ self.addChunk('if self._CHEETAH__filters.has_key("' + theFilter + '"):')
3541+ self.indent()
3542+ self.addChunk('_filter = self._CHEETAH__currentFilter = self._CHEETAH__filters[filterName]')
3543+ self.dedent()
3544+ self.addChunk('else:')
3545+ self.indent()
3546+ self.addChunk('_filter = self._CHEETAH__currentFilter'
3547+ +' = \\\n\t\t\tself._CHEETAH__filters[filterName] = '
3548+ + 'getattr(self._CHEETAH__filtersLib, filterName)(self).filter')
3549+ self.dedent()
3550+
3551+ def closeFilterBlock(self):
3552+ ID, filterDetails = self._filterRegionsStack.pop()
3553+ #self.addChunk('_filter = self._CHEETAH__initialFilter')
3554+ #self.addChunk('_filter = _orig_filter%(ID)s'%locals())
3555+ self.addChunk('_filter = self._CHEETAH__currentFilter = _orig_filter%(ID)s'%locals())
3556+
3557+class AutoMethodCompiler(MethodCompiler):
3558+
3559+ def _setupState(self):
3560+ MethodCompiler._setupState(self)
3561+ self._argStringList = [ ("self", None) ]
3562+ self._streamingEnabled = True
3563+ self._isClassMethod = None
3564+ self._isStaticMethod = None
3565+
3566+ def _useKWsDictArgForPassingTrans(self):
3567+ alreadyHasTransArg = [argname for argname, defval in self._argStringList
3568+ if argname=='trans']
3569+ return (self.methodName()!='respond'
3570+ and not alreadyHasTransArg
3571+ and self.setting('useKWsDictArgForPassingTrans'))
3572+
3573+ def isClassMethod(self):
3574+ if self._isClassMethod is None:
3575+ self._isClassMethod = '@classmethod' in self._decorators
3576+ return self._isClassMethod
3577+
3578+ def isStaticMethod(self):
3579+ if self._isStaticMethod is None:
3580+ self._isStaticMethod = '@staticmethod' in self._decorators
3581+ return self._isStaticMethod
3582+
3583+ def cleanupState(self):
3584+ MethodCompiler.cleanupState(self)
3585+ self.commitStrConst()
3586+ if self._cacheRegionsStack:
3587+ self.endCacheRegion()
3588+ if self._callRegionsStack:
3589+ self.endCallRegion()
3590+
3591+ if self._streamingEnabled:
3592+ kwargsName = None
3593+ positionalArgsListName = None
3594+ for argname, defval in self._argStringList:
3595+ if argname.strip().startswith('**'):
3596+ kwargsName = argname.strip().replace('**', '')
3597+ break
3598+ elif argname.strip().startswith('*'):
3599+ positionalArgsListName = argname.strip().replace('*', '')
3600+
3601+ if not kwargsName and self._useKWsDictArgForPassingTrans():
3602+ kwargsName = 'KWS'
3603+ self.addMethArg('**KWS', None)
3604+ self._kwargsName = kwargsName
3605+
3606+ if not self._useKWsDictArgForPassingTrans():
3607+ if not kwargsName and not positionalArgsListName:
3608+ self.addMethArg('trans', 'None')
3609+ else:
3610+ self._streamingEnabled = False
3611+
3612+ self._indentLev = self.setting('initialMethIndentLevel')
3613+ mainBodyChunks = self._methodBodyChunks
3614+ self._methodBodyChunks = []
3615+ self._addAutoSetupCode()
3616+ self._methodBodyChunks.extend(mainBodyChunks)
3617+ self._addAutoCleanupCode()
3618+
3619+ def _addAutoSetupCode(self):
3620+ if self._initialMethodComment:
3621+ self.addChunk(self._initialMethodComment)
3622+
3623+ if self._streamingEnabled and not self.isClassMethod() and not self.isStaticMethod():
3624+ if self._useKWsDictArgForPassingTrans() and self._kwargsName:
3625+ self.addChunk('trans = %s.get("trans")'%self._kwargsName)
3626+ self.addChunk('if (not trans and not self._CHEETAH__isBuffering'
3627+ ' and not callable(self.transaction)):')
3628+ self.indent()
3629+ self.addChunk('trans = self.transaction'
3630+ ' # is None unless self.awake() was called')
3631+ self.dedent()
3632+ self.addChunk('if not trans:')
3633+ self.indent()
3634+ self.addChunk('trans = DummyTransaction()')
3635+ if self.setting('autoAssignDummyTransactionToSelf'):
3636+ self.addChunk('self.transaction = trans')
3637+ self.addChunk('_dummyTrans = True')
3638+ self.dedent()
3639+ self.addChunk('else: _dummyTrans = False')
3640+ else:
3641+ self.addChunk('trans = DummyTransaction()')
3642+ self.addChunk('_dummyTrans = True')
3643+ self.addChunk('write = trans.response().write')
3644+ if self.setting('useNameMapper'):
3645+ argNames = [arg[0] for arg in self._argStringList]
3646+ allowSearchListAsMethArg = self.setting('allowSearchListAsMethArg')
3647+ if allowSearchListAsMethArg and 'SL' in argNames:
3648+ pass
3649+ elif allowSearchListAsMethArg and 'searchList' in argNames:
3650+ self.addChunk('SL = searchList')
3651+ elif not self.isClassMethod() and not self.isStaticMethod():
3652+ self.addChunk('SL = self._CHEETAH__searchList')
3653+ else:
3654+ self.addChunk('SL = [KWS]')
3655+ if self.setting('useFilters'):
3656+ if self.isClassMethod() or self.isStaticMethod():
3657+ self.addChunk('_filter = lambda x, **kwargs: unicode(x)')
3658+ else:
3659+ self.addChunk('_filter = self._CHEETAH__currentFilter')
3660+ self.addChunk('')
3661+ self.addChunk("#" *40)
3662+ self.addChunk('## START - generated method body')
3663+ self.addChunk('')
3664+
3665+ def _addAutoCleanupCode(self):
3666+ self.addChunk('')
3667+ self.addChunk("#" *40)
3668+ self.addChunk('## END - generated method body')
3669+ self.addChunk('')
3670+
3671+ if not self._isGenerator:
3672+ self.addStop()
3673+ self.addChunk('')
3674+
3675+ def addStop(self, expr=None):
3676+ self.addChunk('return _dummyTrans and trans.response().getvalue() or ""')
3677+
3678+ def addMethArg(self, name, defVal=None):
3679+ self._argStringList.append( (name, defVal) )
3680+
3681+ def methodSignature(self):
3682+ argStringChunks = []
3683+ for arg in self._argStringList:
3684+ chunk = arg[0]
3685+ if chunk == 'self' and self.isClassMethod():
3686+ chunk = 'cls'
3687+ if chunk == 'self' and self.isStaticMethod():
3688+ # Skip the "self" method for @staticmethod decorators
3689+ continue
3690+ if not arg[1] == None:
3691+ chunk += '=' + arg[1]
3692+ argStringChunks.append(chunk)
3693+ argString = (', ').join(argStringChunks)
3694+
3695+ output = []
3696+ if self._decorators:
3697+ output.append(''.join([self._indent + decorator + '\n'
3698+ for decorator in self._decorators]))
3699+ output.append(self._indent + "def "
3700+ + self.methodName() + "(" +
3701+ argString + "):\n\n")
3702+ return ''.join(output)
3703+
3704+
3705+##################################################
3706+## CLASS COMPILERS
3707+
3708+_initMethod_initCheetah = """\
3709+if not self._CHEETAH__instanceInitialized:
3710+ cheetahKWArgs = {}
3711+ allowedKWs = 'searchList namespaces filter filtersLib errorCatcher'.split()
3712+ for k,v in KWs.items():
3713+ if k in allowedKWs: cheetahKWArgs[k] = v
3714+ self._initCheetahInstance(**cheetahKWArgs)
3715+""".replace('\n', '\n'+' '*8)
3716+
3717+class ClassCompiler(GenUtils):
3718+ methodCompilerClass = AutoMethodCompiler
3719+ methodCompilerClassForInit = MethodCompiler
3720+
3721+ def __init__(self, className, mainMethodName='respond',
3722+ moduleCompiler=None,
3723+ fileName=None,
3724+ settingsManager=None):
3725+
3726+ self._settingsManager = settingsManager
3727+ self._fileName = fileName
3728+ self._className = className
3729+ self._moduleCompiler = moduleCompiler
3730+ self._mainMethodName = mainMethodName
3731+ self._setupState()
3732+ methodCompiler = self._spawnMethodCompiler(
3733+ mainMethodName,
3734+ initialMethodComment='## CHEETAH: main method generated for this template')
3735+
3736+ self._setActiveMethodCompiler(methodCompiler)
3737+ if fileName and self.setting('monitorSrcFile'):
3738+ self._addSourceFileMonitoring(fileName)
3739+
3740+ def setting(self, key):
3741+ return self._settingsManager.setting(key)
3742+
3743+ def __getattr__(self, name):
3744+ """Provide access to the methods and attributes of the MethodCompiler
3745+ at the top of the activeMethods stack: one-way namespace sharing
3746+
3747+
3748+ WARNING: Use .setMethods to assign the attributes of the MethodCompiler
3749+ from the methods of this class!!! or you will be assigning to attributes
3750+ of this object instead."""
3751+
3752+ if name in self.__dict__:
3753+ return self.__dict__[name]
3754+ elif hasattr(self.__class__, name):
3755+ return getattr(self.__class__, name)
3756+ elif self._activeMethodsList and hasattr(self._activeMethodsList[-1], name):
3757+ return getattr(self._activeMethodsList[-1], name)
3758+ else:
3759+ raise AttributeError(name)
3760+
3761+ def _setupState(self):
3762+ self._classDef = None
3763+ self._decoratorsForNextMethod = []
3764+ self._activeMethodsList = [] # stack while parsing/generating
3765+ self._finishedMethodsList = [] # store by order
3766+ self._methodsIndex = {} # store by name
3767+ self._baseClass = 'Template'
3768+ self._classDocStringLines = []
3769+ # printed after methods in the gen class def:
3770+ self._generatedAttribs = ['_CHEETAH__instanceInitialized = False']
3771+ self._generatedAttribs.append('_CHEETAH_version = __CHEETAH_version__')
3772+ self._generatedAttribs.append(
3773+ '_CHEETAH_versionTuple = __CHEETAH_versionTuple__')
3774+
3775+ if self.setting('addTimestampsToCompilerOutput'):
3776+ self._generatedAttribs.append('_CHEETAH_genTime = __CHEETAH_genTime__')
3777+ self._generatedAttribs.append('_CHEETAH_genTimestamp = __CHEETAH_genTimestamp__')
3778+
3779+ self._generatedAttribs.append('_CHEETAH_src = __CHEETAH_src__')
3780+ self._generatedAttribs.append(
3781+ '_CHEETAH_srcLastModified = __CHEETAH_srcLastModified__')
3782+
3783+ if self.setting('templateMetaclass'):
3784+ self._generatedAttribs.append('__metaclass__ = '+self.setting('templateMetaclass'))
3785+ self._initMethChunks = []
3786+ self._blockMetaData = {}
3787+ self._errorCatcherCount = 0
3788+ self._placeholderToErrorCatcherMap = {}
3789+
3790+ def cleanupState(self):
3791+ while self._activeMethodsList:
3792+ methCompiler = self._popActiveMethodCompiler()
3793+ self._swallowMethodCompiler(methCompiler)
3794+ self._setupInitMethod()
3795+ if self._mainMethodName == 'respond':
3796+ if self.setting('setup__str__method'):
3797+ self._generatedAttribs.append('def __str__(self): return self.respond()')
3798+ self.addAttribute('_mainCheetahMethod_for_' + self._className +
3799+ '= ' + repr(self._mainMethodName) )
3800+
3801+ def _setupInitMethod(self):
3802+ __init__ = self._spawnMethodCompiler('__init__',
3803+ klass=self.methodCompilerClassForInit)
3804+ __init__.setMethodSignature("def __init__(self, *args, **KWs)")
3805+ __init__.addChunk('super(%s, self).__init__(*args, **KWs)' % self._className)
3806+ __init__.addChunk(_initMethod_initCheetah % {'className' : self._className})
3807+ for chunk in self._initMethChunks:
3808+ __init__.addChunk(chunk)
3809+ __init__.cleanupState()
3810+ self._swallowMethodCompiler(__init__, pos=0)
3811+
3812+ def _addSourceFileMonitoring(self, fileName):
3813+ # @@TR: this stuff needs auditing for Cheetah 2.0
3814+ # the first bit is added to init
3815+ self.addChunkToInit('self._filePath = ' + repr(fileName))
3816+ self.addChunkToInit('self._fileMtime = ' + str(getmtime(fileName)) )
3817+
3818+ # the rest is added to the main output method of the class ('mainMethod')
3819+ self.addChunk('if exists(self._filePath) and ' +
3820+ 'getmtime(self._filePath) > self._fileMtime:')
3821+ self.indent()
3822+ self.addChunk('self._compile(file=self._filePath, moduleName='+self._className + ')')
3823+ self.addChunk(
3824+ 'write(getattr(self, self._mainCheetahMethod_for_' + self._className +
3825+ ')(trans=trans))')
3826+ self.addStop()
3827+ self.dedent()
3828+
3829+ def setClassName(self, name):
3830+ self._className = name
3831+
3832+ def className(self):
3833+ return self._className
3834+
3835+ def setBaseClass(self, baseClassName):
3836+ self._baseClass = baseClassName
3837+
3838+ def setMainMethodName(self, methodName):
3839+ if methodName == self._mainMethodName:
3840+ return
3841+ ## change the name in the methodCompiler and add new reference
3842+ mainMethod = self._methodsIndex[self._mainMethodName]
3843+ mainMethod.setMethodName(methodName)
3844+ self._methodsIndex[methodName] = mainMethod
3845+
3846+ ## make sure that fileUpdate code still works properly:
3847+ chunkToChange = ('write(self.' + self._mainMethodName + '(trans=trans))')
3848+ chunks = mainMethod._methodBodyChunks
3849+ if chunkToChange in chunks:
3850+ for i in range(len(chunks)):
3851+ if chunks[i] == chunkToChange:
3852+ chunks[i] = ('write(self.' + methodName + '(trans=trans))')
3853+ ## get rid of the old reference and update self._mainMethodName
3854+ del self._methodsIndex[self._mainMethodName]
3855+ self._mainMethodName = methodName
3856+
3857+ def setMainMethodArgs(self, argsList):
3858+ mainMethodCompiler = self._methodsIndex[self._mainMethodName]
3859+ for argName, defVal in argsList:
3860+ mainMethodCompiler.addMethArg(argName, defVal)
3861+
3862+
3863+ def _spawnMethodCompiler(self, methodName, klass=None,
3864+ initialMethodComment=None):
3865+ if klass is None:
3866+ klass = self.methodCompilerClass
3867+
3868+ decorators = self._decoratorsForNextMethod or []
3869+ self._decoratorsForNextMethod = []
3870+ methodCompiler = klass(methodName, classCompiler=self,
3871+ decorators=decorators,
3872+ initialMethodComment=initialMethodComment)
3873+ self._methodsIndex[methodName] = methodCompiler
3874+ return methodCompiler
3875+
3876+ def _setActiveMethodCompiler(self, methodCompiler):
3877+ self._activeMethodsList.append(methodCompiler)
3878+
3879+ def _getActiveMethodCompiler(self):
3880+ return self._activeMethodsList[-1]
3881+
3882+ def _popActiveMethodCompiler(self):
3883+ return self._activeMethodsList.pop()
3884+
3885+ def _swallowMethodCompiler(self, methodCompiler, pos=None):
3886+ methodCompiler.cleanupState()
3887+ if pos==None:
3888+ self._finishedMethodsList.append( methodCompiler )
3889+ else:
3890+ self._finishedMethodsList.insert(pos, methodCompiler)
3891+ return methodCompiler
3892+
3893+ def startMethodDef(self, methodName, argsList, parserComment):
3894+ methodCompiler = self._spawnMethodCompiler(
3895+ methodName, initialMethodComment=parserComment)
3896+ self._setActiveMethodCompiler(methodCompiler)
3897+ for argName, defVal in argsList:
3898+ methodCompiler.addMethArg(argName, defVal)
3899+
3900+ def _finishedMethods(self):
3901+ return self._finishedMethodsList
3902+
3903+ def addDecorator(self, decoratorExpr):
3904+ """Set the decorator to be used with the next method in the source.
3905+
3906+ See _spawnMethodCompiler() and MethodCompiler for the details of how
3907+ this is used.
3908+ """
3909+ self._decoratorsForNextMethod.append(decoratorExpr)
3910+
3911+ def addClassDocString(self, line):
3912+ self._classDocStringLines.append( line.replace('%', '%%'))
3913+
3914+ def addChunkToInit(self, chunk):
3915+ self._initMethChunks.append(chunk)
3916+
3917+ def addAttribute(self, attribExpr):
3918+ ## first test to make sure that the user hasn't used any fancy Cheetah syntax
3919+ # (placeholders, directives, etc.) inside the expression
3920+ if attribExpr.find('VFN(') != -1 or attribExpr.find('VFFSL(') != -1:
3921+ raise ParseError(self,
3922+ 'Invalid #attr directive.' +
3923+ ' It should only contain simple Python literals.')
3924+ ## now add the attribute
3925+ self._generatedAttribs.append(attribExpr)
3926+
3927+ def addSuper(self, argsList, parserComment=None):
3928+ className = self._className #self._baseClass
3929+ methodName = self._getActiveMethodCompiler().methodName()
3930+
3931+ argStringChunks = []
3932+ for arg in argsList:
3933+ chunk = arg[0]
3934+ if not arg[1] == None:
3935+ chunk += '=' + arg[1]
3936+ argStringChunks.append(chunk)
3937+ argString = ','.join(argStringChunks)
3938+
3939+ self.addFilteredChunk(
3940+ 'super(%(className)s, self).%(methodName)s(%(argString)s)'%locals())
3941+
3942+ def addErrorCatcherCall(self, codeChunk, rawCode='', lineCol=''):
3943+ if rawCode in self._placeholderToErrorCatcherMap:
3944+ methodName = self._placeholderToErrorCatcherMap[rawCode]
3945+ if not self.setting('outputRowColComments'):
3946+ self._methodsIndex[methodName].addMethDocString(
3947+ 'plus at line %s, col %s'%lineCol)
3948+ return methodName
3949+
3950+ self._errorCatcherCount += 1
3951+ methodName = '__errorCatcher' + str(self._errorCatcherCount)
3952+ self._placeholderToErrorCatcherMap[rawCode] = methodName
3953+
3954+ catcherMeth = self._spawnMethodCompiler(
3955+ methodName,
3956+ klass=MethodCompiler,
3957+ initialMethodComment=('## CHEETAH: Generated from ' + rawCode +
3958+ ' at line %s, col %s'%lineCol + '.')
3959+ )
3960+ catcherMeth.setMethodSignature('def ' + methodName +
3961+ '(self, localsDict={})')
3962+ # is this use of localsDict right?
3963+ catcherMeth.addChunk('try:')
3964+ catcherMeth.indent()
3965+ catcherMeth.addChunk("return eval('''" + codeChunk +
3966+ "''', globals(), localsDict)")
3967+ catcherMeth.dedent()
3968+ catcherMeth.addChunk('except self._CHEETAH__errorCatcher.exceptions(), e:')
3969+ catcherMeth.indent()
3970+ catcherMeth.addChunk("return self._CHEETAH__errorCatcher.warn(exc_val=e, code= " +
3971+ repr(codeChunk) + " , rawCode= " +
3972+ repr(rawCode) + " , lineCol=" + str(lineCol) +")")
3973+
3974+ catcherMeth.cleanupState()
3975+
3976+ self._swallowMethodCompiler(catcherMeth)
3977+ return methodName
3978+
3979+ def closeDef(self):
3980+ self.commitStrConst()
3981+ methCompiler = self._popActiveMethodCompiler()
3982+ self._swallowMethodCompiler(methCompiler)
3983+
3984+ def closeBlock(self):
3985+ self.commitStrConst()
3986+ methCompiler = self._popActiveMethodCompiler()
3987+ methodName = methCompiler.methodName()
3988+ if self.setting('includeBlockMarkers'):
3989+ endMarker = self.setting('blockMarkerEnd')
3990+ methCompiler.addStrConst(endMarker[0] + methodName + endMarker[1])
3991+ self._swallowMethodCompiler(methCompiler)
3992+
3993+ #metaData = self._blockMetaData[methodName]
3994+ #rawDirective = metaData['raw']
3995+ #lineCol = metaData['lineCol']
3996+
3997+ ## insert the code to call the block, caching if #cache directive is on
3998+ codeChunk = 'self.' + methodName + '(trans=trans)'
3999+ self.addChunk(codeChunk)
4000+
4001+ #self.appendToPrevChunk(' # generated from ' + repr(rawDirective) )
4002+ #if self.setting('outputRowColComments'):
4003+ # self.appendToPrevChunk(' at line %s, col %s' % lineCol + '.')
4004+
4005+
4006+ ## code wrapping methods
4007+
4008+ def classDef(self):
4009+ if self._classDef:
4010+ return self._classDef
4011+ else:
4012+ return self.wrapClassDef()
4013+
4014+ __str__ = classDef
4015+ __unicode__ = classDef
4016+
4017+ def wrapClassDef(self):
4018+ ind = self.setting('indentationStep')
4019+ classDefChunks = [self.classSignature(),
4020+ self.classDocstring(),
4021+ ]
4022+ def addMethods():
4023+ classDefChunks.extend([
4024+ ind + '#'*50,
4025+ ind + '## CHEETAH GENERATED METHODS',
4026+ '\n',
4027+ self.methodDefs(),
4028+ ])
4029+ def addAttributes():
4030+ classDefChunks.extend([
4031+ ind + '#'*50,
4032+ ind + '## CHEETAH GENERATED ATTRIBUTES',
4033+ '\n',
4034+ self.attributes(),
4035+ ])
4036+ if self.setting('outputMethodsBeforeAttributes'):
4037+ addMethods()
4038+ addAttributes()
4039+ else:
4040+ addAttributes()
4041+ addMethods()
4042+
4043+ classDef = '\n'.join(classDefChunks)
4044+ self._classDef = classDef
4045+ return classDef
4046+
4047+
4048+ def classSignature(self):
4049+ return "class %s(%s):" % (self.className(), self._baseClass)
4050+
4051+ def classDocstring(self):
4052+ if not self._classDocStringLines:
4053+ return ''
4054+ ind = self.setting('indentationStep')
4055+ docStr = ('%(ind)s"""\n%(ind)s' +
4056+ '\n%(ind)s'.join(self._classDocStringLines) +
4057+ '\n%(ind)s"""\n'
4058+ ) % {'ind':ind}
4059+ return docStr
4060+
4061+ def methodDefs(self):
4062+ methodDefs = [methGen.methodDef() for methGen in self._finishedMethods()]
4063+ return '\n\n'.join(methodDefs)
4064+
4065+ def attributes(self):
4066+ attribs = [self.setting('indentationStep') + str(attrib)
4067+ for attrib in self._generatedAttribs ]
4068+ return '\n\n'.join(attribs)
4069+
4070+class AutoClassCompiler(ClassCompiler):
4071+ pass
4072+
4073+##################################################
4074+## MODULE COMPILERS
4075+
4076+class ModuleCompiler(SettingsManager, GenUtils):
4077+
4078+ parserClass = Parser
4079+ classCompilerClass = AutoClassCompiler
4080+
4081+ def __init__(self, source=None, file=None,
4082+ moduleName='DynamicallyCompiledCheetahTemplate',
4083+ mainClassName=None, # string
4084+ mainMethodName=None, # string
4085+ baseclassName=None, # string
4086+ extraImportStatements=None, # list of strings
4087+ settings=None # dict
4088+ ):
4089+ super(ModuleCompiler, self).__init__()
4090+ if settings:
4091+ self.updateSettings(settings)
4092+ # disable useStackFrames if the C version of NameMapper isn't compiled
4093+ # it's painfully slow in the Python version and bites Windows users all
4094+ # the time:
4095+ if not NameMapper.C_VERSION:
4096+ if not sys.platform.startswith('java'):
4097+ warnings.warn(
4098+ "\nYou don't have the C version of NameMapper installed! "
4099+ "I'm disabling Cheetah's useStackFrames option as it is "
4100+ "painfully slow with the Python version of NameMapper. "
4101+ "You should get a copy of Cheetah with the compiled C version of NameMapper."
4102+ )
4103+ self.setSetting('useStackFrames', False)
4104+
4105+ self._compiled = False
4106+ self._moduleName = moduleName
4107+ if not mainClassName:
4108+ self._mainClassName = moduleName
4109+ else:
4110+ self._mainClassName = mainClassName
4111+ self._mainMethodNameArg = mainMethodName
4112+ if mainMethodName:
4113+ self.setSetting('mainMethodName', mainMethodName)
4114+ self._baseclassName = baseclassName
4115+
4116+ self._filePath = None
4117+ self._fileMtime = None
4118+
4119+ if source and file:
4120+ raise TypeError("Cannot compile from a source string AND file.")
4121+ elif isinstance(file, basestring): # it's a filename.
4122+ f = open(file) # Raises IOError.
4123+ source = f.read()
4124+ f.close()
4125+ self._filePath = file
4126+ self._fileMtime = os.path.getmtime(file)
4127+ elif hasattr(file, 'read'):
4128+ source = file.read() # Can't set filename or mtime--they're not accessible.
4129+ elif file:
4130+ raise TypeError("'file' argument must be a filename string or file-like object")
4131+
4132+ if self._filePath:
4133+ self._fileDirName, self._fileBaseName = os.path.split(self._filePath)
4134+ self._fileBaseNameRoot, self._fileBaseNameExt = os.path.splitext(self._fileBaseName)
4135+
4136+ if not isinstance(source, basestring):
4137+ source = unicode(source)
4138+ # by converting to string here we allow objects such as other Templates
4139+ # to be passed in
4140+
4141+ # Handle the #indent directive by converting it to other directives.
4142+ # (Over the long term we'll make it a real directive.)
4143+ if source == "":
4144+ warnings.warn("You supplied an empty string for the source!", )
4145+
4146+ else:
4147+ unicodeMatch = unicodeDirectiveRE.search(source)
4148+ encodingMatch = encodingDirectiveRE.search(source)
4149+ if unicodeMatch:
4150+ if encodingMatch:
4151+ raise ParseError(
4152+ self, "#encoding and #unicode are mutually exclusive! "
4153+ "Use one or the other.")
4154+ source = unicodeDirectiveRE.sub('', source)
4155+ if isinstance(source, str):
4156+ encoding = unicodeMatch.group(1) or 'ascii'
4157+ source = unicode(source, encoding)
4158+ elif encodingMatch:
4159+ encodings = encodingMatch.groups()
4160+ if len(encodings):
4161+ encoding = encodings[0]
4162+ source = source.decode(encoding)
4163+ else:
4164+ source = unicode(source)
4165+
4166+ if source.find('#indent') != -1: #@@TR: undocumented hack
4167+ source = indentize(source)
4168+
4169+ self._parser = self.parserClass(source, filename=self._filePath, compiler=self)
4170+ self._setupCompilerState()
4171+
4172+ def __getattr__(self, name):
4173+ """Provide one-way access to the methods and attributes of the
4174+ ClassCompiler, and thereby the MethodCompilers as well.
4175+
4176+ WARNING: Use .setMethods to assign the attributes of the ClassCompiler
4177+ from the methods of this class!!! or you will be assigning to attributes
4178+ of this object instead.
4179+ """
4180+ if name in self.__dict__:
4181+ return self.__dict__[name]
4182+ elif hasattr(self.__class__, name):
4183+ return getattr(self.__class__, name)
4184+ elif self._activeClassesList and hasattr(self._activeClassesList[-1], name):
4185+ return getattr(self._activeClassesList[-1], name)
4186+ else:
4187+ raise AttributeError(name)
4188+
4189+ def _initializeSettings(self):
4190+ self.updateSettings(copy.deepcopy(DEFAULT_COMPILER_SETTINGS))
4191+
4192+ def _setupCompilerState(self):
4193+ self._activeClassesList = []
4194+ self._finishedClassesList = [] # listed by ordered
4195+ self._finishedClassIndex = {} # listed by name
4196+ self._moduleDef = None
4197+ self._moduleShBang = '#!/usr/bin/env python'
4198+ self._moduleEncoding = 'ascii'
4199+ self._moduleEncodingStr = ''
4200+ self._moduleHeaderLines = []
4201+ self._moduleDocStringLines = []
4202+ self._specialVars = {}
4203+ self._importStatements = [
4204+ "import sys",
4205+ "import os",
4206+ "import os.path",
4207+ 'try:',
4208+ ' import builtins as builtin',
4209+ 'except ImportError:',
4210+ ' import __builtin__ as builtin',
4211+ "from os.path import getmtime, exists",
4212+ "import time",
4213+ "import types",
4214+ "from Cheetah.Version import MinCompatibleVersion as RequiredCheetahVersion",
4215+ "from Cheetah.Version import MinCompatibleVersionTuple as RequiredCheetahVersionTuple",
4216+ "from Cheetah.Template import Template",
4217+ "from Cheetah.DummyTransaction import *",
4218+ "from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList",
4219+ "from Cheetah.CacheRegion import CacheRegion",
4220+ "import Cheetah.Filters as Filters",
4221+ "import Cheetah.ErrorCatchers as ErrorCatchers",
4222+ ]
4223+
4224+ self._importedVarNames = ['sys',
4225+ 'os',
4226+ 'os.path',
4227+ 'time',
4228+ 'types',
4229+ 'Template',
4230+ 'DummyTransaction',
4231+ 'NotFound',
4232+ 'Filters',
4233+ 'ErrorCatchers',
4234+ 'CacheRegion',
4235+ ]
4236+
4237+ self._moduleConstants = [
4238+ "VFFSL=valueFromFrameOrSearchList",
4239+ "VFSL=valueFromSearchList",
4240+ "VFN=valueForName",
4241+ "currentTime=time.time",
4242+ ]
4243+
4244+ def compile(self):
4245+ classCompiler = self._spawnClassCompiler(self._mainClassName)
4246+ if self._baseclassName:
4247+ classCompiler.setBaseClass(self._baseclassName)
4248+ self._addActiveClassCompiler(classCompiler)
4249+ self._parser.parse()
4250+ self._swallowClassCompiler(self._popActiveClassCompiler())
4251+ self._compiled = True
4252+ self._parser.cleanup()
4253+
4254+ def _spawnClassCompiler(self, className, klass=None):
4255+ if klass is None:
4256+ klass = self.classCompilerClass
4257+ classCompiler = klass(className,
4258+ moduleCompiler=self,
4259+ mainMethodName=self.setting('mainMethodName'),
4260+ fileName=self._filePath,
4261+ settingsManager=self,
4262+ )
4263+ return classCompiler
4264+
4265+ def _addActiveClassCompiler(self, classCompiler):
4266+ self._activeClassesList.append(classCompiler)
4267+
4268+ def _getActiveClassCompiler(self):
4269+ return self._activeClassesList[-1]
4270+
4271+ def _popActiveClassCompiler(self):
4272+ return self._activeClassesList.pop()
4273+
4274+ def _swallowClassCompiler(self, classCompiler):
4275+ classCompiler.cleanupState()
4276+ self._finishedClassesList.append( classCompiler )
4277+ self._finishedClassIndex[classCompiler.className()] = classCompiler
4278+ return classCompiler
4279+
4280+ def _finishedClasses(self):
4281+ return self._finishedClassesList
4282+
4283+ def importedVarNames(self):
4284+ return self._importedVarNames
4285+
4286+ def addImportedVarNames(self, varNames, raw_statement=None):
4287+ settings = self.settings()
4288+ if not varNames:
4289+ return
4290+ if not settings.get('useLegacyImportMode'):
4291+ if raw_statement and getattr(self, '_methodBodyChunks'):
4292+ self.addChunk(raw_statement)
4293+ else:
4294+ self._importedVarNames.extend(varNames)
4295+
4296+ ## methods for adding stuff to the module and class definitions
4297+
4298+ def setBaseClass(self, baseClassName):
4299+ if self._mainMethodNameArg:
4300+ self.setMainMethodName(self._mainMethodNameArg)
4301+ else:
4302+ self.setMainMethodName(self.setting('mainMethodNameForSubclasses'))
4303+
4304+ if self.setting('handlerForExtendsDirective'):
4305+ handler = self.setting('handlerForExtendsDirective')
4306+ baseClassName = handler(compiler=self, baseClassName=baseClassName)
4307+ self._getActiveClassCompiler().setBaseClass(baseClassName)
4308+ elif (not self.setting('autoImportForExtendsDirective')
4309+ or baseClassName=='object' or baseClassName in self.importedVarNames()):
4310+ self._getActiveClassCompiler().setBaseClass(baseClassName)
4311+ # no need to import
4312+ else:
4313+ ##################################################
4314+ ## If the #extends directive contains a classname or modulename that isn't
4315+ # in self.importedVarNames() already, we assume that we need to add
4316+ # an implied 'from ModName import ClassName' where ModName == ClassName.
4317+ # - This is the case in WebKit servlet modules.
4318+ # - We also assume that the final . separates the classname from the
4319+ # module name. This might break if people do something really fancy
4320+ # with their dots and namespaces.
4321+ baseclasses = baseClassName.split(',')
4322+ for klass in baseclasses:
4323+ chunks = klass.split('.')
4324+ if len(chunks)==1:
4325+ self._getActiveClassCompiler().setBaseClass(klass)
4326+ if klass not in self.importedVarNames():
4327+ modName = klass
4328+ # we assume the class name to be the module name
4329+ # and that it's not a builtin:
4330+ importStatement = "from %s import %s" % (modName, klass)
4331+ self.addImportStatement(importStatement)
4332+ self.addImportedVarNames((klass,))
4333+ else:
4334+ needToAddImport = True
4335+ modName = chunks[0]
4336+ #print chunks, ':', self.importedVarNames()
4337+ for chunk in chunks[1:-1]:
4338+ if modName in self.importedVarNames():
4339+ needToAddImport = False
4340+ finalBaseClassName = klass.replace(modName+'.', '')
4341+ self._getActiveClassCompiler().setBaseClass(finalBaseClassName)
4342+ break
4343+ else:
4344+ modName += '.'+chunk
4345+ if needToAddImport:
4346+ modName, finalClassName = '.'.join(chunks[:-1]), chunks[-1]
4347+ #if finalClassName != chunks[:-1][-1]:
4348+ if finalClassName != chunks[-2]:
4349+ # we assume the class name to be the module name
4350+ modName = '.'.join(chunks)
4351+ self._getActiveClassCompiler().setBaseClass(finalClassName)
4352+ importStatement = "from %s import %s" % (modName, finalClassName)
4353+ self.addImportStatement(importStatement)
4354+ self.addImportedVarNames( [finalClassName,] )
4355+
4356+ def setCompilerSetting(self, key, valueExpr):
4357+ self.setSetting(key, eval(valueExpr) )
4358+ self._parser.configureParser()
4359+
4360+ def setCompilerSettings(self, keywords, settingsStr):
4361+ KWs = keywords
4362+ merge = True
4363+ if 'nomerge' in KWs:
4364+ merge = False
4365+
4366+ if 'reset' in KWs:
4367+ # @@TR: this is actually caught by the parser at the moment.
4368+ # subject to change in the future
4369+ self._initializeSettings()
4370+ self._parser.configureParser()
4371+ return
4372+ elif 'python' in KWs:
4373+ settingsReader = self.updateSettingsFromPySrcStr
4374+ # this comes from SettingsManager
4375+ else:
4376+ # this comes from SettingsManager
4377+ settingsReader = self.updateSettingsFromConfigStr
4378+
4379+ settingsReader(settingsStr)
4380+ self._parser.configureParser()
4381+
4382+ def setShBang(self, shBang):
4383+ self._moduleShBang = shBang
4384+
4385+ def setModuleEncoding(self, encoding):
4386+ self._moduleEncoding = encoding
4387+
4388+ def getModuleEncoding(self):
4389+ return self._moduleEncoding
4390+
4391+ def addModuleHeader(self, line):
4392+ """Adds a header comment to the top of the generated module.
4393+ """
4394+ self._moduleHeaderLines.append(line)
4395+
4396+ def addModuleDocString(self, line):
4397+ """Adds a line to the generated module docstring.
4398+ """
4399+ self._moduleDocStringLines.append(line)
4400+
4401+ def addModuleGlobal(self, line):
4402+ """Adds a line of global module code. It is inserted after the import
4403+ statements and Cheetah default module constants.
4404+ """
4405+ self._moduleConstants.append(line)
4406+
4407+ def addSpecialVar(self, basename, contents, includeUnderscores=True):
4408+ """Adds module __specialConstant__ to the module globals.
4409+ """
4410+ name = includeUnderscores and '__'+basename+'__' or basename
4411+ self._specialVars[name] = contents.strip()
4412+
4413+ def addImportStatement(self, impStatement):
4414+ settings = self.settings()
4415+ if not self._methodBodyChunks or settings.get('useLegacyImportMode'):
4416+ # In the case where we are importing inline in the middle of a source block
4417+ # we don't want to inadvertantly import the module at the top of the file either
4418+ self._importStatements.append(impStatement)
4419+
4420+ #@@TR 2005-01-01: there's almost certainly a cleaner way to do this!
4421+ importVarNames = impStatement[impStatement.find('import') + len('import'):].split(',')
4422+ importVarNames = [var.split()[-1] for var in importVarNames] # handles aliases
4423+ importVarNames = [var for var in importVarNames if not var == '*']
4424+ self.addImportedVarNames(importVarNames, raw_statement=impStatement) #used by #extend for auto-imports
4425+
4426+ def addAttribute(self, attribName, expr):
4427+ self._getActiveClassCompiler().addAttribute(attribName + ' =' + expr)
4428+
4429+ def addComment(self, comm):
4430+ if re.match(r'#+$', comm): # skip bar comments
4431+ return
4432+
4433+ specialVarMatch = specialVarRE.match(comm)
4434+ if specialVarMatch:
4435+ # @@TR: this is a bit hackish and is being replaced with
4436+ # #set module varName = ...
4437+ return self.addSpecialVar(specialVarMatch.group(1),
4438+ comm[specialVarMatch.end():])
4439+ elif comm.startswith('doc:'):
4440+ addLine = self.addMethDocString
4441+ comm = comm[len('doc:'):].strip()
4442+ elif comm.startswith('doc-method:'):
4443+ addLine = self.addMethDocString
4444+ comm = comm[len('doc-method:'):].strip()
4445+ elif comm.startswith('doc-module:'):
4446+ addLine = self.addModuleDocString
4447+ comm = comm[len('doc-module:'):].strip()
4448+ elif comm.startswith('doc-class:'):
4449+ addLine = self.addClassDocString
4450+ comm = comm[len('doc-class:'):].strip()
4451+ elif comm.startswith('header:'):
4452+ addLine = self.addModuleHeader
4453+ comm = comm[len('header:'):].strip()
4454+ else:
4455+ addLine = self.addMethComment
4456+
4457+ for line in comm.splitlines():
4458+ addLine(line)
4459+
4460+ ## methods for module code wrapping
4461+
4462+ def getModuleCode(self):
4463+ if not self._compiled:
4464+ self.compile()
4465+ if self._moduleDef:
4466+ return self._moduleDef
4467+ else:
4468+ return self.wrapModuleDef()
4469+
4470+ __str__ = getModuleCode
4471+
4472+ def wrapModuleDef(self):
4473+ self.addSpecialVar('CHEETAH_docstring', self.setting('defDocStrMsg'))
4474+ self.addModuleGlobal('__CHEETAH_version__ = %r'%Version)
4475+ self.addModuleGlobal('__CHEETAH_versionTuple__ = %r'%(VersionTuple,))
4476+ if self.setting('addTimestampsToCompilerOutput'):
4477+ self.addModuleGlobal('__CHEETAH_genTime__ = %r'%time.time())
4478+ self.addModuleGlobal('__CHEETAH_genTimestamp__ = %r'%self.timestamp())
4479+ if self._filePath:
4480+ timestamp = self.timestamp(self._fileMtime)
4481+ self.addModuleGlobal('__CHEETAH_src__ = %r'%self._filePath)
4482+ self.addModuleGlobal('__CHEETAH_srcLastModified__ = %r'%timestamp)
4483+ else:
4484+ self.addModuleGlobal('__CHEETAH_src__ = None')
4485+ self.addModuleGlobal('__CHEETAH_srcLastModified__ = None')
4486+
4487+ moduleDef = """%(header)s
4488+%(docstring)s
4489+
4490+##################################################
4491+## DEPENDENCIES
4492+%(imports)s
4493+
4494+##################################################
4495+## MODULE CONSTANTS
4496+%(constants)s
4497+%(specialVars)s
4498+
4499+if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple:
4500+ raise AssertionError(
4501+ 'This template was compiled with Cheetah version'
4502+ ' %%s. Templates compiled before version %%s must be recompiled.'%%(
4503+ __CHEETAH_version__, RequiredCheetahVersion))
4504+
4505+##################################################
4506+## CLASSES
4507+
4508+%(classes)s
4509+
4510+## END CLASS DEFINITION
4511+
4512+if not hasattr(%(mainClassName)s, '_initCheetahAttributes'):
4513+ templateAPIClass = getattr(%(mainClassName)s, '_CHEETAH_templateClass', Template)
4514+ templateAPIClass._addCheetahPlumbingCodeToClass(%(mainClassName)s)
4515+
4516+%(footer)s
4517+""" % {'header': self.moduleHeader(),
4518+ 'docstring': self.moduleDocstring(),
4519+ 'specialVars': self.specialVars(),
4520+ 'imports': self.importStatements(),
4521+ 'constants': self.moduleConstants(),
4522+ 'classes': self.classDefs(),
4523+ 'footer': self.moduleFooter(),
4524+ 'mainClassName': self._mainClassName,
4525+ }
4526+
4527+ self._moduleDef = moduleDef
4528+ return moduleDef
4529+
4530+ def timestamp(self, theTime=None):
4531+ if not theTime:
4532+ theTime = time.time()
4533+ return time.asctime(time.localtime(theTime))
4534+
4535+ def moduleHeader(self):
4536+ header = self._moduleShBang + '\n'
4537+ header += self._moduleEncodingStr + '\n'
4538+ if self._moduleHeaderLines:
4539+ offSet = self.setting('commentOffset')
4540+
4541+ header += (
4542+ '#' + ' '*offSet +
4543+ ('\n#'+ ' '*offSet).join(self._moduleHeaderLines) + '\n')
4544+
4545+ return header
4546+
4547+ def moduleDocstring(self):
4548+ if not self._moduleDocStringLines:
4549+ return ''
4550+
4551+ return ('"""' +
4552+ '\n'.join(self._moduleDocStringLines) +
4553+ '\n"""\n')
4554+
4555+ def specialVars(self):
4556+ chunks = []
4557+ theVars = self._specialVars
4558+ keys = sorted(theVars.keys())
4559+ for key in keys:
4560+ chunks.append(key + ' = ' + repr(theVars[key]) )
4561+ return '\n'.join(chunks)
4562+
4563+ def importStatements(self):
4564+ return '\n'.join(self._importStatements)
4565+
4566+ def moduleConstants(self):
4567+ return '\n'.join(self._moduleConstants)
4568+
4569+ def classDefs(self):
4570+ classDefs = [klass.classDef() for klass in self._finishedClasses()]
4571+ return '\n\n'.join(classDefs)
4572+
4573+ def moduleFooter(self):
4574+ return """
4575+# CHEETAH was developed by Tavis Rudd and Mike Orr
4576+# with code, advice and input from many other volunteers.
4577+# For more information visit http://www.CheetahTemplate.org/
4578+
4579+##################################################
4580+## if run from command line:
4581+if __name__ == '__main__':
4582+ from Cheetah.TemplateCmdLineIface import CmdLineIface
4583+ CmdLineIface(templateObj=%(className)s()).run()
4584+
4585+""" % {'className':self._mainClassName}
4586+
4587+
4588+##################################################
4589+## Make Compiler an alias for ModuleCompiler
4590+
4591+Compiler = ModuleCompiler
4592
4593=== added file 'cheetah/DirectiveAnalyzer.py'
4594--- cheetah/DirectiveAnalyzer.py 1970-01-01 00:00:00 +0000
4595+++ cheetah/DirectiveAnalyzer.py 2010-10-19 20:12:51 +0000
4596@@ -0,0 +1,98 @@
4597+#!/usr/bin/env python
4598+
4599+import os
4600+import pprint
4601+
4602+try:
4603+ from functools import reduce
4604+except ImportError:
4605+ # Assume we have reduce
4606+ pass
4607+
4608+from Cheetah import Parser
4609+from Cheetah import Compiler
4610+from Cheetah import Template
4611+
4612+class Analyzer(Parser.Parser):
4613+ def __init__(self, *args, **kwargs):
4614+ self.calls = {}
4615+ super(Analyzer, self).__init__(*args, **kwargs)
4616+
4617+ def eatDirective(self):
4618+ directive = self.matchDirective()
4619+ try:
4620+ self.calls[directive] += 1
4621+ except KeyError:
4622+ self.calls[directive] = 1
4623+ super(Analyzer, self).eatDirective()
4624+
4625+class AnalysisCompiler(Compiler.ModuleCompiler):
4626+ parserClass = Analyzer
4627+
4628+
4629+def analyze(source):
4630+ klass = Template.Template.compile(source, compilerClass=AnalysisCompiler)
4631+ return klass._CHEETAH_compilerInstance._parser.calls
4632+
4633+def main_file(f):
4634+ fd = open(f, 'r')
4635+ try:
4636+ print u'>>> Analyzing %s' % f
4637+ calls = analyze(fd.read())
4638+ return calls
4639+ finally:
4640+ fd.close()
4641+
4642+
4643+def _find_templates(directory, suffix):
4644+ for root, dirs, files in os.walk(directory):
4645+ for f in files:
4646+ if not f.endswith(suffix):
4647+ continue
4648+ yield root + os.path.sep + f
4649+
4650+def _analyze_templates(iterable):
4651+ for template in iterable:
4652+ yield main_file(template)
4653+
4654+def main_dir(opts):
4655+ results = _analyze_templates(_find_templates(opts.dir, opts.suffix))
4656+ totals = {}
4657+ for series in results:
4658+ if not series:
4659+ continue
4660+ for k, v in series.iteritems():
4661+ try:
4662+ totals[k] += v
4663+ except KeyError:
4664+ totals[k] = v
4665+ return totals
4666+
4667+
4668+def main():
4669+ from optparse import OptionParser
4670+ op = OptionParser()
4671+ op.add_option('-f', '--file', dest='file', default=None,
4672+ help='Specify a single file to analyze')
4673+ op.add_option('-d', '--dir', dest='dir', default=None,
4674+ help='Specify a directory of templates to analyze')
4675+ op.add_option('--suffix', default='tmpl', dest='suffix',
4676+ help='Specify a custom template file suffix for the -d option (default: "tmpl")')
4677+ opts, args = op.parse_args()
4678+
4679+ if not opts.file and not opts.dir:
4680+ op.print_help()
4681+ return
4682+
4683+ results = None
4684+ if opts.file:
4685+ results = main_file(opts.file)
4686+ if opts.dir:
4687+ results = main_dir(opts)
4688+
4689+ pprint.pprint(results)
4690+
4691+
4692+if __name__ == '__main__':
4693+ main()
4694+
4695
4696=== added file 'cheetah/Django.py'
4697--- cheetah/Django.py 1970-01-01 00:00:00 +0000
4698+++ cheetah/Django.py 2010-10-19 20:12:51 +0000
4699@@ -0,0 +1,16 @@
4700+import Cheetah.Template
4701+
4702+def render(template_file, **kwargs):
4703+ '''
4704+ Cheetah.Django.render() takes the template filename
4705+ (the filename should be a file in your Django
4706+ TEMPLATE_DIRS)
4707+
4708+ Any additional keyword arguments are passed into the
4709+ template are propogated into the template's searchList
4710+ '''
4711+ import django.http
4712+ import django.template.loader
4713+ source, loader = django.template.loader.find_template_source(template_file)
4714+ t = Cheetah.Template.Template(source, searchList=[kwargs])
4715+ return django.http.HttpResponse(t.__str__())
4716
4717=== added file 'cheetah/DummyTransaction.py'
4718--- cheetah/DummyTransaction.py 1970-01-01 00:00:00 +0000
4719+++ cheetah/DummyTransaction.py 2010-10-19 20:12:51 +0000
4720@@ -0,0 +1,108 @@
4721+
4722+'''
4723+Provides dummy Transaction and Response classes is used by Cheetah in place
4724+of real Webware transactions when the Template obj is not used directly as a
4725+Webware servlet.
4726+
4727+Warning: This may be deprecated in the future, please do not rely on any
4728+specific DummyTransaction or DummyResponse behavior
4729+'''
4730+
4731+import logging
4732+import types
4733+
4734+class DummyResponseFailure(Exception):
4735+ pass
4736+
4737+class DummyResponse(object):
4738+ '''
4739+ A dummy Response class is used by Cheetah in place of real Webware
4740+ Response objects when the Template obj is not used directly as a Webware
4741+ servlet
4742+ '''
4743+ def __init__(self):
4744+ self._outputChunks = []
4745+
4746+ def flush(self):
4747+ pass
4748+
4749+ def safeConvert(self, chunk):
4750+ # Exceptionally gross, but the safest way
4751+ # I've found to ensure I get a legit unicode object
4752+ if not chunk:
4753+ return u''
4754+ if isinstance(chunk, unicode):
4755+ return chunk
4756+ try:
4757+ return chunk.decode('utf-8', 'strict')
4758+ except UnicodeDecodeError:
4759+ try:
4760+ return chunk.decode('latin-1', 'strict')
4761+ except UnicodeDecodeError:
4762+ return chunk.decode('ascii', 'ignore')
4763+ except AttributeError:
4764+ return unicode(chunk, errors='ignore')
4765+ return chunk
4766+
4767+ def write(self, value):
4768+ self._outputChunks.append(value)
4769+
4770+ def writeln(self, txt):
4771+ write(txt)
4772+ write('\n')
4773+
4774+ def getvalue(self, outputChunks=None):
4775+ chunks = outputChunks or self._outputChunks
4776+ try:
4777+ return u''.join(chunks)
4778+ except UnicodeDecodeError, ex:
4779+ logging.debug('Trying to work around a UnicodeDecodeError in getvalue()')
4780+ logging.debug('...perhaps you could fix "%s" while you\'re debugging')
4781+ return ''.join((self.safeConvert(c) for c in chunks))
4782+
4783+ def writelines(self, *lines):
4784+ ## not used
4785+ [self.writeln(ln) for ln in lines]
4786+
4787+
4788+class DummyTransaction(object):
4789+ '''
4790+ A dummy Transaction class is used by Cheetah in place of real Webware
4791+ transactions when the Template obj is not used directly as a Webware
4792+ servlet.
4793+
4794+ It only provides a response object and method. All other methods and
4795+ attributes make no sense in this context.
4796+ '''
4797+ def __init__(self, *args, **kwargs):
4798+ self._response = None
4799+
4800+ def response(self, resp=None):
4801+ if self._response is None:
4802+ self._response = resp or DummyResponse()
4803+ return self._response
4804+
4805+
4806+class TransformerResponse(DummyResponse):
4807+ def __init__(self, *args, **kwargs):
4808+ super(TransformerResponse, self).__init__(*args, **kwargs)
4809+ self._filter = None
4810+
4811+ def getvalue(self, **kwargs):
4812+ output = super(TransformerResponse, self).getvalue(**kwargs)
4813+ if self._filter:
4814+ _filter = self._filter
4815+ if isinstance(_filter, type):
4816+ _filter = _filter()
4817+ return _filter.filter(output)
4818+ return output
4819+
4820+
4821+class TransformerTransaction(object):
4822+ def __init__(self, *args, **kwargs):
4823+ self._response = None
4824+ def response(self):
4825+ if self._response:
4826+ return self._response
4827+ return TransformerResponse()
4828+
4829
4830=== added file 'cheetah/ErrorCatchers.py'
4831--- cheetah/ErrorCatchers.py 1970-01-01 00:00:00 +0000
4832+++ cheetah/ErrorCatchers.py 2010-10-19 20:12:51 +0000
4833@@ -0,0 +1,62 @@
4834+# $Id: ErrorCatchers.py,v 1.7 2005/01/03 19:59:07 tavis_rudd Exp $
4835+"""ErrorCatcher class for Cheetah Templates
4836+
4837+Meta-Data
4838+================================================================================
4839+Author: Tavis Rudd <tavis@damnsimple.com>
4840+Version: $Revision: 1.7 $
4841+Start Date: 2001/08/01
4842+Last Revision Date: $Date: 2005/01/03 19:59:07 $
4843+"""
4844+__author__ = "Tavis Rudd <tavis@damnsimple.com>"
4845+__revision__ = "$Revision: 1.7 $"[11:-2]
4846+
4847+import time
4848+from Cheetah.NameMapper import NotFound
4849+
4850+class Error(Exception):
4851+ pass
4852+
4853+class ErrorCatcher:
4854+ _exceptionsToCatch = (NotFound,)
4855+
4856+ def __init__(self, templateObj):
4857+ pass
4858+
4859+ def exceptions(self):
4860+ return self._exceptionsToCatch
4861+
4862+ def warn(self, exc_val, code, rawCode, lineCol):
4863+ return rawCode
4864+## make an alias
4865+Echo = ErrorCatcher
4866+
4867+class BigEcho(ErrorCatcher):
4868+ def warn(self, exc_val, code, rawCode, lineCol):
4869+ return "="*15 + "&lt;" + rawCode + " could not be found&gt;" + "="*15
4870+
4871+class KeyError(ErrorCatcher):
4872+ def warn(self, exc_val, code, rawCode, lineCol):
4873+ raise KeyError("no '%s' in this Template Object's Search List" % rawCode)
4874+
4875+class ListErrors(ErrorCatcher):
4876+ """Accumulate a list of errors."""
4877+ _timeFormat = "%c"
4878+
4879+ def __init__(self, templateObj):
4880+ ErrorCatcher.__init__(self, templateObj)
4881+ self._errors = []
4882+
4883+ def warn(self, exc_val, code, rawCode, lineCol):
4884+ dict = locals().copy()
4885+ del dict['self']
4886+ dict['time'] = time.strftime(self._timeFormat,
4887+ time.localtime(time.time()))
4888+ self._errors.append(dict)
4889+ return rawCode
4890+
4891+ def listErrors(self):
4892+ """Return the list of errors."""
4893+ return self._errors
4894+
4895+
4896
4897=== added file 'cheetah/FileUtils.py'
4898--- cheetah/FileUtils.py 1970-01-01 00:00:00 +0000
4899+++ cheetah/FileUtils.py 2010-10-19 20:12:51 +0000
4900@@ -0,0 +1,357 @@
4901+
4902+from glob import glob
4903+import os
4904+from os import listdir
4905+import os.path
4906+import re
4907+from tempfile import mktemp
4908+
4909+def _escapeRegexChars(txt,
4910+ escapeRE=re.compile(r'([\$\^\*\+\.\?\{\}\[\]\(\)\|\\])')):
4911+ return escapeRE.sub(r'\\\1', txt)
4912+
4913+def findFiles(*args, **kw):
4914+ """Recursively find all the files matching a glob pattern.
4915+
4916+ This function is a wrapper around the FileFinder class. See its docstring
4917+ for details about the accepted arguments, etc."""
4918+
4919+ return FileFinder(*args, **kw).files()
4920+
4921+def replaceStrInFiles(files, theStr, repl):
4922+
4923+ """Replace all instances of 'theStr' with 'repl' for each file in the 'files'
4924+ list. Returns a dictionary with data about the matches found.
4925+
4926+ This is like string.replace() on a multi-file basis.
4927+
4928+ This function is a wrapper around the FindAndReplace class. See its
4929+ docstring for more details."""
4930+
4931+ pattern = _escapeRegexChars(theStr)
4932+ return FindAndReplace(files, pattern, repl).results()
4933+
4934+def replaceRegexInFiles(files, pattern, repl):
4935+
4936+ """Replace all instances of regex 'pattern' with 'repl' for each file in the
4937+ 'files' list. Returns a dictionary with data about the matches found.
4938+
4939+ This is like re.sub on a multi-file basis.
4940+
4941+ This function is a wrapper around the FindAndReplace class. See its
4942+ docstring for more details."""
4943+
4944+ return FindAndReplace(files, pattern, repl).results()
4945+
4946+
4947+##################################################
4948+## CLASSES
4949+
4950+class FileFinder:
4951+
4952+ """Traverses a directory tree and finds all files in it that match one of
4953+ the specified glob patterns."""
4954+
4955+ def __init__(self, rootPath,
4956+ globPatterns=('*',),
4957+ ignoreBasenames=('CVS', '.svn'),
4958+ ignoreDirs=(),
4959+ ):
4960+
4961+ self._rootPath = rootPath
4962+ self._globPatterns = globPatterns
4963+ self._ignoreBasenames = ignoreBasenames
4964+ self._ignoreDirs = ignoreDirs
4965+ self._files = []
4966+
4967+ self.walkDirTree(rootPath)
4968+
4969+ def walkDirTree(self, dir='.',
4970+
4971+ listdir=os.listdir,
4972+ isdir=os.path.isdir,
4973+ join=os.path.join,
4974+ ):
4975+
4976+ """Recursively walk through a directory tree and find matching files."""
4977+ processDir = self.processDir
4978+ filterDir = self.filterDir
4979+
4980+ pendingDirs = [dir]
4981+ addDir = pendingDirs.append
4982+ getDir = pendingDirs.pop
4983+
4984+ while pendingDirs:
4985+ dir = getDir()
4986+ ## process this dir
4987+ processDir(dir)
4988+
4989+ ## and add sub-dirs
4990+ for baseName in listdir(dir):
4991+ fullPath = join(dir, baseName)
4992+ if isdir(fullPath):
4993+ if filterDir(baseName, fullPath):
4994+ addDir( fullPath )
4995+
4996+ def filterDir(self, baseName, fullPath):
4997+
4998+ """A hook for filtering out certain dirs. """
4999+
5000+ return not (baseName in self._ignoreBasenames or
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: