diff options
author | hierro <hierro> | 2002-04-21 19:16:50 +0000 |
---|---|---|
committer | hierro <hierro> | 2002-04-21 19:16:50 +0000 |
commit | aaed495ff81c08e58901022152c5129511632543 (patch) | |
tree | 9bee272c83f859cc8cc7a18ecb99cbd04b949ccd | |
parent | 92a4a7f34ba398f182d1e63fa70e424b812377fc (diff) | |
download | python-cheetah-aaed495ff81c08e58901022152c5129511632543.tar.gz |
All changes to date.
-rw-r--r-- | docs/devel_guide_src/Makefile | 4 | ||||
-rw-r--r-- | docs/devel_guide_src/bnf.tex | 7 | ||||
-rw-r--r-- | docs/devel_guide_src/comments.tex | 7 | ||||
-rw-r--r-- | docs/devel_guide_src/compiler.tex | 11 | ||||
-rw-r--r-- | docs/devel_guide_src/compiler2.tex | 11 | ||||
-rw-r--r-- | docs/devel_guide_src/design.tex | 11 | ||||
-rw-r--r-- | docs/devel_guide_src/devel_guide.tex | 63 | ||||
-rw-r--r-- | docs/devel_guide_src/documenting.tex | 11 | ||||
-rw-r--r-- | docs/devel_guide_src/errorHandling.tex | 11 | ||||
-rw-r--r-- | docs/devel_guide_src/files.tex | 11 | ||||
-rw-r--r-- | docs/devel_guide_src/flowControl.tex | 7 | ||||
-rw-r--r-- | docs/devel_guide_src/inheritanceEtc.tex | 7 | ||||
-rw-r--r-- | docs/devel_guide_src/introduction.tex | 42 | ||||
-rw-r--r-- | docs/devel_guide_src/output.tex | 7 | ||||
-rw-r--r-- | docs/devel_guide_src/parser.tex | 11 | ||||
-rw-r--r-- | docs/devel_guide_src/patching.tex | 11 | ||||
-rw-r--r-- | docs/devel_guide_src/placeholders.tex | 512 | ||||
-rw-r--r-- | docs/devel_guide_src/pyModules.tex | 245 | ||||
-rw-r--r-- | docs/devel_guide_src/template.tex | 11 |
19 files changed, 916 insertions, 84 deletions
diff --git a/docs/devel_guide_src/Makefile b/docs/devel_guide_src/Makefile index e498386..94153e4 100644 --- a/docs/devel_guide_src/Makefile +++ b/docs/devel_guide_src/Makefile @@ -1,11 +1,13 @@ # You must change PYTHONSRC to the path of your Python source distributon. -PYTHONSRC=/home/iron/nobackup/PYTHON/Python-2.2b1 +PYTHONSRC=/home/iron/nobackup/PYTHON/Python-2.2 DOCNAME=devel_guide MKHOWTO=$(PYTHONSRC)/Doc/tools/mkhowto MAIN_TEX_FILE= devel_guide.tex all: ps pdf html htmlMultiPage text +almost-all: ps html htmlMultiPage text + pdf: $(MKHOWTO) --pdf $(MAIN_TEX_FILE) mv $(DOCNAME).pdf ../ diff --git a/docs/devel_guide_src/bnf.tex b/docs/devel_guide_src/bnf.tex new file mode 100644 index 0000000..aa7149c --- /dev/null +++ b/docs/devel_guide_src/bnf.tex @@ -0,0 +1,7 @@ +\section{A BNF Grammar of Cheetah} +\label{bnf} + + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/comments.tex b/docs/devel_guide_src/comments.tex new file mode 100644 index 0000000..ff98d72 --- /dev/null +++ b/docs/devel_guide_src/comments.tex @@ -0,0 +1,7 @@ +\section{Directives: Comments} +\label{comments.} + + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/compiler.tex b/docs/devel_guide_src/compiler.tex new file mode 100644 index 0000000..9e807ad --- /dev/null +++ b/docs/devel_guide_src/compiler.tex @@ -0,0 +1,11 @@ +\section{The compiler} +\label{compiler} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{} +\label{} + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/compiler2.tex b/docs/devel_guide_src/compiler2.tex new file mode 100644 index 0000000..7f277eb --- /dev/null +++ b/docs/devel_guide_src/compiler2.tex @@ -0,0 +1,11 @@ +\section{The Compiler (more info)} +\label{compiler2} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{} +\label{} + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/design.tex b/docs/devel_guide_src/design.tex new file mode 100644 index 0000000..84df362 --- /dev/null +++ b/docs/devel_guide_src/design.tex @@ -0,0 +1,11 @@ +\section{Design Decisions and Tradeoffs} +\label{design} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{} +\label{} + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/devel_guide.tex b/docs/devel_guide_src/devel_guide.tex index b9d48d3..1d9b46e 100644 --- a/docs/devel_guide_src/devel_guide.tex +++ b/docs/devel_guide_src/devel_guide.tex @@ -2,10 +2,10 @@ \usepackage{moreverb} %% Verbatim Code Listings \title{Cheetah Developers' Guide} -\release{0.9.12 post b2} +\release{0.9.12} -\author{The Cheetah Development Team} -\authoraddress{\email{cheetahtemplate-discuss@lists.sourceforge.net}} +\author{Mike Orr} +\authoraddress{\email{iron@mso.oz.net}} \begin{document} \maketitle @@ -13,21 +13,14 @@ %\tableofcontents -\copyright{Copyright 2002, The Cheetah Development Team. +\copyright{Copyright 2002, Mike Orr. This document may be copied and modified under the terms of the {\bf Open Publication License} \url{http://www.opencontent.org/openpub/} } -This document is not written yet. It is intended to accomplish the following: +This document is still being written. Material to include: -** A sample small .tmpl file and its corresponding .py template module. - -** Show how cheetah-compile translates placeholders into Python, as well as -caching, filter, the other directives, and comments. - -** Show how placeholders which are local/global/builtin variables get -translated to bare variable names, while other placeholders are turned into a -searchList lookup. This is what '\$' does: besides marking the beginning of a -placeholder, it intelligently converts it to one or the other. +** Show how cheetah-compile translates caching, filter, the other directives, +and comments. ** List all the methods accessible via 'self'. (Some of this may belong in the Users' Guide.) @@ -41,32 +34,44 @@ test suite works. ** History of Cheetah (including the WebOnions image). Describe major milestone changes from the changelog. -% @@MO: WebOnions logo. +% @@MO: WebOnions logo. (Goes with history.) ** Design decisions and tradeoffs that have been made. ** How to build the documentation. Why LaTeX, a minimum LaTeX reference, etc. -** Show where you can omit the '\$' and where you can't. +** Show where you can omit the '\$' (leniency) and where you can't. + +** Appendix A will be a formal BNF grammar of Cheetah. % Two files placeholders.tex and safeDelegation.tex contain material to be %incorporated into this guide. -% Why all the \$s? "Smart" variable disposition. Where you have to use them -% in expressions. -% Canadian spelling in honour of Cheetah's creator. - -%% @@MO: VFS=... is a common Python speed hack. - - %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %\include{whoShouldRead} - %\include{introduction} %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %\appendix - %\include{glossary} - %\include{grammar} - %\include{APIref} + \include{introduction} + \include{files} + \include{template} + \include{compiler} + \include{pyModules} + \include{placeholders} + \include{comments} + \include{output} + \include{inheritanceEtc} + \include{flowControl} + \include{errorHandling} + \include{parserInstructions} + + \include{parser} + \include{compiler2} + \include{design} + \include{patching} + \include{documenting} + + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \appendix + \include{bnf} + \include{safeDelegation} \end{document} % Local Variables: diff --git a/docs/devel_guide_src/documenting.tex b/docs/devel_guide_src/documenting.tex new file mode 100644 index 0000000..81063eb --- /dev/null +++ b/docs/devel_guide_src/documenting.tex @@ -0,0 +1,11 @@ +\section{Documenting Cheetah} +\label{documenting} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{} +\label{} + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/errorHandling.tex b/docs/devel_guide_src/errorHandling.tex new file mode 100644 index 0000000..e5b57d2 --- /dev/null +++ b/docs/devel_guide_src/errorHandling.tex @@ -0,0 +1,11 @@ +\section{Directives: Error Handling} +\label{errorHandling} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{} +\label{} + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/files.tex b/docs/devel_guide_src/files.tex new file mode 100644 index 0000000..083267d --- /dev/null +++ b/docs/devel_guide_src/files.tex @@ -0,0 +1,11 @@ +\section{Files} +\label{files} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{} +\label{} + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/flowControl.tex b/docs/devel_guide_src/flowControl.tex new file mode 100644 index 0000000..f3f5537 --- /dev/null +++ b/docs/devel_guide_src/flowControl.tex @@ -0,0 +1,7 @@ +\section{Directives: Flow Control} +\label{flowControl} + + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/inheritanceEtc.tex b/docs/devel_guide_src/inheritanceEtc.tex new file mode 100644 index 0000000..cd0e8c1 --- /dev/null +++ b/docs/devel_guide_src/inheritanceEtc.tex @@ -0,0 +1,7 @@ +\section{Directives: Import, Inheritance, Declaration and Assignment} +\label{inheritanceEtc} + + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/introduction.tex b/docs/devel_guide_src/introduction.tex new file mode 100644 index 0000000..6f59999 --- /dev/null +++ b/docs/devel_guide_src/introduction.tex @@ -0,0 +1,42 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\section{Introduction} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Who should read this Guide?} + +The Cheetah Developers' Guide is for those who want to learn how Cheetah works +internally, or wish to modify or extend Cheetah. It is assumed that +you've read the Cheetah Users' Guide and have an intermediate knowledge of +Python. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Contents} + +This Guide takes a behaviorist approach. First we'll look at what the +Cheetah compiler generates when it compiles a template definition, and +how it compiles the various \$placeholder features and \#directives. +Then we'll stroll through the files in the Cheetah source distribution +and show how each file contributes to the compilation and/or filling of +templates. We'll list every method/attribute inherited by a template +object. Finally, we'll describe how to submit +bugfixes/enhancements to Cheetah, and how to add to the documentation. + +Appendices will contain a BNF syntax of Cheetah constructs, and ... +\code{self} +cheetah-compile does when it converts a template definition to a .py +template module. Since +compiler, it describes what happens to all templates. + +This Guide first takes a look at the files inside the Cheetah source +distribution. Then it gives a brief overview of the \code{Template} object. +Then it describes the Cheetah compiler, how it works. Then it takes a look at +.py template modules. Then we discuss the various placeholder syntaxes, +expressions and directives, and see their effect on the .py template module. +Then we'll take a closer look at the parser and then the compiler. + +Finally, chapter ?? shows how to modify or extend Cheetah without incurring +the wrath of the developers, and how to contribute to Cheetah's documentation. + +% Local Variables: +% TeX-master: "users_guide" +% End: diff --git a/docs/devel_guide_src/output.tex b/docs/devel_guide_src/output.tex new file mode 100644 index 0000000..ea2f5c3 --- /dev/null +++ b/docs/devel_guide_src/output.tex @@ -0,0 +1,7 @@ +\section{Directives: Output} +\label{output} + + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/parser.tex b/docs/devel_guide_src/parser.tex new file mode 100644 index 0000000..ebce787 --- /dev/null +++ b/docs/devel_guide_src/parser.tex @@ -0,0 +1,11 @@ +\section{The parser (more info)} +\label{parser} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{} +\label{} + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/patching.tex b/docs/devel_guide_src/patching.tex new file mode 100644 index 0000000..7e194f6 --- /dev/null +++ b/docs/devel_guide_src/patching.tex @@ -0,0 +1,11 @@ +\section{Patching Cheetah} +\label{patching} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{} +\label{} + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/placeholders.tex b/docs/devel_guide_src/placeholders.tex index e34d5fc..c2d7de9 100644 --- a/docs/devel_guide_src/placeholders.tex +++ b/docs/devel_guide_src/placeholders.tex @@ -1,54 +1,458 @@ -This is not in legal TeX syntax yet, so don't try to compile it! - -Here's an analysis of NameMapper.py. - - valueForKey(obj, key): -Search 'obj' for 'key'. Tries to return (1) a same-name attribute, (2) -a same-name dictionary key, or (3) an attribute that is "'_' + key". -If fail, raise NameMapper.NotFound. - - valueForName(obj, name): -Wrapper for _valueForName. Splits 'name' into 'nameChunks' at dots. -'executeCallables' is, I assume, whether to do autocalling. - - _valueForName(obj, nameChunks, executeCallables): -'passNamespace' is false initially, since valueForName doesn't specify it. -Take the first identifier in nameChunks. If 'passNamespace', do -valueForKey on it (if fail, raise NameMapper.NotFoundInNamespace). -If not 'passNamespace', same but if fail, allow NameMapper.NotFound to -propagate. Why the difference in exceptions? - -Assuming the first identifier was successfully found, if the value meets -autocallable criteria, call it. - -If there are further components in nameChunks, recurse using the found -value as 'obj', and "nameChunks minus the first identifier" as -'nameChunks' (so that the next identifier will be first each recursion). -The result is a name lookup of a.b.c.d.e, looking recursively in 'obj' only. -No searchList is involved. - - valueFromSearchList(searchList, name, executeCallables): -name -> nameChunks. Call _valueForName using each element in searchList -in turn as the searchable 'obj'. 'passNamespace' is always True, which -causes the called function (_valueForName) to raise -NameMapper.NotFoundInNamespace instead of NameMapper.NotFound if fail. -Why? Especially since this function (valueFromSearchList) converts -NameMapper.NotFoundInNamespace to NameMapper.NotFound anyway, so the net -effect is the same. - - hasKey(obj, key): -Boolean whether 'obj' has 'key', searching the attributes, subscripts -and underlined attributes. There is one discrepency from -valueForKey(): the underlined attribute is searched second rather than -third. - - hasName(obj, key): -Boolean whether 'obj' has 'name', based on valueForName. - -What's the difference between a 'key' and a 'name'? - -Class 'Mixin' wraps valueForName and valueForKey into methods. -Presumably, this is inherited by Template and thus defines the public -entry points for this module. Meaning that if I want to have a -boolean "placeholder value exists" method in Template, I'll have to -wrap hasKey or hasName. But which one? And why do both functions exist? +\section{Placeholders} +\label{placeholders} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Simple placeholders} +\label{placeholders.simple} + +Let's add a few \$placeholders to our template: + +\begin{verbatim} +>>> from Cheetah.Template import Template +>>> values = {'what': 'surreal', 'punctuation': '?'} +>>> t = Template("""\ +... Hello, $what world$punctuation +... One of Python's least-used functions is $xrange. +... """, [values]) +>>> print t +Hello, surreal world? +One of Python's least-used functions is <built-in function xrange>. + +>>> print t.generatedModuleCode() + 1 #!/usr/bin/env python + + 2 """ + 3 Autogenerated by CHEETAH: The Python-Powered Template Engine + 4 CHEETAH VERSION: 0.9.12 + 5 Generation time: Sun Apr 21 00:53:01 2002 + 6 """ + + 7 __CHEETAH_genTime__ = 'Sun Apr 21 00:53:01 2002' + 8 __CHEETAH_version__ = '0.9.12' + + 9 ################################################## + 10 ## DEPENDENCIES + + 11 import sys + 12 import os + 13 import os.path + 14 from os.path import getmtime, exists + 15 import time + 16 import types + 17 from Cheetah.Template import Template + 18 from Cheetah.DummyTransaction import DummyTransaction + 19 from Cheetah.NameMapper import NotFound, valueForName, + valueFromSearchList + 20 import Cheetah.Filters as Filters + 21 import Cheetah.ErrorCatchers as ErrorCatchers + + 22 ################################################## + 23 ## MODULE CONSTANTS + + 24 try: + 25 True, False + 26 except NameError: + 27 True, False = (1==1), (1==0) + + 28 ################################################## + 29 ## CLASSES + + 30 class GenTemplate(Template): + 31 """ + 32 + 33 Autogenerated by CHEETAH: The Python-Powered Template Engine + 34 """ + + 35 ################################################## + 36 ## GENERATED METHODS + + + 37 def __init__(self, *args, **KWs): + 38 """ + 39 + 40 """ + + 41 Template.__init__(self, *args, **KWs) + + 42 def respond(self, + 43 trans=None, + 44 dummyTrans=False, + 45 VFS=valueFromSearchList, + 46 VFN=valueForName, + 47 getmtime=getmtime, + 48 currentTime=time.time): + + + 49 """ + 50 This is the main method generated by Cheetah + 51 """ + + 52 if not trans: + 53 trans = DummyTransaction() + 54 dummyTrans = True + 55 write = trans.response().write + 56 SL = self._searchList + 57 filter = self._currentFilter + 58 globalSetVars = self._globalSetVars + 59 + 60 ######################################## + 61 ## START - generated method body + 62 + 63 write('Hello, ') + 64 write(filter(VFS(SL,"what",1))) # generated from '$what' at + # line 1, col 8. + 65 write(' world') + 66 write(filter(VFS(SL,"punctuation",1))) # generated from + # '$punctuation' at line 1, col 19. + 67 write("\nOne of Python's least-used methods is ") + 68 write(filter(xrange)) # generated from '$xrange' at line 2, + # col 39. + 69 write('.\n') + 70 + 71 ######################################## + 72 ## END - generated method body + 73 + 74 if dummyTrans: + 75 return trans.response().getvalue() + 76 else: + 77 return "" + 78 + 79 ################################################## + 80 ## GENERATED ATTRIBUTES + + + 81 __str__ = respond + + 82 _mainCheetahMethod_for_GenTemplate= 'respond' + + + 83 # CHEETAH was developed by Tavis Rudd, Chuck Esterbrook, Ian Bicking + # and Mike Orr; + 84 # with code, advice and input from many other volunteers. + 85 # For more information visit http://www.CheetahTemplate.org + + 86 ################################################## + 87 ## if run from command line: + 88 if __name__ == '__main__': + 89 GenTemplate().runAsMainProgram() + +\end{verbatim} + +(Again, I have added line numbers and split the lines as in the previous +chapter.) + +This generated template module is different from the previous one in several +trivial respects and one important respect. Trivially, +\code{.\_filePath} and \code{.\_fileMtime} are not updated in +\code{.\_\_init\_\_}, so they inherit the value \code{None} from +\code{Template}. Also, that if-stanza in \code{.respond} that recompiles the +template if the source file changes is missing -- because there is no source +file. So this module is several lines shorter than the other one. + +But the important way this module is different is that instead of the one +\code{write} call outputting a string literal, this module has a series of +\code{write} calls (lines 63-69) outputting successive chunks of the +template. Regular text has been translated into a string literal, and +placeholders into function calls. Every placeholder is wrapped inside a +\code{filter} call to apply the current output filter. (The default +output filter converts all objects to strings, and \code{None} to \code{""}.) + +Placeholders referring to a Python builtin like \code{xrange} (line 68) +generate a bare variable name. Placeholders to be looked up in the searchList +have a nested function call; e.g., +\begin{verbatim} +write(filter(VFS(SL,"what",1))) # generated from '$what' at line 1, col 8. +\end{verbatim} +\code{VFS}, remember, is a function imported from \code{Cheetah.NameMapper} +that looks up a value in a searchList. So we pass it the searchList, the +name to look up, and a boolean (1) indicating we want autocalling. (It's +\code{1} rather than \code{True} because it's generated from an +\code{and} expression, and that's what Python 2.2 outputs for true \code{and} +expressions.) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Complex placeholders} +\label{placeholders.complex} + +Placeholders can get far more complicated than that. This example shows what +kind of code the various NameMapper features produce. The formulas are +taken from Cheetah's test suite, in the +\code{Cheetah.Tests.SyntaxAndOutput.Placeholders} class. + +\begin{verbatim} +1 placeholder: $aStr +2 placeholders: $aStr $anInt +2 placeholders, back-to-back: $aStr$anInt +1 placeholder enclosed in {}: ${aStr} +1 escaped placeholder: \$var +func placeholder - with (): $aFunc() +func placeholder - with (int): $aFunc(1234) +func placeholder - with (string): $aFunc('aoeu') +func placeholder - with ('''\nstring'\n'''): $aFunc('''\naoeu'\n''') +func placeholder - with (string*int): $aFunc('aoeu'*2) +func placeholder - with (int*float): $aFunc(2*2.0) +Python builtin values: $None $True $False +func placeholder - with ($arg=float): $aFunc($arg=4.0) +deeply nested argstring: $aFunc( $arg = $aMeth( $arg = $aFunc( 1 ) ) ): +function with None: $aFunc(None) +autocalling: $aFunc! $aFunc(). +nested autocalling: $aFunc($aFunc). +list subscription: $aList[0] +list slicing: $aList[:2] +list slicing and subcription combined: $aList[:2][0] +dict - NameMapper style: $aDict.one +dict - Python style: $aDict['one'] +dict combined with autocalled string method: $aDict.one.upper +dict combined with string method: $aDict.one.upper() +nested dict - NameMapper style: $aDict.nestedDict.two +nested dict - Python style: $aDict['nestedDict']['two'] +nested dict - alternating style: $aDict['nestedDict'].two +nested dict - NameMapper style + method: $aDict.nestedDict.two.upper +nested dict - alternating style + method: $aDict['nestedDict'].two.upper +nested dict - NameMapper style + method + slice: $aDict.nestedDict.two.upper[:4] +nested dict - Python style, variable key: $aDict[$anObj.meth('nestedDict')].two +object method: $anObj.meth1 +object method + complex slice: $anObj.meth1[0: ((4/4*2)*2)/$anObj.meth1(2) ] +very complex slice: $( anObj.meth1[0: ((4/4*2)*2)/$anObj.meth1(2) ] ) +\end{verbatim} + +We'll need a big program to set up the placeholder values. Here it is: + +\begin{verbatim} +#!/usr/bin/env python +from ComplexExample import ComplexExample + +try: # Python >= 2.2.1 + True, False +except NameError: # Older Python + True, False = (1==1), (1==0) + +class DummyClass: + _called = False + def __str__(self): + return 'object' + + def meth(self, arg="arff"): + return str(arg) + + def meth1(self, arg="doo"): + return arg + + def meth2(self, arg1="a1", arg2="a2"): + return str(arg1) + str(arg2) + + def callIt(self, arg=1234): + self._called = True + self._callArg = arg + +def dummyFunc(arg="Scooby"): + return arg + +defaultTestNameSpace = { + 'aStr':'blarg', + 'anInt':1, + 'aFloat':1.5, + 'aList': ['item0','item1','item2'], + 'aDict': {'one':'item1', + 'two':'item2', + 'nestedDict':{1:'nestedItem1', + 'two':'nestedItem2' + }, + 'nestedFunc':dummyFunc, + }, + 'aFunc': dummyFunc, + 'anObj': DummyClass(), + 'aMeth': DummyClass().meth1, +} + +print ComplexExample( searchList=[defaultTestNameSpace] ) +\end{verbatim} + +Here's the output: + +\begin{verbatim} +1 placeholder: blarg +2 placeholders: blarg 1 +2 placeholders, back-to-back: blarg1 +1 placeholder enclosed in {}: blarg +1 escaped placeholder: $var +func placeholder - with (): Scooby +func placeholder - with (int): 1234 +func placeholder - with (string): aoeu +func placeholder - with ('''\nstring'\n'''): +aoeu' + +func placeholder - with (string*int): aoeuaoeu +func placeholder - with (int*float): 4.0 +Python builtin values: 1 0 +func placeholder - with ($arg=float): 4.0 +deeply nested argstring: 1: +function with None: +autocalling: Scooby! Scooby. +nested autocalling: Scooby. +list subscription: item0 +list slicing: ['item0', 'item1'] +list slicing and subcription combined: item0 +dict - NameMapper style: item1 +dict - Python style: item1 +dict combined with autocalled string method: ITEM1 +dict combined with string method: ITEM1 +nested dict - NameMapper style: nestedItem2 +nested dict - Python style: nestedItem2 +nested dict - alternating style: nestedItem2 +nested dict - NameMapper style + method: NESTEDITEM2 +nested dict - alternating style + method: NESTEDITEM2 +nested dict - NameMapper style + method + slice: NEST +nested dict - Python style, variable key: nestedItem2 +object method: doo +object method + complex slice: do +very complex slice: do + +\end{verbatim} + +And here -- tada! -- is the generated module. +To save space, I've included only the lines containing the \code{write} calls. +The rest of the module is the same as in the first example, chapter +\ref{pyModules.example}. I've truncated some of the lines to make them fit on +the page. + +\begin{verbatim} + 1 write('1 placeholder: ') + 2 write(filter(VFS(SL,"aStr",1))) # generated from '$aStr' at line 1, col 16. + 3 write('\n2 placeholders: ') + 4 write(filter(VFS(SL,"aStr",1))) # generated from '$aStr' at line 2, col 17. + 5 write(' ') + 6 write(filter(VFS(SL,"anInt",1))) + # generated from '$anInt' at line 2, col 23. + 7 write('\n2 placeholders, back-to-back: ') + 8 write(filter(VFS(SL,"aStr",1))) # generated from '$aStr' at line 3, col 31. + 9 write(filter(VFS(SL,"anInt",1))) + # generated from '$anInt' at line 3, col 36. +10 write('\n1 placeholder enclosed in {}: ') +11 write(filter(VFS(SL,"aStr",1))) # generated from '${aStr}' at line 4, + # col 31. +12 write('\n1 escaped placeholder: $var\nfunc placeholder - with (): ') +13 write(filter(VFS(SL,"aFunc",0)())) # generated from '$aFunc()' at line 6, + # col 29. +14 write('\nfunc placeholder - with (int): ') +15 write(filter(VFS(SL,"aFunc",0)(1234))) # generated from '$aFunc(1234)' at + # line 7, col 32. +16 write('\nfunc placeholder - with (string): ') +17 write(filter(VFS(SL,"aFunc",0)('aoeu'))) # generated from "$aFunc('aoeu')" + # at line 8, col 35. +18 write("\nfunc placeholder - with ('''\\nstring'\\n'''): ") +19 write(filter(VFS(SL,"aFunc",0)('''\naoeu'\n'''))) # generated from + # "$aFunc('''\\naoeu'\\n''')" at line 9, col 46. +20 write('\nfunc placeholder - with (string*int): ') +21 write(filter(VFS(SL,"aFunc",0)('aoeu'*2))) # generated from + # "$aFunc('aoeu'*2)" at line 10, col 39. +22 write('\nfunc placeholder - with (int*float): ') +23 write(filter(VFS(SL,"aFunc",0)(2*2.0))) # generated from '$aFunc(2*2.0)' + # at line 11, col 38. +24 write('\nPython builtin values: ') +25 write(filter(None)) # generated from '$None' at line 12, col 24. +26 write(' ') +27 write(filter(True)) # generated from '$True' at line 12, col 30. +28 write(' ') +29 write(filter(False)) # generated from '$False' at line 12, col 36. +30 write('\nfunc placeholder - with ($arg=float): ') +31 write(filter(VFS(SL,"aFunc",0)(arg=4.0))) # generated from + # '$aFunc($arg=4.0)' at line 13, col 40. +32 write('\ndeeply nested argstring: ') +33 write(filter(VFS(SL,"aFunc",0)( + arg = VFS(SL,"aMeth",0)( arg = VFS(SL,"aFunc",0)( 1 ) ) ))) + # generated from '$aFunc( $arg = $aMeth( $arg = $aFunc( 1 ) ) )' + # at line 14, col 26. +34 write(':\nfunction with None: ') +35 write(filter(VFS(SL,"aFunc",0)(None))) # generated from '$aFunc(None)' at + # line 15, col 21. +36 write('\nautocalling: ') +37 write(filter(VFS(SL,"aFunc",1))) # generated from '$aFunc' at line 16, + # col 14. +38 write('! ') +39 write(filter(VFS(SL,"aFunc",0)())) # generated from '$aFunc()' at line 16, + # col 22. +40 write('.\nnested autocalling: ') +41 write(filter(VFS(SL,"aFunc",0)(VFS(SL,"aFunc",1)))) # generated from + # '$aFunc($aFunc)' at line 17, col 21. +42 write('.\nlist subscription: ') +43 write(filter(VFS(SL,"aList",1)[0])) # generated from '$aList[0]' at line + # 18, col 20. +44 write('\nlist slicing: ') +45 write(filter(VFS(SL,"aList",1)[:2])) # generated from '$aList[:2]' at + # line 19, col 15. +46 write('\nlist slicing and subcription combined: ') +47 write(filter(VFS(SL,"aList",1)[:2][0])) # generated from '$aList[:2][0]' + # at line 20, col 40. +48 write('\ndict - NameMapper style: ') +49 write(filter(VFS(SL,"aDict.one",1))) # generated from '$aDict.one' at line + # 21, col 26. +50 write('\ndict - Python style: ') +51 write(filter(VFS(SL,"aDict",1)['one'])) # generated from "$aDict['one']" + # at line 22, col 22. +52 write('\ndict combined with autocalled string method: ') +53 write(filter(VFS(SL,"aDict.one.upper",1))) # generated from + # '$aDict.one.upper' at line 23, col 46. +54 write('\ndict combined with string method: ') +55 write(filter(VFN(VFS(SL,"aDict.one",1),"upper",0)())) # generated from + # '$aDict.one.upper()' at line 24, col 35. +56 write('\nnested dict - NameMapper style: ') +57 write(filter(VFS(SL,"aDict.nestedDict.two",1))) # generated from + # '$aDict.nestedDict.two' at line 25, col 33. +58 write('\nnested dict - Python style: ') +59 write(filter(VFS(SL,"aDict",1)['nestedDict']['two'])) # generated from + # "$aDict['nestedDict']['two']" at line 26, col 29. +60 write('\nnested dict - alternating style: ') +61 write(filter(VFN(VFS(SL,"aDict",1)['nestedDict'],"two",1))) # generated + # from "$aDict['nestedDict'].two" at line 27, col 34. +62 write('\nnested dict - NameMapper style + method: ') +63 write(filter(VFS(SL,"aDict.nestedDict.two.upper",1))) # generated from + # '$aDict.nestedDict.two.upper' at line 28, col 42. +64 write('\nnested dict - alternating style + method: ') +65 write(filter(VFN(VFS(SL,"aDict",1)['nestedDict'],"two.upper",1))) + # generated from "$aDict['nestedDict'].two.upper" at line 29, col 43. +66 write('\nnested dict - NameMapper style + method + slice: ') +67 write(filter(VFN(VFS(SL,"aDict.nestedDict.two",1),"upper",1)[:4])) + # generated from '$aDict.nestedDict.two.upper[:4]' at line 30, col 50. +68 write('\nnested dict - Python style, variable key: ') +69 write(filter(VFN(VFS(SL,"aDict",1) + [VFN(VFS(SL,"anObj",1),"meth",0)('nestedDict')],"two",1))) + # generated from "$aDict[$anObj.meth('nestedDict')].two" at line 31, + # col 43. +70 write('\nobject method: ') +71 write(filter(VFS(SL,"anObj.meth1",1))) # generated from '$anObj.meth1' at + # line 32, col 16. +72 write('\nobject method + complex slice: ') +73 write(filter(VFN(VFS(SL,"anObj",1),"meth1",1) + [0: ((4/4*2)*2)/VFN(VFS(SL,"anObj",1),"meth1",0)(2) ])) + # generated from '$anObj.meth1[0: ((4/4*2)*2)/$anObj.meth1(2) ]' + # at line 33, col 32. +74 write('\nvery complex slice: ') +75 write(filter(VFN(VFS(SL,"anObj",1),"meth1",1) + [0: ((4/4*2)*2)/VFN(VFS(SL,"anObj",1),"meth1",0)(2) ] )) + # generated from '$( anObj.meth1[0: ((4/4*2)*2)/$anObj.meth1(2) ] )' + # at line 34, col 21. +76 write('\n') +\end{verbatim} + +For each placeholder lookup, the the innermost level of nesting is a \code{VFS} +call, which looks up the first (leftmost) placeholder component in the +searchList. This is wrapped by zero or more \code{VFN} calls, which perform +Universal Dotted Notation lookup on the next dotted component of the +placeholder, looking for an attribute or key by that name within the previous +object (not in the searchList). Autocalling is performed by \code{VFS} and +\code{VFN}: that's the reason for their third argument. + +Explicit function/method arguments, subscripts and keys (which +are all expressions) are left unchanged, besides expanding any embedded +\$placeholders in them. This means they must result in valid Python +expressions, following the standard Python quoting rules. + +Built-in Python values (\code{None}, \code{True} and \code{False}) are +converted to \code{filter(None)}, etc. They use normal Python variable +lookup rather than \code{VFS}. (Cheetah emulates \code{True} and \code{False} +using global variables for Python < 2.2.1, when they weren't builtins yet.) + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/pyModules.tex b/docs/devel_guide_src/pyModules.tex new file mode 100644 index 0000000..1a91b4c --- /dev/null +++ b/docs/devel_guide_src/pyModules.tex @@ -0,0 +1,245 @@ +\section{.py Template Modules} +\label{pyModules} + +The next few chapters examine the structure of a .py template module and how +the various directives and placeholder expressions effect it. This will give +us a behavioral understanding of the Cheetah compiler -- what it does. We'll +present the first generated module in its entirety, then show describe each +placeholder and directive in terms of the changes it creates in the module. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{An example} +\label{pyModules.example} + +Our first template, following a long noble tradition in computer tutorials, +produces a familiar, friendly greeting. Here's the template: + +\begin{verbatim} +Hello, world! +\end{verbatim} + +... here's the output: + +\begin{verbatim} +Hello, world! +\end{verbatim} + +... and here's the .py template module cheetah-compile produced, with line +numbers added: + +% @@MO: Is it possible to print the line numbers gray instead of black? + +\begin{verbatim} + 1 #!/usr/bin/env python + + 2 """ + 3 Autogenerated by CHEETAH: The Python-Powered Template Engine + 4 CHEETAH VERSION: 0.9.12 + 5 Generation time: Sat Apr 20 14:27:47 2002 + 6 Source file: x.tmpl + 7 Source file last modified: Wed Apr 17 22:10:59 2002 + 8 """ + + 9 __CHEETAH_genTime__ = 'Sat Apr 20 14:27:47 2002' + 10 __CHEETAH_src__ = 'x.tmpl' + 11 __CHEETAH_version__ = '0.9.12' + + 12 ################################################## + 13 ## DEPENDENCIES + + 14 import sys + 15 import os + 16 import os.path + 17 from os.path import getmtime, exists + 18 import time + 19 import types + 20 from Cheetah.Template import Template + 21 from Cheetah.DummyTransaction import DummyTransaction + 22 from Cheetah.NameMapper import NotFound, valueForName, + valueFromSearchList + 23 import Cheetah.Filters as Filters + 24 import Cheetah.ErrorCatchers as ErrorCatchers + + 25 ################################################## + 26 ## MODULE CONSTANTS + + 27 try: + 28 True, False + 29 except NameError: + 30 True, False = (1==1), (1==0) + + 31 ################################################## + 32 ## CLASSES + + 33 class x(Template): + 34 """ + 35 + 36 Autogenerated by CHEETAH: The Python-Powered Template Engine + 37 """ + + 38 ################################################## + 39 ## GENERATED METHODS + + + 40 def __init__(self, *args, **KWs): + 41 """ + 42 + 43 """ + + 44 Template.__init__(self, *args, **KWs) + 45 self._filePath = 'x.tmpl' + 46 self._fileMtime = 1019106659 + + 47 def respond(self, + 48 trans=None, + 49 dummyTrans=False, + 50 VFS=valueFromSearchList, + 51 VFN=valueForName, + 52 getmtime=getmtime, + 53 currentTime=time.time): + + + 54 """ + 55 This is the main method generated by Cheetah + 56 """ + + 57 if not trans: + 58 trans = DummyTransaction() + 59 dummyTrans = True + 60 write = trans.response().write + 61 SL = self._searchList + 62 filter = self._currentFilter + 63 globalSetVars = self._globalSetVars + 64 + 65 ######################################## + 66 ## START - generated method body + 67 + 68 if exists(self._filePath) and getmtime(self._filePath) > \ + self._fileMtime: + 69 self.compile(file=self._filePath) + 70 write(getattr(self, self._mainCheetahMethod_for_x) + (trans=trans)) + 71 if dummyTrans: + 72 return trans.response().getvalue() + 73 else: + 74 return "" + 75 write('Hello, world!\n') + 76 + 77 ######################################## + 78 ## END - generated method body + 79 + 80 if dummyTrans: + 81 return trans.response().getvalue() + 82 else: + 83 return "" + 84 + 85 ################################################## + 86 ## GENERATED ATTRIBUTES + + + 87 __str__ = respond + + 88 _mainCheetahMethod_for_x= 'respond' + + + 89 # CHEETAH was developed by Tavis Rudd, Chuck Esterbrook, Ian Bicking + # and Mike Orr; + 90 # with code, advice and input from many other volunteers. + 91 # For more information visit http://www.CheetahTemplate.org + + 92 ################################################## + 93 ## if run from command line: + 94 if __name__ == '__main__': + 95 x().runAsMainProgram() + +\end{verbatim} + +(I added the line numbers for this Guide, and split a few lines to fit the +page width. The continuation lines don't have line numbers, and I added +indentation, backslashes and '\#' as necessary to make the result a valid +Python program.) + +The Cheetah version used was a CVS version between 0.9.12 and 0.9.13. The +result may be slightly different in your version. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{A walk through the example} +\label{pyModules.walk} + +Lines 20-24 are the Cheetah-specific imports. Line 33 introduces our generated +class, \code{x}, a subclass of \code{Template}. It's called x because the +source file was x.tmpl. + +Lines 40-46 are the \code{.\_\_init\_\_} method called when the template is +instantiated or used as a Webware servlet, or when the module is run as a +standalone program. We can see it calling its superclass constructor and +setting \code{.\_filePath} and \code{.\_fileMtime} to the filename and +modification time (in Unix ticks) of the source .tmpl file. + +Lines 47-84 are the main method \code{.respond}, the one that fills the +template. Normally you call it without arguments, but Webware calls it with a +Webware \code{Transaction} object representing the current request. Lines +57-59 set up the \code{trans} variable. If a real or dummy transaction is +passed in, the method uses it. Otherwise (if the \code{trans} argument is +\code{None}), the method creates a \code{DummyTransaction} instance. +\code{dummyTrans} is a flag that just tells whether a dummy transaction is in +effect; it'll be used at the end of the method. + +The other four \code{.respond} arguments aren't anything you'd ever want to +pass in; they exist solely to speed up access to these frequently-used +global functions. This is a standard Python trick described in question 4.7 +of the Python FAQ (\url{http://www.python.org/cgi-bin/faqw.py}). +\code{VFS} and \code{VFN} are the functions that give your template the +benefits of NameMapper lookup, such as the ability to use the searchList. + +Line 60 initializes the \code{write} variable. This important variable is +discussed below. + +Lines 60-63 initialize a few more local variables. \code{SL} is the +searchList. \code{filter} is the current output filter. \code{globalSetVars} +are the variables that have been defined with \code{\#set global}. + +The comments at lines 65 and 78 delimit the start and end of the code that +varies with each template. The code outside this region is identical in all +template modules. That's not quite true -- \code{\#import} for instance +generates additional \code{import} statements at the top of the module -- +but it's true enough for the most part. + +Lines 68-74 exist only if the template source was a named file rather than +a string or file object. The stanza recompiles the template if the source +file has changed. Lines 70-74 seem to be redundant with 75-83: both +fill the template and send the output. The reason the first set of lines +exists is because the second set may become invalid when the template is +recompiled. (This is for \em{re}\ compilation only. The initial compilation +happened in the \code{.\_\_init\_\_} method if the template wasn't +precompiled.) + +Line 75 is the most interesting line in this module. It's a direct +translation of what we put in the template definition, ``Hello, world!'' Here +the content is a single string literal. \code{write} looks like an ordinary +function call, but remember that line 60 made it an alias to +\code{trans.response().write}, a method in the transaction. The next few +chapters describe how the different placeholders and directives influence this +portion of the generated class. + +Lines 80-83 finish the template filling. If \code{trans} is a real Webware +transaction, \code{write} has already sent the output to Webware for handling, +so we return \code{""}. If \code{trans} is a dummy transaction, +\code{write} has been accumulating the output in a Python \code{StringIO} +object rather than sending it anywhere, so we have to return it. + +Line 83 is the end of the \code{.respond} method. + +Line 87 makes code{.\_\_str\_\_} an alias for the main method, so that you +can \code{print} it or apply \code{str} to it and it will fill the template. +Line 88 gives the name of the main method, because sometimes it's not +\code{.respond}. + +Lines 94-95 allow the module to be run directly as a script. Essentially, +they process the command-line arguments and them make the template fill +itself. + + +% Local Variables: +% TeX-master: "devel_guide" +% End: diff --git a/docs/devel_guide_src/template.tex b/docs/devel_guide_src/template.tex new file mode 100644 index 0000000..0aeb76e --- /dev/null +++ b/docs/devel_guide_src/template.tex @@ -0,0 +1,11 @@ +\section{Template} +\label{template} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{} +\label{} + +% Local Variables: +% TeX-master: "devel_guide" +% End: |