diff options
author | tavis_rudd <tavis_rudd> | 2001-06-13 03:50:36 +0000 |
---|---|---|
committer | tavis_rudd <tavis_rudd> | 2001-06-13 03:50:36 +0000 |
commit | 32567d843c4f6be31453904b60e7863b9e889d25 (patch) | |
tree | 21d5d8f60f7b3226d517c6721f355620edb19a12 | |
download | python-cheetah-32567d843c4f6be31453904b60e7863b9e889d25.tar.gz |
Initial revision
99 files changed, 11082 insertions, 0 deletions
@@ -0,0 +1,82 @@ +Please initial your changes +================================================================================ +0.9.7 (not released yet) + - moved some stuff from __init__.py into CHANGES and TODO + - added a new command 'sdist_docs' to setup.py which rebuilds the docs + when making a source distribution + - changed the name of the ./Cheetah dir to ./src + +0.9.6 (June 12, 2001) + - fixed a bug in NameMapper that was preventing 'obj.__class__.__name__' from mapping (TR) + +0.9.5 (June 10, 2001) + - implemented the #cache directive - see the mailing list (TR) + - reworked the handling of cached $placeholders and set $var to mean NO_CACHE, + $*var to mean STATIC_CACHE, and $*15*var to mean TIMED_REFRESH_CACHE (TR) + - renamed Template._getValueForName as Template.mapName (TR) + +0.9.4 (June 9, 2001) + - created a SettingsManager base class to handle settings for the Template class (TR) + - moved the HTML docs icons into the same dir as the HTML (TR) + +0.9.3 + - updated the User's Guide Makefile. Builds HTML, PDF, and PS in the ./docs dir now. (TR) + - changed the refs to 'Tavis Rudd' in the docs to 'The Cheetah Development Team' (TR) + - added a few bits to the docs (TR) + - did some internal renaming so 'nameMapperTags' are referred as 'placeholderTags' (TR) + - added the #slurp directive (TR) + +0.9.2 + - got the PSP plugin working again. It still need test cases. (TR) + +0.9.1 + - Changed the name of the package to 'Cheetah' from TemplateServer (TR) + - Changed the name of the Server module and its TemplateServer class to 'Template' (TR) + - Changed the name of the 'TScompile' script to 'cheetah-compile' (TR) + - updated the docs (TR) + +0.9.0 + - changed the names and behaviour of the #parse and #include directives (TR) + see the docs for more. (TR) + - changed #verbatim to #raw (TR) + - fixed a bug in Tests.py that caused an error on the first run. (TR) + - more docs (TR + MO) + ! all tests pass with Python 2.1 and 2.0 (TR) + +0.8.4 + - changed the #directive end tags to #end if instead of #/if and #end (TR) + macro instead of #/macro (TR) + - more work on the User's Guide (TR) + - fixed a bug in TScompile (TR) + +0.8.3 + - fixed a problem with the regexs that caused $vars and #directives at the (TR) + very beginning of the template string not to match in Python 2.0 (TR) + - removed some Test cases that made invalid assumptions about the order (TR) + of items in dictionaries. (TR) + +0.8.2 + - finished half of the User's Guide (TR) + - fixed several small bugs (TR) + - added the #comment directive and removed the old <# multiline comment tag #> (TR) + - changed the explicit directive closure to /# from ;# (TR) + + +0.7.6 + - several small bug fixes (TR) + - reimplemented the #block directive to avoid maximum recursion depth errors (TR) + with large blocks. (TR) + - created many new test cases in the regression testing suite (TR) + - added an example site to the examples/ directory (TR) + - started the User's Guide (TR) + +0.7.5 + - implemented the command-line compiler (TR) + +0.7.3-4 + - implemented the regression testing suite (TR) + - fixed a number of small bugs (TR) + +0.7.2 + - implemented the #longMacro directive (TR) +
\ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..620b975 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,6 @@ +include *.py TODO CHANGES README examples third_party_packages docs +recursive-include bin * +recursive-include docs * +recursive-include PlateKit *.py +recursive-include examples * +recursive-include third_party_packages *
\ No newline at end of file @@ -0,0 +1,54 @@ +Cheetah TODO list + +When a task is completed please change its - to an ! and move it to the bottom +of the list. + +Specification +================================================================================ +- firm up the list of features/syntax that are not subject to change +- firm up the NamedValueAccess syntax (along with new test cases) + +Implementation +================================================================================ +- design and implement the new parser we've been discussing +- reimplement the #include directive so it monitors for changes to + the included files +- redesign and implement the Servlet Factory for Webware so the #extend directive + will work with .tmpl files as well as .py files +- make sure that all the #directives that have start and end tags are + implemented in such a way that they won't cause 'maximum recursion' limit + errors if their content block is long. Simple regexes won't cut it in these + cases. + +Test Suite +================================================================================ +- write script that will run the test cases with both Python 2.0 and 2.1 +- independent suite of test cases for NameMapper +- test cases for the PSP plugin +- test cases for the SkeletonPage framework + +Documentation +================================================================================ +- add the list of what's stable and what's not to the 'How Mature is Cheetah' + section +- finish all the empty sections +- flesh out the 'Quick Tutorial' in the 'Getting Started' section +- add the glossary +- add an 'Information for Developers' Section +- add examples to the Examples section + +Website +================================================================================ +- automate the documentation update (including regenerating the learn.html page) +- add links to the CVS + some instructions +- See if we can get WebKit working on Sourceforge... + +Examples +================================================================================ +- create some non-html code generation examples + +Packaging +================================================================================ +- Consider moving SettingsManager.py and NameMapper.py into the + third_party_packages dir and making setup_all.py the main install script +! make 'setup.py sdist_docs' generate the update docs by executing the Makefile diff --git a/bin/cheetah-compile b/bin/cheetah-compile new file mode 100644 index 0000000..164ce49 --- /dev/null +++ b/bin/cheetah-compile @@ -0,0 +1,5 @@ +#! /usr/bin/env python + +from Cheetah.Compiler import MainProgram + +MainProgram().run()
\ No newline at end of file diff --git a/docs/src/Makefile b/docs/src/Makefile new file mode 100644 index 0000000..173aef0 --- /dev/null +++ b/docs/src/Makefile @@ -0,0 +1,35 @@ +DOCNAME=users_guide +PYTHONSRC=/home/tavis/tmp/Python-2.1 +MKHOWTO=$(PYTHONSRC)/Doc/tools/mkhowto +MAIN_TEX_FILE= users_guide.tex + +all: pdf ps html text htmlMultiPage + +pdf: + $(MKHOWTO) --pdf $(MAIN_TEX_FILE) + mv $(DOCNAME).pdf ../ + +ps: + $(MKHOWTO) --ps $(MAIN_TEX_FILE) + mv $(DOCNAME).ps ../ +html: + -rm -rf $(DOCNAME) + $(MKHOWTO) --html --split 1 --iconserver . $(MAIN_TEX_FILE) + -rm -R ../$(DOCNAME)_html + mv $(DOCNAME) ../$(DOCNAME)_html + +htmlMultiPage: + -rm -rf $(DOCNAME) + $(MKHOWTO) --html --iconserver . $(MAIN_TEX_FILE) + -rm -R ../$(DOCNAME)_html_multipage + mv $(DOCNAME) ../$(DOCNAME)_html_multipage + +text: + $(MKHOWTO) --text $(MAIN_TEX_FILE) + mv $(DOCNAME).txt ../ + +clean: + -rm -rf $(DOCNAME) + -rm *.aux *.l2h *~ + -rm -rf ../html + diff --git a/docs/src/TDL.aux b/docs/src/TDL.aux new file mode 100644 index 0000000..6f921d2 --- /dev/null +++ b/docs/src/TDL.aux @@ -0,0 +1,42 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {4}The Template Definition Language}{8}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.1}Placeholder tags}{8}} +\@writefile{toc}{\contentsline {subsubsection}{Rules for placeholder names}{8}} +\@writefile{toc}{\contentsline {subsubsection}{The searchList}{9}} +\@writefile{toc}{\contentsline {subsubsection}{Placeholder values}{9}} +\@writefile{toc}{\contentsline {subsubsection}{Placeholders with no value defined}{10}} +\@writefile{toc}{\contentsline {subsubsection}{Dotted notation}{10}} +\@writefile{toc}{\contentsline {subsubsection}{Caching}{10}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}Directive tags}{11}} +\@writefile{toc}{\contentsline {subsubsection}{Escaping directives}{11}} +\@writefile{toc}{\contentsline {subsubsection}{Tag closures: explicit and implicit}{11}} +\@writefile{toc}{\contentsline {subsubsection}{Whitespace handling}{11}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.3}Comment directives}{11}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.4}\#raw directives}{12}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.5}\#include directives}{12}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.6}\#cache directives}{13}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.7}Display logic directives}{13}} +\@writefile{toc}{\contentsline {subsubsection}{Conditional blocks}{13}} +\@writefile{toc}{\contentsline {subsubsection}{For loops}{13}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.8}\#block directives}{13}} +\@writefile{toc}{\contentsline {subsubsection}{\#redefine directives}{13}} +\@setckpt{TDL}{ +\setcounter{page}{14} +\setcounter{equation}{0} +\setcounter{enumi}{2} +\setcounter{enumii}{6} +\setcounter{enumiii}{0} +\setcounter{enumiv}{0} +\setcounter{footnote}{1} +\setcounter{mpfootnote}{0} +\setcounter{part}{0} +\setcounter{section}{4} +\setcounter{subsection}{8} +\setcounter{subsubsection}{0} +\setcounter{paragraph}{0} +\setcounter{subparagraph}{0} +\setcounter{figure}{0} +\setcounter{table}{0} +\setcounter{LT@tables}{0} +\setcounter{LT@chunks}{0} +} diff --git a/docs/src/TDL.tex b/docs/src/TDL.tex new file mode 100644 index 0000000..2401d3d --- /dev/null +++ b/docs/src/TDL.tex @@ -0,0 +1,348 @@ +\section{The Template Definition Language} + +{\bf Template definitions} are text strings, or files, that have been marked up +with Cheetah tags for special processing. Cheetah has 2 types of tags: + +\begin{enumerate} +\item {\bf placeholders}: for marking areas of the template that should be + replaced with something. + + Placeholders begin with a dollar sign (\code{\$varName}). + +\item {\bf directives}: for everything else: + \begin{enumerate} % level 2 + \item {\bf raw text} for marking verbatim blocks should not be parsed for + Cheetah tags. + \item {\bf comments} that should not appear in the output + \item {\bf includes} to include external text. The text can be included + verbatim or with parsing for Cheetah tags. + \item {\bf display logic} such as {\bf conditional blocks} (if-blocks) and + {\bf for loops} + \item {\bf blocks}, which are named sections of a template that can be + redefined (overridden) in a subclass or by template users + \item etc. + \end{enumerate} % level 2 + + Directives begin with a hash character (\#). + +\end{enumerate} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Placeholder tags} +Cheetah uses placeholder tags in the form \code{\$varName} to mark out +areas of the template definition that should be replaced with something. Placeholders are +equivalent to {\bf fields} on a form. Placeholders can be replaced with plain +content or variables, objects and function output from Python. + +The example below demonstrates the use of placeholders in an HTML document. + +\begin{verbatim} +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> +$contents +</BODY> +</HTML> +\end{verbatim} + +When this template is filled in, the placeholders \code{\$title} and +\code{\$content} will be replaced with the values of the variables \code{title} +and \code{content}. + +\subsubsection{Rules for placeholder names} +\begin{itemize} + +\item Cheetah ignores all dollar signs (\code{\$}) that are not followed + by a letter or an underscore. As a corollary, dollar amounts + (\code{\$2.50})are not placeholders but are instead output literally as + they should be. Cheetah also ignores any placeholder escaped by a + backslash (\code{$\backslash$\$placeholderName}). + +\item The first character of a placeholder name must be either an underscore or + a letter. Valid characters for the rest of the name are underscores, + letters, numbers and periods. These names are valid: \code{\$a}, + \code{\$_}, \code{\$var}, \code{\$_var}, \code{\$var1}, \code{\$_1var}, + \code{\$var2_}, \code{\$dict.key}, \code{\$list.item}, + \code{\$object.method}. These names are not: \code{\$1}, \code{\$var@2}, + \code{\$\^var}. + +\item names are case-sensitive. \code{\$var} does not equal \code{\$Var} + or \code{\$vAr} or \code{\$VAR}. + +\item Trailing periods are ignored. Cheetah will recognize that + placeholder name in \code{\$varName.} is \code{varName} and the period will + be left alone in the filled template output. + +\item Placeholders can also be written in the form \code{\$\{placeholderName\}}. + This is useful for cases where there is no whitespace between the + placeholder and surrounding text (\code{surrounding\$\{embeddedVar\}text}). +\end{itemize} + +\subsubsection{The searchList} +A {\bf namespace} is a Python dictionary that links names to values. Each +template definition that has been loaded into a \code{Template} objet is +associated with an ordered list of namespaces in which values for placeholder +variable names can be stored. This list is called the {\bf searchList}. + +The searchList can contain one or more namespaces. In most cases only one +namespace will be in the searchList unless you explicitly load extra ones. When +Cheetah fills in \code{\$content} in previous example it searchs +sequentially through the searchList until it finds a value for \code{\$content}. +Thus, if three namespaces are loaded and two of them contain a value for +\code{\$content}, the value for \code{content} from the namespace that is +closest to the start of the searchList will be returned. + +If you add a Python object to the searchList, its attributes and methods will be +accessible as placeholder names. For example, \code{myObject} contains +\code{myAttrib} and \code{myMethod}. If \code{myObject} is added to the +searchList, \code{\$myAttrib} and \code{\$myMethod} can be used as placeholder +names. + +The default namespace in every searchList is the \code{Template} object itself. +This means that any attributes or methods that are added to classes that inherit +from \code{Template} can be accessed in templates via \code{\$placeholders}. New +namespaces can be added to the searchList at any time using the +\code{Template.addToSearchList()} method. See section \ref{webware} for more +information on how to use namespaces and the searchList. + +\subsubsection{Placeholder values} + +Placeholder names can map to Python text strings, numbers, dictionaries, lists +(arrays), functions, objects, or even nested Cheetah templates. If the value is +not a String, Cheetah will call \code{str()} on it to obtain a string +representation that can be inserted into the template. But if the value is a +callable object (e.g., a function or a method), Cheetah will first call it and +then call \code{str()} on the result. You may include or omit the () after +callable placeholders: Cheetah will call it (without arguments) in either case. + + +\begin{verbatim} +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> + +$aString ## these names are chosen to indicate what +$aNumber ## type of value they map to. They could be +$aList ## any valid placeholder name. +$aDictionary +$aFunction +$anObject + +</BODY> +</HTML> +\end{verbatim} + +Note that you don't need to include () after \code{\$aFunction}. Cheetah +will recognize callable variables like functions and methods, call them and +interpolate their return value in the filled template output. + +You cannot pass arguments to a function or method called this way. (Actually, +you can, but this feature may be removed soon, because it introduces too much +complexity that is better done in Python outside the template.) The function or +method must either accept no arguments, or all its arguments must have default +values. Macro calls (see the Macros section) can accept arguments, but we +aren't talking about macros here. + +Templates can be nested. It is valid to embed a placeholder name that maps to +another \code{Template} object. The filled output of the nested template will be +interpolated into the top-level template's output. There is no limit on the +depth of template nesting. + +\subsubsection{Placeholders with no value defined} +If there is no value defined in the searchList for a placeholder name, +Cheetah will search for an leading underscore version of the name. For +example, if it can't find \code{\$varName} it will attempt to find +\code{\$_varName}. If that fails, Cheetah will include the placeholder +tag verbatim in the filled template output. + +This behaviour can be customized and a default value can be set for names +that are not found. See section \ref{customizing} for more details. + +\subsubsection{Dotted notation} +Placeholder names can also use {\bf dotted notation} to access entries in +dictionaries, items in lists, and the attributes and methods of objects. +Cheetah uses a consistent dotted notation syntax to access the contents +of all types of containers. This is unlike Python, C++, Java, and other +languages where dotted notation can only be used to access the attributes and +methods of objects. \footnote{Cheetah uses a Python module called + NameMapper to handle this style of dotted notation. NameMapper is distributed + as part of the Cheetah package and can be used as a stand-alone tool. + NameMapper was inspired by Chuck Esterbrook's NamedValueAccess module.} + +\begin{verbatim} +$aDictionary.keyName ## must be a valid key of the dictionary +$aList.3 ## must be a valid index (0-based like in Python) +$anObject.attributeName ## must be a valid attribute name +$anObject.method ## must be a valid method name, + ## leave off the parentheses on method() +\end{verbatim} + + +Dotted notation can be used on nested containers of any depth. +\begin{verbatim} +$dict1.dict2.dict3.keyName ## nested dictionaries +$anObject.nestedDict.keyName ## if the object contains a dictionary +\end{verbatim} + + +\subsubsection{Caching} + +By default the value of each \code{\$placeholder} is updated for each request. +If you want to statically cache the value of the \code{\$placeholder} upon +startup, add an asterisk after the \$ sign. \code{\$var} becomes \code{\$*var}. +See the section on the \code{\#cache} directive below for more information. + +If you only need to update the value of the \code{\$placeholders} at specific time intervals use +this form: \code{\$variable} becomes \code{\$*15*variable} , where 15 is the time interval in +minutes. The time interval can also be specified in fractions of a minute such +as \code{\$*0.5*variable}. + +\begin{verbatim} +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> + +$var ## dynamic - will be reinterpolated for each request +$*var2 ## static - will be interpolated only once at start-up +$*5*var3 ## timed refresh - will be updated every 5 minutes. + +</BODY> +</HTML> +\end{verbatim} + +%Note that Cheetah will automatically detect nested \code{Template} objects +%and these +%% @@finish this off + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Directive tags} +Directives tags are used for all functionality that cannot be handled with +simple placeholders and are enclosed in \code{\#} and \code{/\#}. +Cheetah does not use HTML/XML style tags because they would be hard to +distinguish from real HTML tags and would not be visible in rendered HTML when +something goes wrong. + +Some directives consist of a single tag while others have {\bf start} and +{\bf end} tags that surround a chunk of text. End tags are written in the form +\code{\#end nameOfTheDirective/\#}. + +\subsubsection{Escaping directives} +Directives can be escaped by placing a backslash ($\backslash$) before them. +Escaped directives will be printed verbatim. + +\subsubsection{Tag closures: explicit and implicit} +Directive tags can closed explicitly with \code{/\#} or implicitly with the end +of the line if you're feeling lazy. +\begin{verbatim} +#block /# +Text in the contents area of the +block directive +#end block /# +\end{verbatim} +or +\begin{verbatim} +#block +Text in the contents area of the +block directive +#end block +\end{verbatim} + +\subsubsection{Whitespace handling} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Comment directives} + +Comment directives are used to mark notes, explanations, and decorative text +that should not appear in the output. There are two forms of the comment +directive: single-line and multi-line. + +All text in a template definition that lies between 2 hash characters +(\code{\#\#}) and the end of the line is treated as a single-line comment and +will not show up in the output, unless the 2 hash characters are escaped with a +backslash. +\begin{verbatim} +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> +##==================================== a decorative comment +$contents ## an end-of-line comment +##==================================== +</BODY> +</HTML> +\end{verbatim} + +Any text between \code{\#*} and \code{*\#} will be treated as a multi-line +comment. +\begin{verbatim} +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> +#* + Here is some multiline + comment text +*# +##==================================== a decorative comment +$contents ## an end-of-line comment +##==================================== +</BODY> +</HTML> +\end{verbatim} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{\#raw directives} +Any section of a template definition that is delimeted by \code{\#raw} and +\code{\#end raw} will be printed verbatim without any parsing of +\$placeholders or other directives. This can be very useful for debugging or +writing Cheetah examples and tutorials. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{\#include directives} + +\code{\#include} directives are used to include text from outside the template +definition. The text can come from \code{\$placeholder} variables or from +external files. The example below demonstrates use with \code{\$placeholder} +variables. + +\begin{verbatim} +#include $myParseText +\end{verbatim} + +This example demonstrates its use with external files. +\begin{verbatim} +#include "includeFileName.txt" +\end{verbatim} + +By default, included text will be parsed for Cheetah tags. The keyword +{\bf raw} can be used to mark the text for verbatim inclusion without any tag +parsing. + +\begin{verbatim} +#include raw $myParseText +#include raw "includeFileName.txt" +\end{verbatim} + +\code{Template} uses its .getFileContents(fileName) method to locate the file to +be included. This method can be overriden in subclasses if you want to modify +or extend its behaviour. It is possible to implement the logic for getting +remote files such as \code{http://myserver.com/file.txt}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{\#cache directives} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Display logic directives} + +\subsubsection{Conditional blocks} + +\subsubsection{For loops} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{\#block directives} + +\subsubsection{\#redefine directives} + + diff --git a/docs/src/TemplateObjects.aux b/docs/src/TemplateObjects.aux new file mode 100644 index 0000000..d625967 --- /dev/null +++ b/docs/src/TemplateObjects.aux @@ -0,0 +1,27 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {3}Template Objects}{7}} +\newlabel{TSobjects}{{3}{7}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.1}The Template class}{7}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.2}Constructing Template objects}{7}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.3}Using Template objects}{7}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.4}Modifying Template objects}{7}} +\@setckpt{TemplateObjects}{ +\setcounter{page}{8} +\setcounter{equation}{0} +\setcounter{enumi}{4} +\setcounter{enumii}{0} +\setcounter{enumiii}{0} +\setcounter{enumiv}{0} +\setcounter{footnote}{0} +\setcounter{mpfootnote}{0} +\setcounter{part}{0} +\setcounter{section}{3} +\setcounter{subsection}{4} +\setcounter{subsubsection}{0} +\setcounter{paragraph}{0} +\setcounter{subparagraph}{0} +\setcounter{figure}{0} +\setcounter{table}{0} +\setcounter{LT@tables}{0} +\setcounter{LT@chunks}{0} +} diff --git a/docs/src/TemplateObjects.tex b/docs/src/TemplateObjects.tex new file mode 100644 index 0000000..125e04b --- /dev/null +++ b/docs/src/TemplateObjects.tex @@ -0,0 +1,22 @@ +\section{Template Objects} +\label{TSobjects} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{The Template class} +The Template class is the heart of Cheetah. It parses and compiles +Template Definitions into python code and serves the filled template output to +any client that requests it. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Constructing Template objects} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Using Template objects} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Modifying Template objects} + + + diff --git a/docs/src/customizing.aux b/docs/src/customizing.aux new file mode 100644 index 0000000..7eadb4d --- /dev/null +++ b/docs/src/customizing.aux @@ -0,0 +1,30 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {7}Customizing and extending Cheetah}{18}} +\newlabel{customizing}{{7}{18}} +\@writefile{toc}{\contentsline {subsection}{\numberline {7.1}Custom handling of unknown placeholder names}{18}} +\@writefile{toc}{\contentsline {subsection}{\numberline {7.2}Plugins}{18}} +\@writefile{toc}{\contentsline {subsubsection}{The PSP plugin}{18}} +\@writefile{toc}{\contentsline {subsection}{\numberline {7.3}Custom variable-tags}{18}} +\@writefile{toc}{\contentsline {subsection}{\numberline {7.4}Custom directives}{18}} +\@writefile{toc}{\contentsline {subsection}{\numberline {7.5}Custom error handlers}{18}} +\@writefile{toc}{\contentsline {subsection}{\numberline {7.6}Safe delegation}{18}} +\@setckpt{customizing}{ +\setcounter{page}{19} +\setcounter{equation}{0} +\setcounter{enumi}{2} +\setcounter{enumii}{6} +\setcounter{enumiii}{0} +\setcounter{enumiv}{0} +\setcounter{footnote}{1} +\setcounter{mpfootnote}{0} +\setcounter{part}{0} +\setcounter{section}{7} +\setcounter{subsection}{6} +\setcounter{subsubsection}{0} +\setcounter{paragraph}{0} +\setcounter{subparagraph}{0} +\setcounter{figure}{0} +\setcounter{table}{0} +\setcounter{LT@tables}{0} +\setcounter{LT@chunks}{0} +} diff --git a/docs/src/customizing.tex b/docs/src/customizing.tex new file mode 100644 index 0000000..d1a0298 --- /dev/null +++ b/docs/src/customizing.tex @@ -0,0 +1,22 @@ +\section{Customizing and extending Cheetah} +\label{customizing} + +\subsection{Custom handling of unknown placeholder names} +%% handlers +%% default vars + +\subsection{Plugins} +\subsubsection{The PSP plugin} + +\subsection{Custom variable-tags} + +\subsection{Custom directives} + +\subsection{Custom error handlers} + +\subsection{Safe delegation} +Safe Delegation, as provided by Zope and Allaire's Spectra, is not a core aim of +Cheetah. However, several hooks were built into Cheetah so that Safe +Delegation can be implemented at a later date. + + diff --git a/docs/src/errorMessages.tex b/docs/src/errorMessages.tex new file mode 100644 index 0000000..b628677 --- /dev/null +++ b/docs/src/errorMessages.tex @@ -0,0 +1,5 @@ +\section{Error Messages} + +\subsection{Errors during compilation} + +\subsection{Run-time errors}
\ No newline at end of file diff --git a/docs/src/examples.aux b/docs/src/examples.aux new file mode 100644 index 0000000..889674a --- /dev/null +++ b/docs/src/examples.aux @@ -0,0 +1,25 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {8}Examples}{19}} +\newlabel{examples}{{8}{19}} +\@writefile{toc}{\contentsline {subsection}{\numberline {8.1}Syntax examples}{19}} +\@writefile{toc}{\contentsline {subsection}{\numberline {8.2}Webware Examples}{19}} +\@setckpt{examples}{ +\setcounter{page}{20} +\setcounter{equation}{0} +\setcounter{enumi}{2} +\setcounter{enumii}{6} +\setcounter{enumiii}{0} +\setcounter{enumiv}{0} +\setcounter{footnote}{1} +\setcounter{mpfootnote}{0} +\setcounter{part}{0} +\setcounter{section}{8} +\setcounter{subsection}{2} +\setcounter{subsubsection}{0} +\setcounter{paragraph}{0} +\setcounter{subparagraph}{0} +\setcounter{figure}{0} +\setcounter{table}{0} +\setcounter{LT@tables}{0} +\setcounter{LT@chunks}{0} +} diff --git a/docs/src/examples.tex b/docs/src/examples.tex new file mode 100644 index 0000000..5d313b8 --- /dev/null +++ b/docs/src/examples.tex @@ -0,0 +1,26 @@ +\section{Examples} +\label{examples} + +The Cheetah distribution comes with an 'examples' directory. Browse the +files in this directory and its subdirectories for examples of how +Cheetah can be used. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Syntax examples} +Cheetah's \code{Tests} module contains a large number of test cases +that can double as examples of how the Template Definition Language works. +To view these cases go to the base directory of your Cheetah distribution +and open the file Cheetah/Tests.py in a text editor. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Webware Examples} +The 'examples' directory has a subdirectory called 'webware_examples'. It +contains example servlets that use Webware. + +A subdirectory titled 'webwareSite' contains a complete website example. This +site is my proposal for the new Webware website. The site demonstrates the +advanced Cheetah features such as the \code{\#data} and \code{\#redefine} +directives. It also demonstrates how the TScompile program can be used to +generate Webware .py servlet files from .tmpl Template Definition files. + diff --git a/docs/src/gettingStarted.aux b/docs/src/gettingStarted.aux new file mode 100644 index 0000000..cf15261 --- /dev/null +++ b/docs/src/gettingStarted.aux @@ -0,0 +1,27 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {2}Getting Started}{5}} +\newlabel{gettingStarted}{{2}{5}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Requirements}{5}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}Installation}{5}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Testing your installation}{5}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.4}Quickstart tutorial}{5}} +\@setckpt{gettingStarted}{ +\setcounter{page}{7} +\setcounter{equation}{0} +\setcounter{enumi}{4} +\setcounter{enumii}{0} +\setcounter{enumiii}{0} +\setcounter{enumiv}{0} +\setcounter{footnote}{0} +\setcounter{mpfootnote}{0} +\setcounter{part}{0} +\setcounter{section}{2} +\setcounter{subsection}{4} +\setcounter{subsubsection}{0} +\setcounter{paragraph}{0} +\setcounter{subparagraph}{0} +\setcounter{figure}{0} +\setcounter{table}{0} +\setcounter{LT@tables}{0} +\setcounter{LT@chunks}{0} +} diff --git a/docs/src/gettingStarted.tex b/docs/src/gettingStarted.tex new file mode 100644 index 0000000..c940a0d --- /dev/null +++ b/docs/src/gettingStarted.tex @@ -0,0 +1,94 @@ +\section{Getting Started} +\label{gettingStarted} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Requirements} +Cheetah requires Python release 2.0 or greater and should run on any +operating system that Python 2.0 runs on. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Installation} + +To install Cheetah for a single user: +\begin{enumerate} +\item copy the 'src' sub-directory to a directory called 'Cheetah' that is in the + user's PYTHON_PATH + +\end{enumerate} + +To install Cheetah for system-wide use: +\begin{enumerate} +\item on POSIX systems (AIX, Solaris, Linux, IRIX, etc.) become the 'root' user + and run: python ./setup.py install + +\item On non-POSIX systems, such as Windows NT, login as an administrator and + type this at the command-line: python setup.py install +\end{enumerate} + + +On POSIX systems, the system-wide installation will also install the Cheetah's +command-line compiler program, TScompile, to a system-wide executable path such as +/usr/local/bin. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Testing your installation} +You can run the test suite to insure that your installation is correct by +following these steps: +\begin{enumerate} +\item CD into the directory ./Cheetah +\item type: \code{python Tests.py} +\end{enumerate} + +If any of the tests fail please send a message to the email list with a copy of +the test output and the following details about your installation: + +\begin{enumerate} +\item your version of Cheetah +\item your version of Python +\item your operating system +\item whether you have changed anything in the Cheetah installation +\end{enumerate} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Quickstart tutorial} +This tutorial briefly introduces the basic usage of Cheetah. See the +following chapters for more detailed explanations. + +{\bf This tutorial will be fleshed out further at later date.} + +The core of Cheetah is the \code{Template} class in the \code{Cheetah.Template} +module. The following example shows how to use the \code{Template} class from an +interactive Python session. Lines prefixed with \code{>>>} and \code{...} are +user input. The remaining lines are Python output. + +\begin{verbatim} +>>> from Cheetah.Template import Template +>>> templateDef = """ +... <HTML> +... <HEAD><TITLE>$title</TITLE></HEAD> +... <BODY> +... $contents +... </BODY> +... </HTML>""" +>>> nameSpace = {'title': 'Hello World Example', 'contents': 'Hello World!'} +>>> templateObj = Template(templateDef, nameSpace) +>>> print templateObj + +<HTML> +<HEAD><TITLE>Hello World Example</TITLE></HEAD> +<BODY> +Hello World! +</BODY> +</HTML> +>>> print templateObj # templateObj can be printed as many times as you need + +<HTML> +<HEAD><TITLE>Hello World Example</TITLE></HEAD> +<BODY> +Hello World! +</BODY> +</HTML> + +\end{verbatim} + + diff --git a/docs/src/gfdl.tex b/docs/src/gfdl.tex new file mode 100644 index 0000000..a6774c2 --- /dev/null +++ b/docs/src/gfdl.tex @@ -0,0 +1,367 @@ +% gfdl.tex +% This file is a chapter. It must be included in a larger document to work +% properly. + +\section{GNU Free Documentation License} + +Version 1.1, March 2000\\ + + Copyright $\copyright$ 2000 Free Software Foundation, Inc.\\ + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\\ + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +\subsection*{Preamble} + +The purpose of this License is to make a manual, textbook, or other +written document ``free'' in the sense of freedom: to assure everyone +the effective freedom to copy and redistribute it, with or without +modifying it, either commercially or noncommercially. Secondarily, +this License preserves for the author and publisher a way to get +credit for their work, while not being considered responsible for +modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +\subsection{Applicability and Definitions} + +This License applies to any manual or other work that contains a +notice placed by the copyright holder saying it can be distributed +under the terms of this License. The ``Document'', below, refers to any +such manual or work. Any member of the public is a licensee, and is +addressed as ``you''. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (For example, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, whose contents can be viewed and edited directly and +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup has been designed to thwart or discourage +subsequent modification by readers is not Transparent. A copy that is +not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, \LaTeX~input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML designed for human modification. Opaque formats include +PostScript, PDF, proprietary formats that can be read and edited only +by proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML produced by some word processors for output +purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + + +\subsection{Verbatim Copying} + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +\subsection{Copying in Quantity} + +If you publish printed copies of the Document numbering more than 100, +and the Document's license notice requires Cover Texts, you must enclose +the copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a publicly-accessible computer-network location containing a complete +Transparent copy of the Document, free of added material, which the +general network-using public has access to download anonymously at no +charge using public-standard network protocols. If you use the latter +option, you must take reasonably prudent steps, when you begin +distribution of Opaque copies in quantity, to ensure that this +Transparent copy will remain thus accessible at the stated location +until at least one year after the last time you distribute an Opaque +copy (directly or through your agents or retailers) of that edition to +the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +\subsection{Modifications} + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +\begin{itemize} + +\item Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +\item List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has less than five). +\item State on the Title page the name of the publisher of the + Modified Version, as the publisher. +\item Preserve all the copyright notices of the Document. +\item Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +\item Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +\item Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +\item Include an unaltered copy of this License. +\item Preserve the section entitled ``History'', and its title, and add to + it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section entitled ``History'' in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +\item Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the ``History'' section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +\item In any section entitled ``Acknowledgements'' or ``Dedications'', + preserve the section's title, and preserve in the section all the + substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +\item Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +\item Delete any section entitled ``Endorsements''. Such a section + may not be included in the Modified Version. +\item Do not retitle any existing section as ``Endorsements'' + or to conflict in title with any Invariant Section. + +\end{itemize} + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties -- for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +\subsection{Combining Documents} + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections entitled ``History'' +in the various original documents, forming one section entitled +``History''; likewise combine any sections entitled ``Acknowledgements'', +and any sections entitled ``Dedications''. You must delete all sections +entitled ``Endorsements.'' + + +\subsection{Collections of Documents} + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + + +\subsection{Aggregation With Independent Works} + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, does not as a whole count as a Modified Version +of the Document, provided no compilation copyright is claimed for the +compilation. Such a compilation is called an ``aggregate'', and this +License does not apply to the other self-contained works thus compiled +with the Document, on account of their being thus compiled, if they +are not themselves derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one quarter +of the entire aggregate, the Document's Cover Texts may be placed on +covers that surround only the Document within the aggregate. +Otherwise they must appear on covers around the whole aggregate. + + +\subsection{Translation} + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License provided that you also include the +original English version of this License. In case of a disagreement +between the translation and the original English version of this +License, the original English version will prevail. + + +\subsection{Termination} + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +\subsection{Future Revisions of This Licence} + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +\url{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + +\subsection*{ADDENDUM: How to use this License for your documents} + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +\begin{quote} + + Copyright $\copyright$ YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.1 + or any later version published by the Free Software Foundation; + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + A copy of the license is included in the section entitled ``GNU + Free Documentation License''. + +\end{quote} + +If you have no Invariant Sections, write ``with no Invariant Sections'' +instead of saying which ones are invariant. If you have no +Front-Cover Texts, write ``no Front-Cover Texts'' instead of +``Front-Cover Texts being LIST''; likewise for Back-Cover Texts. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + diff --git a/docs/src/introduction.aux b/docs/src/introduction.aux new file mode 100644 index 0000000..33f4f81 --- /dev/null +++ b/docs/src/introduction.aux @@ -0,0 +1,46 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{2}} +\newlabel{intro}{{1}{2}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.1}What is Cheetah?}{2}} +\newlabel{intro.whatIs}{{1.1}{2}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.2}Why is it called Cheetah?}{3}} +\newlabel{intro.name}{{1.2}{3}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.3}Who developed Cheetah?}{3}} +\newlabel{intro.developers}{{1.3}{3}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.4}How mature is Cheetah?}{3}} +\newlabel{intro.mature}{{1.4}{3}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.5}Where can I get releases?}{3}} +\newlabel{intro.releases}{{1.5}{3}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.6}Where can I get news?}{3}} +\newlabel{intro.news}{{1.6}{3}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.7}How can I contribute?}{3}} +\newlabel{intro.contribute}{{1.7}{3}} +\@writefile{toc}{\contentsline {subsubsection}{Bug reports and patches}{4}} +\@writefile{toc}{\contentsline {subsubsection}{Example sites and tutorials}{4}} +\@writefile{toc}{\contentsline {subsubsection}{Macro libraries}{4}} +\@writefile{toc}{\contentsline {subsubsection}{Test cases}{4}} +\@writefile{toc}{\contentsline {subsubsection}{Publicity}{4}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.8}Acknowledgements}{4}} +\newlabel{intro.acknowledgments}{{1.8}{4}} +\@writefile{toc}{\contentsline {subsection}{\numberline {1.9}License}{4}} +\newlabel{intro.license}{{1.9}{4}} +\@setckpt{introduction}{ +\setcounter{page}{5} +\setcounter{equation}{0} +\setcounter{enumi}{6} +\setcounter{enumii}{0} +\setcounter{enumiii}{0} +\setcounter{enumiv}{0} +\setcounter{footnote}{0} +\setcounter{mpfootnote}{0} +\setcounter{part}{0} +\setcounter{section}{1} +\setcounter{subsection}{9} +\setcounter{subsubsection}{0} +\setcounter{paragraph}{0} +\setcounter{subparagraph}{0} +\setcounter{figure}{0} +\setcounter{table}{0} +\setcounter{LT@tables}{0} +\setcounter{LT@chunks}{0} +} diff --git a/docs/src/introduction.tex b/docs/src/introduction.tex new file mode 100644 index 0000000..8652112 --- /dev/null +++ b/docs/src/introduction.tex @@ -0,0 +1,213 @@ +\section{Introduction} +\label{intro} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{What is Cheetah?} +\label{intro.whatIs} + +Cheetah is a Python-based template engine and code-generator. It aims: + +\begin{itemize} +\item {\bf to make it easy to separate content, graphic design, and program code.} + + Program code should not pollute HTML and HTML should not pollute program + code. Nor should content pollute the structure of complex HTML designs and + vice versa. + + There should be no need for a designer to work through a programmer to + change a website's design or to use dynamic components that have already + been coded. Likewise, content-providers should not have to work through a + 'webmaster' to add new content to a site. All members of the team should + be able to work independently and in parallel. + + A clean separation makes it easier for a team of content-providers, + designers, and programmers to work together without stepping on each + other's toes and polluting each other's work. Other advantages include + faster development time; HTML and program code that are easier to + understand and maintain; content that can be displayed in a variety of + non-HTML formats such as PDF; and highly modular, flexible, and reusable + site architectures. + +\item {\bf to make it easy to integrate content, graphic design, and program code.} + + While it should be easy to develop content, graphic design, and program + code separately, it should NOT be difficult to integrate them as part of a + website. There should be no difficult hoops to jump through. + + It should be easy: + \begin{itemize} + \item for programmers to create reusable components and functions that are + accessible and understandable to designers. + \item for designers to mark out placeholders for content and dynamic components + in their templates. + \item for designers to soft-code aspects of their design that are either + repeated in several places or are subject to change. + \item for designers to extend and customize existing templates and thus minimize + duplication of effort and code. + \item and, of course, for content-providers to use the templates that + designers have created. + \end{itemize} + + +\item {\bf to provide template designers with a small set of 'Display Logic' + programming structures such as conditional blocks and + for loops} + + Graphic designers often do tasks that would be easier, faster, and less + error prone if they had access to {\bf conditional blocks} and {\bf for + loops}. However, a full programming language would be overkill for these + simple tasks and most designers don't have the time or desire to learn one. + +\item {\bf to be equally well-suited for HTML, SGML, XML, SQL, Postscript, form + email, LaTeX, or any other text-based format.} + + Although it was designed with dynamic websites and web applications in mind, + Cheetah is not HTML-specific. + +\item {\bf to achieve all these aims in a manner that is efficient, flexible, and + extendable.} + +\end{itemize} + +Cheetah achieves these aims by: + +\begin{itemize} + +\item blending the power and flexibility of Python with the simplicity of a + small Template Definition language that non-programmers can understand. + +\item giving template designers a simple way of accessing Python variables, + objects, and functions in their templates. + +\item providing a modular, object-orientated framework that makes it easy to + create and maintain large websites. + +\item compiling 'Template Definitions' into native Python code at startup. + Thereafter this code is executed for each request. This approach is + dramatically faster than the string substitution approach used by many + templating engines. + +\item providing a very simple, yet powerful, caching mechanism that can + significantly increase the responsiveness of a dynamic website. + +\end{itemize} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Why is it called Cheetah?} +\label{intro.name} + +Cheetah is fast, flexible, agile and graceful - like its namesake. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Who developed Cheetah?} +\label{intro.developers} + +Cheetah is one of several templating frameworks that grew out of a 'templates' +thread on the 'Webware For Python' email list. Tavis Rudd, Mike Orr, Chuck +Esterbrook, Ian Bicking and Tom Schwaller are the core developers. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{How mature is Cheetah?} +\label{intro.mature} + +Cheetah is alpha/beta software as this User's Guide is incomplete and several aspects +of the design are still subject to change. However, it has been tested +extensively and has few known issues. We are hoping to release production +version 1.0 in the summer of 2001. + +Here's a summary of known issues and aspects of the design that are in flux. +\begin{itemize} +\item The \#include directive is not working with relative path file includes + when used with Webware. This should be resolved soon. +\item The \#include directive might be reworked to monitor for changes in the + included file at run-time. It currently does the include once-off at + compile-time. +\item The implementation of \$placeholders(WithArgstrings) needs to be fleshed + out to handle nesting. +\end{itemize} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Where can I get releases?} +\label{intro.releases} + +Cheetah releases can be downloaded from +\url{http://CheetahTemplate.sourceforge.net} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Where can I get news?} +\label{intro.news} + +News and updates can be obtained from the the Cheetah website: +\url{http://CheetahTemplate.sourceforge.net} + +Cheetah discussions take place on the list +\email{cheetahtemplate-discuss@lists.sourceforge.net}. + +If you encounter difficulties, or are unsure about how to do something, +please post a detailed message to the list. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{How can I contribute?} +\label{intro.contribute} + +Cheetah is the work of many volunteers. If you use Cheetah please share your +experiences, tricks, customizations, and frustrations. + +\subsubsection{Bug reports and patches} + +If you think there is a bug in Cheetah, send a message to the email list +with the following information: + +\begin{enumerate} +\item a description of what you were trying to do and what happened +\item all tracebacks and error output +\item your version of Cheetah +\item your version of Python +\item your operating system +\item whether you have changed anything in the Cheetah installation +\end{enumerate} + +\subsubsection{Example sites and tutorials} +If you're developing a website with Cheetah, please send a link to the +email list so we can keep track of Cheetah sites. Also, if you discover +new and interesting ways to use Cheetah please share your experience and +write a quick tutorial about your technique. + +\subsubsection{Macro libraries} +We hope to build up a framework of macros libraries (see section +\ref{macros.libraries}) to distribute with Cheetah and would appreciate +any contributions. + +\subsubsection{Test cases} +Cheetah is packaged with a regression testing suite that is run with each +new release to ensure that everything is working as expected and that recent +changes haven't broken anything. The test cases are in the Cheetah.Tests +module. If you find a reproduceable bug please consider writing a test case +that will pass only when the bug is fixed. Send any new test cases to the email +list with the subject-line ``new test case for Cheetah.'' + +\subsubsection{Publicity} +Help spread the word ... recommend it to others, write articles about it, etc. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Acknowledgements} +\label{intro.acknowledgments} + +We'd like to thank the following people for contributing valuable advice, code +and encouragement: Geoff Talvola, Jay Love, Terrel Shumway, Sasa Zivkov, Arkaitz +Bitorika, Jeremiah Bellomy, Baruch Even, Paul Boddie, Stephan Diehl, and Geir +Magnusson. + +The Velocity, WebMacro and Smarty projects provided inspiration and design +ideas. Cheetah has benefited from the creativity and energy of their +developers. Thank you. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{License} +\label{intro.license} + +Cheetah is released for unlimited distribution under the terms of the +Python license. + diff --git a/docs/src/macros.aux b/docs/src/macros.aux new file mode 100644 index 0000000..bd9f6d6 --- /dev/null +++ b/docs/src/macros.aux @@ -0,0 +1,31 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {5}Macros}{14}} +\newlabel{macros}{{5}{14}} +\@writefile{toc}{\contentsline {subsection}{\numberline {5.1}What are macros?}{14}} +\newlabel{macros.whatAre}{{5.1}{14}} +\@writefile{toc}{\contentsline {subsection}{\numberline {5.2}Using macros}{14}} +\newlabel{macros.using}{{5.2}{14}} +\@writefile{toc}{\contentsline {subsection}{\numberline {5.3}Defining macros}{14}} +\newlabel{macros.defining}{{5.3}{14}} +\@writefile{toc}{\contentsline {subsection}{\numberline {5.4}Macro libraries}{14}} +\newlabel{macros.libraries}{{5.4}{14}} +\@setckpt{macros}{ +\setcounter{page}{15} +\setcounter{equation}{0} +\setcounter{enumi}{2} +\setcounter{enumii}{6} +\setcounter{enumiii}{0} +\setcounter{enumiv}{0} +\setcounter{footnote}{1} +\setcounter{mpfootnote}{0} +\setcounter{part}{0} +\setcounter{section}{5} +\setcounter{subsection}{4} +\setcounter{subsubsection}{0} +\setcounter{paragraph}{0} +\setcounter{subparagraph}{0} +\setcounter{figure}{0} +\setcounter{table}{0} +\setcounter{LT@tables}{0} +\setcounter{LT@chunks}{0} +} diff --git a/docs/src/macros.tex b/docs/src/macros.tex new file mode 100644 index 0000000..1c3fa95 --- /dev/null +++ b/docs/src/macros.tex @@ -0,0 +1,18 @@ +\section{Macros} +\label{macros} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{What are macros?} +\label{macros.whatAre} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Using macros} +\label{macros.using} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Defining macros} +\label{macros.defining} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{Macro libraries} +\label{macros.libraries}
\ No newline at end of file diff --git a/docs/src/modusers_guide.ind b/docs/src/modusers_guide.ind new file mode 100644 index 0000000..8af6c8a --- /dev/null +++ b/docs/src/modusers_guide.ind @@ -0,0 +1,4 @@ +\ +\begin{theindex} +\label{modindex} +\end{theindex} diff --git a/docs/src/moreverb.sty b/docs/src/moreverb.sty new file mode 100755 index 0000000..0f9470a --- /dev/null +++ b/docs/src/moreverb.sty @@ -0,0 +1,196 @@ +%%% moreverb.sty
+%%% AJCD 20 Sep 91
+%%% adds various verbatim environments using Rainer Sch\"opf's new verbatim
+%%% environment.
+
+%%% Marginal hacks (RF) to work `properly' with 2e
+\def\filedate{1994/12/13}
+\def\fileversion{v2.0}
+%
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{moreverb}
+ [\filedate\space \fileversion\space
+ LaTeX2e package for `more' verbatim enhancements]
+\typeout{Package: `moreverb'
+ \fileversion \space <\filedate> (RF, after AJCD and RmS)}
+%\typeout{English Documentation \@spaces <\docdate>} % oh no there isn't
+
+%%% load verbatim style if not already loaded.
+\@ifundefined{verbatim@processline}{\RequirePackage{verbatim}}{}
+
+%%% verbatimwrite writes all text in its body to a file, the name of which it
+%%% is given as an argument. Written by RmS.
+\newwrite \verbatim@out
+\def\verbatimwrite#1{%
+ \@bsphack
+ \immediate\openout \verbatim@out #1
+ \let\do\@makeother\dospecials
+ \catcode`\^^M\active \catcode`\^^I=12
+ \def\verbatim@processline{%
+ \immediate\write\verbatim@out
+ {\the\verbatim@line}}%
+ \verbatim@start}
+
+\def\endverbatimwrite{%
+ \immediate\closeout\verbatim@out
+ \@esphack}
+
+%%% Auxiliary macros and counters for expanding tabs. Use by listing and
+%%% verbatimtab environments.
+\newcount\tab@position \newcount\tab@size
+\newcount\verbatimtabsize \verbatimtabsize=8
+\def\@xobeytab{\leavevmode\penalty\@M
+ {\loop\ \global\advance\tab@position-1 \ifnum\tab@position>0 \repeat}}
+\begingroup
+ \catcode`\^^I=\active
+ \gdef\@vobeytabs{\catcode`\^^I\active\let^^I\@xobeytab}%
+\endgroup
+\def\verbatim@tabexpand#1{%
+ \ifx#1\@nil \let\next\par \else
+ \ifx#1\@xobeysp \@xobeysp\advance\tab@position-1 \else
+ \ifx#1\@xobeytab \@xobeytab\else
+ #1\advance\tab@position-1
+ \fi\fi
+ \ifnum\tab@position=0 \tab@position\tab@size \fi
+ \let\next\verbatim@tabexpand
+ \fi\next
+}
+
+%%% listing defines a verbatim environment with numbered lines; it takes an
+%%% optional argument specifying the number of lines between numbered
+%%% lines, and a mandatory argument specifying the starting line. listingcont
+%%% continues from the place where listing left off.
+%%% The style in which the label is set can be altered by re-defining
+%%% \listinglabel. * versions are provided.
+\newcount\listing@line \listing@line=1 \newcount\listing@step \listing@step=1
+% Adding an \hbox in front of the line causes a line break, so I go
+% through this rigmarole to get the lines aligned nicely. I probably
+% missed some obvious reason why \hboxes don't work.
+\def\listinglabel#1{\rlap{\small\rm\the#1}\hskip2.5em}
+\def\thelisting@line{%
+ \setbox0\hbox{\listinglabel\listing@line}%
+ \@tempcnta=\listing@line
+ \divide\@tempcnta\listing@step \multiply\@tempcnta\listing@step
+ \ifnum\listing@line=1 \unhbox0
+ \else \ifnum\@tempcnta=\listing@line \unhbox0
+ \else \hskip\wd0
+ \fi\fi}
+\def\listing{\@ifnextchar[{\@listing}{\@listing[1]}}
+\def\@listing[#1]#2{%
+ \global\listing@line=#2\global\listing@step=#1\listingcont}
+\def\listingcont{%
+ \tab@size=\verbatimtabsize
+ \def\verbatim@processline{\tab@position\tab@size
+ \thelisting@line \global\advance\listing@line1
+ \expandafter\verbatim@tabexpand\the\verbatim@line\@nil}%
+ \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs\verbatim@start}
+\let\endlisting=\endtrivlist
+\let\endlistingcont=\endtrivlist
+\@namedef{listing*}{\@ifnextchar[{\@listingstar}{\@listingstar[1]}}
+\def\@listingstar[#1]#2{%
+ \global\listing@line=#2\global\listing@step=#1\relax
+ \csname listingcont*\endcsname}
+\@namedef{listingcont*}{%
+ \def\verbatim@processline{%
+ \thelisting@line \global\advance\listing@line1
+ \the\verbatim@line\par}%
+ \@verbatim\verbatim@start}
+\expandafter\let\csname endlisting*\endcsname =\endtrivlist
+\expandafter\let\csname endlistingcont*\endcsname =\endtrivlist
+
+%%% file input version of listing
+\def\listinginput{%
+ \@ifnextchar[{\@listinginput}{\@listinginput[1]}}
+{\catcode`\~=\active \lccode`\~=`\^^M \lccode`\N=`\N
+ \lowercase{%
+ \gdef\@listinginput[#1]#2#3{\begingroup
+ \global\listing@line=#2\global\listing@step=#1
+ \tab@size=\verbatimtabsize
+ \def\verbatim@processline{\tab@position\tab@size
+ \thelisting@line \global\advance\listing@line1
+ \expandafter\verbatim@tabexpand\the\verbatim@line\@nil}%
+ \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs
+ \def\verbatim@addtoline##1~{%
+ \verbatim@line\expandafter{\the\verbatim@line##1}}%
+ \openin\verbtab@in=#3
+ \ifeof\verbtab@in\typeout{No file #3.}\else
+ \verbtab@oktrue
+ \loop
+ \read\verbtab@in to \verbtab@line
+ \ifeof\verbtab@in\verbtab@okfalse\else
+ \expandafter\verbatim@addtoline\verbtab@line
+ \verbatim@processline
+ \verbatim@startline
+ \fi
+ \ifverbtab@ok\repeat
+ \closein\verbtab@in\fi
+ \endtrivlist\endgroup\@doendpe}}}
+
+%%% verbatimcmd is a verbatim environment with the exception of the escape and
+%%% grouping characters \, {, }.
+\def\verbatimcmd{%
+ \@verbatim \catcode`\\=0 \catcode`\{=1 \catcode`\}=2
+ \frenchspacing\@vobeyspaces\verbatim@start
+}
+\def\endverbatimcmd{%
+ \let\par\relax
+ \def\verbatim@{\endtrivlist\endgroup}%
+ \begingroup}
+
+%%% boxedverbatim produces a verbatim environment in a framed box.
+%%% written by Victor Eijkhout
+\def\boxedverbatim{%
+ % redefine `processline' to produce only a line as wide
+ % as the natural width of the line
+ \def\verbatim@processline{%
+ {\setbox0=\hbox{\the\verbatim@line}%
+ \hsize=\wd0 \the\verbatim@line\par}}%
+ % save the verbatim code in a box
+ \setbox0=\vbox\bgroup \verbatim
+}
+\def\endboxedverbatim{%
+ \endverbatim
+ \egroup % close the box and `fbox' it
+ \fbox{\box0}% <<<=== change here for centering,...
+}
+
+%%% verbatimtab is a verbatim environment which expands tab characters; it
+%%% takes an optional argument specifying the width of tab stops
+\def\verbatimtab{\futurelet\next\@verbatimtab}
+\def\@verbatimtab{\if\next[ \let\next\@@verbatimtab\else
+ \def\next{\@@verbatimtab[\the\verbatimtabsize]}\fi\next}
+\def\@@verbatimtab[#1]{%
+ \do@verbatimtab{#1}{%
+ \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs\verbatim@start}%
+}
+\def\do@verbatimtab#1#2{%
+ \tab@size=#1
+ \def\verbatim@processline{\tab@position\tab@size
+ \expandafter\verbatim@tabexpand\the\verbatim@line\@nil}#2
+}
+\let\endverbatimtab=\endtrivlist
+
+%%% file input version of verbatimtab
+\newread\verbtab@in \newif\ifverbtab@ok
+\def\verbatimtabinput{%
+ \@ifnextchar[{\@verbatimtabinput}{\@verbatimtabinput[\the\verbatimtabsize]}}
+{\catcode`\~=\active \lccode`\~=`\^^M \lccode`\N=`\N
+ \lowercase{%
+ \gdef\@verbatimtabinput[#1]#2{\begingroup
+ \do@verbatimtab{#1}{%
+ \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs}%
+ \def\verbatim@addtoline##1~{%
+ \verbatim@line\expandafter{\the\verbatim@line##1}}%
+ \openin\verbtab@in=#2
+ \ifeof\verbtab@in\typeout{No file #2.}\else
+ \verbtab@oktrue
+ \loop
+ \read\verbtab@in to \verbtab@line
+ \ifeof\verbtab@in\verbtab@okfalse\else
+ \expandafter\verbatim@addtoline\verbtab@line
+ \verbatim@processline
+ \verbatim@startline
+ \fi
+ \ifverbtab@ok\repeat
+ \closein\verbtab@in\fi
+ \endtrivlist\endgroup\@doendpe}}}
diff --git a/docs/src/users_guide.aux b/docs/src/users_guide.aux new file mode 100644 index 0000000..f3ba329 --- /dev/null +++ b/docs/src/users_guide.aux @@ -0,0 +1,9 @@ +\relax +\@input{introduction.aux} +\@input{gettingStarted.aux} +\@input{TemplateObjects.aux} +\@input{TDL.aux} +\@input{macros.aux} +\@input{webware.aux} +\@input{customizing.aux} +\@input{examples.aux} diff --git a/docs/src/users_guide.how b/docs/src/users_guide.how new file mode 100644 index 0000000..42f5ab2 --- /dev/null +++ b/docs/src/users_guide.how @@ -0,0 +1,110 @@ ++++ TEXINPUTS=/home/tavis/Cheetah/docs/src:/home/tavis/tmp/Python-2.1/Doc/paper-letter:/home/tavis/tmp/Python-2.1/Doc/texinputs::::~/.TeX:/usr/doc/.TeX:/usr/doc/.TeX::~/.TeX:/usr/doc/.TeX:/usr/doc/.TeX::~/.TeX:/usr/doc/.TeX:/usr/doc/.TeX: ++++ pdflatex users_guide +This is pdfTeX, Version 3.14159-13d (Web2C 7.3.1) +(/home/tavis/Cheetah/docs/src/users_guide.tex[/var/lib/texmf/pdftex/config/pdft +ex.cfg] +LaTeX2e <1999/12/01> patch level 1 +Babel <v3.6Z> and hyphenation patterns for american, french, german, ngerman, n +ohyphenation, loaded. +(/home/tavis/tmp/Python-2.1/Doc/texinputs/howto.cls +Document Class: howto 1998/02/25 Document class (Python HOWTO) +(/home/tavis/tmp/Python-2.1/Doc/texinputs/pypaper.sty +(/usr/share/texmf/tex/latex/psnfss/times.sty) +Using Times instead of Computer Modern. +) (/usr/share/texmf/tex/latex/base/article.cls +Document Class: article 1999/09/10 v1.4a Standard LaTeX document class +(/usr/share/texmf/tex/latex/base/size10.clo)) +(/usr/share/texmf/tex/latex/fancyhdr/fancyhdr.sty) +Using fancier footers than usual. +(/home/tavis/tmp/Python-2.1/Doc/texinputs/python.sty +(/usr/share/texmf/tex/latex/tools/longtable.sty) +(/usr/share/texmf/pdftex/plain/misc/pdfcolor.tex) +(/usr/share/texmf/tex/latex/tools/verbatim.sty) +(/usr/share/texmf/tex/latex/base/alltt.sty))) +(/home/tavis/Cheetah/docs/src/moreverb.sty +Package: `moreverb' v2.0 <1994/12/13> (RF, after AJCD and RmS) +) +No file users_guide.aux. +(/usr/share/texmf/tex/latex/psnfss/ot1ptm.fd) +Underfull \hbox (badness 10000) in paragraph at lines 11--11 + +(/usr/share/texmf/tex/latex/psnfss/ot1phv.fd) +No file users_guide.toc. +(/usr/share/texmf/tex/latex/psnfss/omsptm.fd) +Underfull \hbox (badness 10000) has occurred while \output is active +[] +[1[/var/lib/texmf/dvips/config/pdftex.map]] +(/home/tavis/Cheetah/docs/src/introduction.tex [2] [3] + +LaTeX Warning: Reference `macros.libraries' on page 4 undefined on input line 1 +80. + +) [4] (/home/tavis/Cheetah/docs/src/gettingStarted.tex +(/usr/share/texmf/tex/latex/psnfss/ot1pcr.fd)) [5] [6] +(/home/tavis/Cheetah/docs/src/TemplateObjects.tex) [7] +(/home/tavis/Cheetah/docs/src/TDL.tex [8] + +LaTeX Warning: Reference `webware' on page 9 undefined on input line 106. + +[9] + +LaTeX Warning: Reference `customizing' on page 10 undefined on input line 160. + +[10] [11] [12]) [13] (/home/tavis/Cheetah/docs/src/macros.tex) [14] +(/home/tavis/Cheetah/docs/src/webware.tex [15] [16]) [17] +(/home/tavis/Cheetah/docs/src/customizing.tex) [18] +(/home/tavis/Cheetah/docs/src/examples.tex) [19] +(/home/tavis/Cheetah/docs/src/users_guide.aux +(/home/tavis/Cheetah/docs/src/introduction.aux) +(/home/tavis/Cheetah/docs/src/gettingStarted.aux) +(/home/tavis/Cheetah/docs/src/TemplateObjects.aux) +(/home/tavis/Cheetah/docs/src/TDL.aux) (/home/tavis/Cheetah/docs/src/macros.aux +) (/home/tavis/Cheetah/docs/src/webware.aux) +(/home/tavis/Cheetah/docs/src/customizing.aux) +(/home/tavis/Cheetah/docs/src/examples.aux)) + +LaTeX Warning: There were undefined references. + + +LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. + + ) +(see the transcript file for additional information)<8r.enc><cmsy10.pfb> +Output written on users_guide.pdf (19 pages, 44632 bytes). +Transcript written on users_guide.log. ++++ pdflatex users_guide +This is pdfTeX, Version 3.14159-13d (Web2C 7.3.1) +(/home/tavis/Cheetah/docs/src/users_guide.tex[/var/lib/texmf/pdftex/config/pdft +ex.cfg] +LaTeX2e <1999/12/01> patch level 1 +Babel <v3.6Z> and hyphenation patterns for american, french, german, ngerman, n +ohyphenation, loaded. +(/home/tavis/tmp/Python-2.1/Doc/texinputs/howto.cls +Document Class: howto 1998/02/25 Document class (Python HOWTO) +(/home/tavis/tmp/Python-2.1/Doc/texinputs/pypaper.sty +(/usr/share/texmf/tex/latex/psnfss/times.sty) +Using Times instead of Computer Modern. +) (/usr/share/texmf/tex/latex/base/article.cls +Document Class: article 1999/09/10 v1.4a Standard LaTeX document class +(/usr/share/texmf/tex/latex/base/size10.clo)) +(/usr/share/texmf/tex/latex/fancyhdr/fancyhdr.sty) +Using fancier footers than usual. +(/home/tavis/tmp/Python-2.1/Doc/texinputs/python.sty +(/usr/share/texmf/tex/latex/tools/longtable.sty) +(/usr/share/texmf/pdftex/plain/misc/pdfcolor.tex) +(/usr/share/texmf/tex/latex/tools/verbatim.sty) +(/usr/share/texmf/tex/latex/base/alltt.sty) +! Interruption. +\@argdef ...fdefinable #1{\@yargdef #1\@ne {#2}{#3 + }} +l.634 }{\end{fulllineitems}} + +? +! Emergency stop. +\@argdef ...fdefinable #1{\@yargdef #1\@ne {#2}{#3 + }} +l.634 }{\end{fulllineitems}} + +! ==> Fatal error occurred, the output PDF file not finished! +Transcript written on users_guide.log. +*** Session transcript and error messages are in users_guide.how. diff --git a/docs/src/users_guide.ind b/docs/src/users_guide.ind new file mode 100644 index 0000000..f68479c --- /dev/null +++ b/docs/src/users_guide.ind @@ -0,0 +1,4 @@ +\ +\begin{theindex} +\label{genindex} +\end{theindex} diff --git a/docs/src/users_guide.l2h b/docs/src/users_guide.l2h new file mode 100644 index 0000000..b474c36 --- /dev/null +++ b/docs/src/users_guide.l2h @@ -0,0 +1,606 @@ +package main; +push (@INC, '/home/tavis/tmp/Python-2.1/Doc/perl'); +$mydir = '/home/tavis/tmp/Python-2.1/Doc/perl'; +# LaTeX2HTML support base for use with Python documentation. + +package main; + +use L2hos; + +$HTML_VERSION = 4.0; + +$MAX_LINK_DEPTH = 2; +$ADDRESS = ''; + +$NO_FOOTNODE = 1; +$NUMBERED_FOOTNOTES = 1; + +# Python documentation uses section numbers to support references to match +# in the printed and online versions. +# +$SHOW_SECTION_NUMBERS = 1; + +$ICONSERVER = '../icons'; +$IMAGE_TYPE = 'gif'; + +# Control where the navigation bars should show up: +$TOP_NAVIGATION = 1; +$BOTTOM_NAVIGATION = 1; +$AUTO_NAVIGATION = 0; + +$BODYTEXT = ''; +$CHILDLINE = "\n<p><hr>\n"; +$VERBOSITY = 0; + +# default # of columns for the indexes +$INDEX_COLUMNS = 2; +$MODULE_INDEX_COLUMNS = 4; + + +# A little painful, but lets us clean up the top level directory a little, +# and not be tied to the current directory (as far as I can tell). Testing +# an existing definition of $mydir is needed since it cannot be computed when +# run under mkhowto with recent versions of LaTeX2HTML, since this file is +# not read directly by LaTeX2HTML any more. mkhowto is required to prepend +# the required definition at the top of the actual input file. +# +if (!defined $mydir) { + use Cwd; + use File::Basename; + ($myname, $mydir, $myext) = fileparse(__FILE__, '\..*'); + chop $mydir; # remove trailing '/' + $mydir = getcwd() . "$dd$mydir" + unless $mydir =~ s|^/|/|; +} +$LATEX2HTMLSTYLES = "$mydir$envkey$LATEX2HTMLSTYLES"; +push (@INC, $mydir); + +($myrootname, $myrootdir, $myext) = fileparse($mydir, '\..*'); +chop $myrootdir; + + +# Hackish way to get the appropriate paper-*/ directory into $TEXINPUTS; +# pass in the paper size (a4 or letter) as the environment variable PAPER +# to add the right directory. If not given, the current directory is +# added instead for use with HOWTO processing. +# +if (defined $ENV{'PAPER'}) { + $mytexinputs = "$myrootdir${dd}paper-$ENV{'PAPER'}$envkey"; +} +else { + $mytexinputs = getcwd() . $envkey; +} +$mytexinputs .= "$myrootdir${dd}texinputs"; + + +# Change this variable to change the text added in "About this document..."; +# this should be an absolute pathname to get it right. +# +$ABOUT_FILE = "$myrootdir${dd}html${dd}stdabout.dat"; + + +sub custom_driver_hook { + # + # This adds the directory of the main input file to $TEXINPUTS; it + # seems to be sufficiently general that it should be fine for HOWTO + # processing. + # + my $file = @_[0]; + my($jobname, $dir, $ext) = fileparse($file, '\..*'); + $dir = L2hos->Make_directory_absolute($dir); + $dir =~ s/$dd$//; + $TEXINPUTS = "$dir$envkey$mytexinputs"; + print "\nAdding $dir to \$TEXINPUTS\n"; +} + + +$CUSTOM_BUTTONS = ''; + +sub make_nav_sectref { + my($label,$title) = @_; + if ($title) { + if ($title =~ /\<[aA] /) { + $title =~ s/\<[aA] /<a class="sectref" /; + } + else { + $title = "<span class=\"sectref\">$title</span>"; + } + return "<b class=\"navlabel\">$label:</b> $title\n"; + } + return ''; +} + +@my_icon_tags = (); +$my_icon_tags{'next'} = 'Next Page'; +$my_icon_tags{'next_page'} = 'Next Page'; +$my_icon_tags{'previous'} = 'Previous Page'; +$my_icon_tags{'previous_page'} = 'Previous Page'; +$my_icon_tags{'up'} = 'Up One Level'; +$my_icon_tags{'contents'} = 'Contents'; +$my_icon_tags{'index'} = 'Index'; +$my_icon_tags{'modules'} = 'Module Index'; + +@my_icon_names = (); +$my_icon_names{'previous_page'} = 'previous'; +$my_icon_names{'next_page'} = 'next'; + +sub get_my_icon { + my $name = @_[0]; + my $text = $my_icon_tags{$name}; + if ($my_icon_names{$name}) { + $name = $my_icon_names{$name}; + } + if ($text eq '') { + $name = 'blank'; + } + my $iconserver = ($ICONSERVER eq '.') ? '' : "$ICONSERVER/"; + return "<img src=\"$iconserver$name.$IMAGE_TYPE\"\n border=\"0\"" + . " height=\"32\"\n alt=\"$text\" width=\"32\">"; +} + +sub use_my_icon { + my $s = @_[0]; + if ($s =~ /\<tex2html_([a-z_]+)_visible_mark\>/) { + my $r = get_my_icon($1); + $s =~ s/\<tex2html_[a-z_]+_visible_mark\>/$r/; + } + return $s; +} + +sub make_nav_panel { + my $s; + my $BLANK_ICON = get_my_icon('blank'); + $NEXT = $NEXT_TITLE ? use_my_icon("$NEXT") : $BLANK_ICON; + $UP = $UP_TITLE ? use_my_icon("$UP") : $BLANK_ICON; + $PREVIOUS = $PREVIOUS_TITLE ? use_my_icon("$PREVIOUS") : $BLANK_ICON; + $CONTENTS = use_my_icon("$CONTENTS"); + $INDEX = $INDEX ? use_my_icon("$INDEX") : $BLANK_ICON; + if (!$CUSTOM_BUTTONS) { + $CUSTOM_BUTTONS = $BLANK_ICON; + } + $s = ('<table align="center" width="100%" cellpadding="0" cellspacing="2">' + . "\n<tr>" + # left-hand side + . "\n<td>$PREVIOUS</td>" + . "\n<td>$UP</td>" + . "\n<td>$NEXT</td>" + # title box + . "\n<td align=\"center\" width=\"100%\">$t_title</td>" + # right-hand side + . "\n<td>$CONTENTS</td>" + . "\n<td>$CUSTOM_BUTTONS</td>" # module index + . "\n<td>$INDEX</td>" + . "\n</tr></table>\n" + # textual navigation + . make_nav_sectref("Previous", $PREVIOUS_TITLE) + . make_nav_sectref("Up", $UP_TITLE) + . make_nav_sectref("Next", $NEXT_TITLE) + ); + # remove these; they are unnecessary and cause errors from validation + $s =~ s/ NAME="tex2html\d+"\n */ /g; + return $s; +} + +sub get_version_text { + if ($PACKAGE_VERSION ne '' && $t_date) { + return ("<span class=\"release-info\">" + . "Release $PACKAGE_VERSION," + . " documentation updated on $t_date.</span>"); + } + if ($PACKAGE_VERSION ne '') { + return ("<span class=\"release-info\">" + . "Release $PACKAGE_VERSION.</span>"); + } + if ($t_date) { + return ("<span class=\"release-info\">Documentation released on " + . "$t_date.</span>"); + } + return ''; +} + + +sub top_navigation_panel { + return "\n" + . make_nav_panel() + . "<br><hr>\n"; +} + +sub bot_navigation_panel { + return "\n<p><hr>\n" + . make_nav_panel() + . "<hr>\n" + . get_version_text() + . "\n"; +} + +sub add_link { + # Returns a pair (iconic link, textual link) + my($icon, $current_file, @link) = @_; + my($dummy, $file, $title) = split($delim, + $section_info{join(' ',@link)}); + if ($icon =~ /\<tex2html_([_a-z]+)_visible_mark\>/) { + my $r = get_my_icon($1); + $icon =~ s/\<tex2html_[_a-z]+_visible_mark\>/$r/; + } + if ($title && ($file ne $current_file)) { + $title = purify($title); + $title = get_first_words($title, $WORDS_IN_NAVIGATION_PANEL_TITLES); + return (make_href($file, $icon), make_href($file, "$title")) + } + elsif ($icon eq get_my_icon('up') && $EXTERNAL_UP_LINK) { + return (make_href($EXTERNAL_UP_LINK, $icon), + make_href($EXTERNAL_UP_LINK, "$EXTERNAL_UP_TITLE")) + } + elsif ($icon eq get_my_icon('previous') + && $EXTERNAL_PREV_LINK && $EXTERNAL_PREV_TITLE) { + return (make_href($EXTERNAL_PREV_LINK, $icon), + make_href($EXTERNAL_PREV_LINK, "$EXTERNAL_PREV_TITLE")) + } + elsif ($icon eq get_my_icon('next') + && $EXTERNAL_DOWN_LINK && $EXTERNAL_DOWN_TITLE) { + return (make_href($EXTERNAL_DOWN_LINK, $icon), + make_href($EXTERNAL_DOWN_LINK, "$EXTERNAL_DOWN_TITLE")) + } + return (&inactive_img($icon), ""); +} + +sub add_special_link { + my($icon, $file, $current_file) = @_; + if ($icon =~ /\<tex2html_([_a-z]+)_visible_mark\>/) { + my $r = get_my_icon($1); + $icon =~ s/\<tex2html_[_a-z]+_visible_mark\>/$r/; + } + return (($file && ($file ne $current_file)) + ? make_href($file, $icon) + : undef) +} + +# The img_tag() function seems only to be called with the parameter +# 'anchor_invisible_mark', which we want to turn into ''. Since +# replace_icon_marks() is the only interesting caller, and all it really +# does is call img_tag(), we can just define the hook alternative to be +# a no-op instead. +# +sub replace_icons_hook {} + +sub do_cmd_arabic { + # get rid of that nasty <SPAN CLASS="arabic">...</SPAN> + my($ctr, $val, $id, $text) = &read_counter_value(@_[0]); + return ($val ? farabic($val) : "0") . $text; +} + + +sub gen_index_id { + # this is used to ensure common index key generation and a stable sort + my($str,$extra) = @_; + sprintf('%s###%s%010d', $str, $extra, ++$global{'max_id'}); +} + +sub insert_index { + my($mark,$datafile,$columns,$letters,$prefix) = @_; + my $prog = "$myrootdir/tools/buildindex.py"; + my $index; + if ($letters) { + $index = `$prog --columns $columns --letters $datafile`; + } + else { + $index = `$prog --columns $columns $datafile`; + } + if (!s/$mark/$prefix$index/) { + print "\nCould not locate index mark: $mark"; + } +} + +sub add_idx { + print "\nBuilding HTML for the index ..."; + close(IDXFILE); + insert_index($idx_mark, 'index.dat', $INDEX_COLUMNS, 1, ''); +} + + +$idx_module_mark = '<tex2html_idx_module_mark>'; +$idx_module_title = 'Module Index'; + +sub add_module_idx { + print "\nBuilding HTML for the module index ..."; + my $key; + my $first = 1; + my $prevplat = ''; + my $allthesame = 1; + my $prefix = ''; + foreach $key (keys %Modules) { + $key =~ s/<tt>([a-zA-Z0-9._]*)<\/tt>/\1/; + my $plat = "$ModulePlatforms{$key}"; + $plat = '' + if ($plat eq $IGNORE_PLATFORM_ANNOTATION); + if (!$first) { + $allthesame = 0 + if ($prevplat ne $plat); + } + else { $first = 0; } + $prevplat = $plat; + } + open(MODIDXFILE, '>modindex.dat') || die "\n$!\n"; + foreach $key (keys %Modules) { + # dump the line in the data file; just use a dummy seqno field + my $nkey = $1; + my $moditem = "$Modules{$key}"; + my $plat = ''; + $key =~ s/<tt>([a-zA-Z0-9._]*)<\/tt>/\1/; + if ($ModulePlatforms{$key} && !$allthesame) { + $plat = (" <em>(<span class='platform'>$ModulePlatforms{$key}" + . '</span>)</em>'); + } + print MODIDXFILE $moditem . $IDXFILE_FIELD_SEP + . "<tt class='module'>$key</tt>$plat###\n"; + } + close(MODIDXFILE); + + if ($GLOBAL_MODULE_INDEX) { + $prefix = <<MODULE_INDEX_PREFIX; + +<p> This index only lists modules documented in this manual. + The <em class="citetitle"><a href="$GLOBAL_MODULE_INDEX">Global Module + Index</a></em> lists all modules that are documented in this set + of manuals.</p> +MODULE_INDEX_PREFIX + } + if (!$allthesame) { + $prefix .= <<PLAT_DISCUSS; + +<p> Some module names are followed by an annotation indicating what +platform they are available on.</p> + +PLAT_DISCUSS + } + insert_index($idx_module_mark, 'modindex.dat', $MODULE_INDEX_COLUMNS, 0, + $prefix); +} + +# replace both indexes as needed: +sub add_idx_hook { + add_idx() if (/$idx_mark/); + process_python_state(); + if ($MODULE_INDEX_FILE) { + local ($_); + open(MYFILE, "<$MODULE_INDEX_FILE"); + sysread(MYFILE, $_, 1024*1024); + close(MYFILE); + add_module_idx(); + open(MYFILE,">$MODULE_INDEX_FILE"); + print MYFILE $_; + close(MYFILE); + } +} + + +# In addition to the standard stuff, add label to allow named node files and +# support suppression of the page complete (for HTML Help use). +sub do_cmd_tableofcontents { + local($_) = @_; + $TITLE = $toc_title; + $tocfile = $CURRENT_FILE; + my($closures,$reopens) = preserve_open_tags(); + anchor_label('contents', $CURRENT_FILE, $_); # this is added + join('', "<BR>\n\\tableofchildlinks[off]", $closures + , make_section_heading($toc_title, 'H2'), $toc_mark + , $reopens, $_); +} +# In addition to the standard stuff, add label to allow named node files. +sub do_cmd_listoffigures { + local($_) = @_; + $TITLE = $lof_title; + $loffile = $CURRENT_FILE; + my($closures,$reopens) = preserve_open_tags(); + anchor_label('lof', $CURRENT_FILE, $_); # this is added + join('', "<BR>\n", $closures + , make_section_heading($lof_title, 'H2'), $lof_mark + , $reopens, $_); +} +# In addition to the standard stuff, add label to allow named node files. +sub do_cmd_listoftables { + local($_) = @_; + $TITLE = $lot_title; + $lotfile = $CURRENT_FILE; + my($closures,$reopens) = preserve_open_tags(); + anchor_label('lot', $CURRENT_FILE, $_); # this is added + join('', "<BR>\n", $closures + , make_section_heading($lot_title, 'H2'), $lot_mark + , $reopens, $_); +} +# In addition to the standard stuff, add label to allow named node files. +sub do_cmd_textohtmlinfopage { + local($_) = @_; + if ($INFO) { # + anchor_label("about",$CURRENT_FILE,$_); # this is added + } # + my $the_version = ''; # and the rest is + if ($t_date) { # mostly ours + $the_version = ",\n$t_date"; + if ($PACKAGE_VERSION) { + $the_version .= ", Release $PACKAGE_VERSION"; + } + } + $_ = (($INFO == 1) + ? join('', + $close_all, + "<strong>$t_title</strong>$the_version\n", + `cat $ABOUT_FILE`, + $open_all, $_) + : join('', $close_all, $INFO,"\n", $open_all, $_)); + $_; +} + +# $idx_mark will be replaced with the real index at the end +sub do_cmd_textohtmlindex { + local($_) = @_; + $TITLE = $idx_title; + $idxfile = $CURRENT_FILE; + if (%index_labels) { make_index_labels(); } + if (($SHORT_INDEX) && (%index_segment)) { make_preindex(); } + else { $preindex = ''; } + my $heading = make_section_heading($idx_title, 'h2') . $idx_mark; + my($pre,$post) = minimize_open_tags($heading); + anchor_label('genindex',$CURRENT_FILE,$_); # this is added + return "<br>\n" . $pre . $_; +} + +$MODULE_INDEX_FILE = ''; + +# $idx_module_mark will be replaced with the real index at the end +sub do_cmd_textohtmlmoduleindex { + local($_) = @_; + $TITLE = $idx_module_title; + anchor_label('modindex', $CURRENT_FILE, $_); + $MODULE_INDEX_FILE = "$CURRENT_FILE"; + $_ = ('<p>' . make_section_heading($idx_module_title, 'h2') + . $idx_module_mark . $_); + return $_; +} + +# The bibliography and the index should be treated as separate +# sections in their own HTML files. The \bibliography{} command acts +# as a sectioning command that has the desired effect. But when the +# bibliography is constructed manually using the thebibliography +# environment, or when using the theindex environment it is not +# possible to use the normal sectioning mechanism. This subroutine +# inserts a \bibliography{} or a dummy \textohtmlindex command just +# before the appropriate environments to force sectioning. + +# XXX This *assumes* that if there are two {theindex} environments, +# the first is the module index and the second is the standard +# index. This is sufficient for the current Python documentation, +# but that's about it. + +sub add_bbl_and_idx_dummy_commands { + my $id = $global{'max_id'}; + + s/([\\]begin\s*$O\d+$C\s*thebibliography)/$bbl_cnt++; $1/eg; + s/([\\]begin\s*$O\d+$C\s*thebibliography)/$id++; "\\bibliography$O$id$C$O$id$C $1"/geo; + my(@parts) = split(/\\begin\s*$O\d+$C\s*theindex/); + if (scalar(@parts) == 3) { + # Be careful to re-write the string in place, since $_ is *not* + # returned explicity; *** nasty side-effect dependency! *** + print "\nadd_bbl_and_idx_dummy_commands ==> adding module index"; + my $rx = "([\\\\]begin\\s*$O\\d+$C\\s*theindex[\\s\\S]*)" + . "([\\\\]begin\\s*$O\\d+$C\\s*theindex)"; + s/$rx/\\textohtmlmoduleindex \1 \\textohtmlindex \2/o; + # Add a button to the navigation areas: + $CUSTOM_BUTTONS .= ('<a href="modindex.html" title="Module Index">' + . get_my_icon('modules') + . '</a>'); + } + else { + $CUSTOM_BUTTONS .= get_my_icon('blank'); + $global{'max_id'} = $id; # not sure why.... + s/([\\]begin\s*$O\d+$C\s*theindex)/\\textohtmlindex $1/o; + s/[\\]printindex/\\textohtmlindex /o; + } + #---------------------------------------------------------------------- + lib_add_bbl_and_idx_dummy_commands() + if defined(&lib_add_bbl_and_idx_dummy_commands); +} + +# The bibliographic references, the appendices, the lists of figures +# and tables etc. must appear in the contents table at the same level +# as the outermost sectioning command. This subroutine finds what is +# the outermost level and sets the above to the same level; + +sub set_depth_levels { + # Sets $outermost_level + my $level; + #RRM: do not alter user-set value for $MAX_SPLIT_DEPTH + foreach $level ("part", "chapter", "section", "subsection", + "subsubsection", "paragraph") { + last if (($outermost_level) = /\\($level)$delimiter_rx/); + } + $level = ($outermost_level ? $section_commands{$outermost_level} : + do {$outermost_level = 'section'; 3;}); + + #RRM: but calculate value for $MAX_SPLIT_DEPTH when a $REL_DEPTH was given + if ($REL_DEPTH && $MAX_SPLIT_DEPTH) { + $MAX_SPLIT_DEPTH = $level + $MAX_SPLIT_DEPTH; + } elsif (!($MAX_SPLIT_DEPTH)) { $MAX_SPLIT_DEPTH = 1 }; + + %unnumbered_section_commands = ('tableofcontents' => $level, + 'listoffigures' => $level, + 'listoftables' => $level, + 'bibliography' => $level, + 'textohtmlindex' => $level, + 'textohtmlmoduleindex' => $level); + $section_headings{'textohtmlmoduleindex'} = 'h1'; + + %section_commands = (%unnumbered_section_commands, + %section_commands); + + make_sections_rx(); +} + + +# This changes the markup used for {verbatim} environments, and is the +# best way I've found that ensures the <dl> goes on the outside of the +# <pre>...</pre>. +# +# Note that this *must* be done in the init file, not the python.perl +# style support file. The %declarations must be set before +# initialize() is called in the main LaTeX2HTML script (which happens +# before style files are loaded). +# +%declarations = ('preform' => '<dl><dd><pre class="verbatim"></pre></dl>', + %declarations); + + +# This is added to get rid of the long comment that follows the +# doctype declaration; MSIE5 on NT4 SP4 barfs on it and drops the +# content of the page. +sub make_head_and_body { + my($title, $body) = @_; + $body = " $body" unless ($body eq ''); + my $DTDcomment = ''; + my($version, $isolanguage) = ($HTML_VERSION, 'EN'); + my %isolanguages = ( 'english', 'EN' , 'USenglish', 'EN.US' + , 'original', 'EN' , 'german' , 'DE' + , 'austrian', 'DE.AT', 'french' , 'FR' + , 'spanish', 'ES'); + $isolanguage = $isolanguages{$default_language}; + $isolanguage = 'EN' unless $isolanguage; + $title = &purify($title,1); + eval("\$title = ". $default_title ) unless ($title); + + # allow user-modification of the <TITLE> tag; thanks Dan Young + if (defined &custom_TITLE_hook) { + $title = &custom_TITLE_hook($title, $toc_sec_title); + } + + if ($DOCTYPE =~ /\/\/[\w\.]+\s*$/) { # language spec included + $DTDcomment = "<!DOCTYPE html PUBLIC \"$DOCTYPE\">\n"; + } else { + $DTDcomment = "<!DOCTYPE html PUBLIC \"$DOCTYPE//" + . ($ISO_LANGUAGE ? $ISO_LANGUAGE : $isolanguage) . "\">\n"; + } + + $STYLESHEET = $FILE.".css" unless $STYLESHEET; + if (!$charset && $CHARSET) { $charset = $CHARSET; $charset =~ s/_/\-/go; } + + join('', ($DOCTYPE ? $DTDcomment : '' ) + ,"<html>\n<head>\n<title>", $title, "</title>\n" + , &meta_information($title) + , ($CHARSET && $HTML_VERSION ge "2.1" ? + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=$charset\">\n" + : "" ) + , ($BASE ? "<base href=\"$BASE\">\n" : "" ) + , "<link rel=\"STYLESHEET\" href=\"$STYLESHEET\">" + , $more_links_mark + , "\n</head>\n<body$body>"); +} + +1; # This must be the last line + +# auxillary init file for latex2html +# generated by mkhowto +$NO_AUTO_LINK = 1; +$ABOUT_FILE = "/home/tavis/tmp/Python-2.1/Doc/html/about.dat"; +$IMAGE_TYPE = "gif"; +$MAX_LINK_DEPTH = "3"; +$MAX_SPLIT_DEPTH = "6"; +1; diff --git a/docs/src/users_guide.log b/docs/src/users_guide.log new file mode 100644 index 0000000..efef21c --- /dev/null +++ b/docs/src/users_guide.log @@ -0,0 +1,93 @@ +This is pdfTeX, Version 3.14159-13d (Web2C 7.3.1) (format=pdflatex 2000.3.11) 12 JUN 2001 20:48 +**users_guide +(/home/tavis/Cheetah/docs/src/users_guide.tex[/var/lib/texmf/pdftex/config/pdft +ex.cfg] +LaTeX2e <1999/12/01> patch level 1 +Babel <v3.6Z> and hyphenation patterns for american, french, german, ngerman, n +ohyphenation, loaded. +(/home/tavis/tmp/Python-2.1/Doc/texinputs/howto.cls +Document Class: howto 1998/02/25 Document class (Python HOWTO) +(/home/tavis/tmp/Python-2.1/Doc/texinputs/pypaper.sty +(/usr/share/texmf/tex/latex/psnfss/times.sty +Package: times 1999/03/29 PSNFSS v.7.2 Times font as default roman : S Rahtz +) +Using Times instead of Computer Modern. +) (/usr/share/texmf/tex/latex/base/article.cls +Document Class: article 1999/09/10 v1.4a Standard LaTeX document class +(/usr/share/texmf/tex/latex/base/size10.clo +File: size10.clo 1999/09/10 v1.4a Standard LaTeX file (size option) +) +\c@part=\count79 +\c@section=\count80 +\c@subsection=\count81 +\c@subsubsection=\count82 +\c@paragraph=\count83 +\c@subparagraph=\count84 +\c@figure=\count85 +\c@table=\count86 +\abovecaptionskip=\skip41 +\belowcaptionskip=\skip42 +\bibindent=\dimen102 +) +(/usr/share/texmf/tex/latex/fancyhdr/fancyhdr.sty +\headwidth=\dimen103 +) +Using fancier footers than usual. +(/home/tavis/tmp/Python-2.1/Doc/texinputs/python.sty +Package: python 1998/01/11 LaTeX package (Python markup) + +(/usr/share/texmf/tex/latex/tools/longtable.sty +Package: longtable 1998/05/13 v4.09 Multi-page Table package (DPC) +\LTleft=\skip43 +\LTright=\skip44 +\LTpre=\skip45 +\LTpost=\skip46 +\LTchunksize=\count87 +\LTcapwidth=\dimen104 +\LT@head=\box26 +\LT@firsthead=\box27 +\LT@foot=\box28 +\LT@lastfoot=\box29 +\LT@cols=\count88 +\LT@rows=\count89 +\c@LT@tables=\count90 +\c@LT@chunks=\count91 +\LT@p@ftn=\toks14 +) +(/usr/share/texmf/pdftex/plain/misc/pdfcolor.tex) +(/usr/share/texmf/tex/latex/tools/verbatim.sty +Package: verbatim 2000/01/07 v1.5m LaTeX2e package for verbatim enhancements +\every@verbatim=\toks15 +\verbatim@line=\toks16 +\verbatim@in@stream=\read1 +) +\py@codewidth=\skip47 + +(/usr/share/texmf/tex/latex/base/alltt.sty +Package: alltt 1997/06/16 v2.0g defines alltt environment +) +\py@ModSynopsisFile=\write3 + +! Interruption. +\@argdef ...fdefinable #1{\@yargdef #1\@ne {#2}{#3 + }} +l.634 }{\end{fulllineitems}} + +? +! Emergency stop. +\@argdef ...fdefinable #1{\@yargdef #1\@ne {#2}{#3 + }} +l.634 }{\end{fulllineitems}} + +End of file on the terminal! + + +Here is how much of TeX's memory you used: + 732 strings out of 25631 + 8397 string characters out of 193579 + 54392 words of memory out of 384000 + 3753 multiletter control sequences out of 10000+15000 + 3640 words of font info for 14 fonts, out of 400000 for 1000 + 14 hyphenation exceptions out of 1000 + 30i,0n,42p,231b,40s stack positions out of 300i,100n,500p,50000b,4000s +! ==> Fatal error occurred, the output PDF file not finished! diff --git a/docs/src/users_guide.pdf b/docs/src/users_guide.pdf Binary files differnew file mode 100644 index 0000000..a71ab87 --- /dev/null +++ b/docs/src/users_guide.pdf diff --git a/docs/src/users_guide.tex b/docs/src/users_guide.tex new file mode 100644 index 0000000..8e410c1 --- /dev/null +++ b/docs/src/users_guide.tex @@ -0,0 +1,38 @@ +\documentclass{howto} +\usepackage{moreverb} %% Verbatim Code Listings + +\title{Cheetah Users Guide} +\release{0.9.6} + +\author{The Cheetah Development Team} +\authoraddress{\email{cheetahtemplate-devel@lists.sourceforge.net}} + +\begin{document} +\maketitle + + +\tableofcontents + + +\copyright{Copyright 2001, The Cheetah Development Team. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.1 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.} + + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \include{introduction} + \include{gettingStarted} + \include{TemplateObjects} + \include{TDL} + \include{macros} + \include{webware} + \include{customizing} + \include{examples} + %\include{errorMessages} + %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %\appendix + %\input{gfdl.tex} + +\end{document} diff --git a/docs/src/users_guide.toc b/docs/src/users_guide.toc new file mode 100644 index 0000000..0da3cdb --- /dev/null +++ b/docs/src/users_guide.toc @@ -0,0 +1,74 @@ +\contentsline {section}{\numberline {1}Introduction}{2} +\contentsline {subsection}{\numberline {1.1}What is Cheetah?}{2} +\contentsline {subsection}{\numberline {1.2}Why is it called Cheetah?}{3} +\contentsline {subsection}{\numberline {1.3}Who developed Cheetah?}{3} +\contentsline {subsection}{\numberline {1.4}How mature is Cheetah?}{3} +\contentsline {subsection}{\numberline {1.5}Where can I get releases?}{3} +\contentsline {subsection}{\numberline {1.6}Where can I get news?}{3} +\contentsline {subsection}{\numberline {1.7}How can I contribute?}{3} +\contentsline {subsubsection}{Bug reports and patches}{4} +\contentsline {subsubsection}{Example sites and tutorials}{4} +\contentsline {subsubsection}{Macro libraries}{4} +\contentsline {subsubsection}{Test cases}{4} +\contentsline {subsubsection}{Publicity}{4} +\contentsline {subsection}{\numberline {1.8}Acknowledgements}{4} +\contentsline {subsection}{\numberline {1.9}License}{4} +\contentsline {section}{\numberline {2}Getting Started}{5} +\contentsline {subsection}{\numberline {2.1}Requirements}{5} +\contentsline {subsection}{\numberline {2.2}Installation}{5} +\contentsline {subsection}{\numberline {2.3}Testing your installation}{5} +\contentsline {subsection}{\numberline {2.4}Quickstart tutorial}{5} +\contentsline {section}{\numberline {3}Template Objects}{7} +\contentsline {subsection}{\numberline {3.1}The Template class}{7} +\contentsline {subsection}{\numberline {3.2}Constructing Template objects}{7} +\contentsline {subsection}{\numberline {3.3}Using Template objects}{7} +\contentsline {subsection}{\numberline {3.4}Modifying Template objects}{7} +\contentsline {section}{\numberline {4}The Template Definition Language}{8} +\contentsline {subsection}{\numberline {4.1}Placeholder tags}{8} +\contentsline {subsubsection}{Rules for placeholder names}{8} +\contentsline {subsubsection}{The searchList}{9} +\contentsline {subsubsection}{Placeholder values}{9} +\contentsline {subsubsection}{Placeholders with no value defined}{10} +\contentsline {subsubsection}{Dotted notation}{10} +\contentsline {subsubsection}{Caching}{10} +\contentsline {subsection}{\numberline {4.2}Directive tags}{11} +\contentsline {subsubsection}{Escaping directives}{11} +\contentsline {subsubsection}{Tag closures: explicit and implicit}{11} +\contentsline {subsubsection}{Whitespace handling}{11} +\contentsline {subsection}{\numberline {4.3}Comment directives}{11} +\contentsline {subsection}{\numberline {4.4}\#raw directives}{12} +\contentsline {subsection}{\numberline {4.5}\#include directives}{12} +\contentsline {subsection}{\numberline {4.6}\#cache directives}{13} +\contentsline {subsection}{\numberline {4.7}Display logic directives}{13} +\contentsline {subsubsection}{Conditional blocks}{13} +\contentsline {subsubsection}{For loops}{13} +\contentsline {subsection}{\numberline {4.8}\#block directives}{13} +\contentsline {subsubsection}{\#redefine directives}{13} +\contentsline {section}{\numberline {5}Macros}{14} +\contentsline {subsection}{\numberline {5.1}What are macros?}{14} +\contentsline {subsection}{\numberline {5.2}Using macros}{14} +\contentsline {subsection}{\numberline {5.3}Defining macros}{14} +\contentsline {subsection}{\numberline {5.4}Macro libraries}{14} +\contentsline {section}{\numberline {6}Using Cheetah with Webware}{15} +\contentsline {subsection}{\numberline {6.1}Background}{15} +\contentsline {subsection}{\numberline {6.2}Using the Inheritance approach}{15} +\contentsline {subsubsection}{The \#data directive}{17} +\contentsline {subsubsection}{The SkeletonPage framework}{17} +\contentsline {subsubsection}{PlateKit}{17} +\contentsline {subsection}{\numberline {6.3}Using the Containment approach}{17} +\contentsline {subsection}{\numberline {6.4}User interaction in either approach}{17} +\contentsline {subsection}{\numberline {6.5}Components}{17} +\contentsline {subsubsection}{What are components?}{17} +\contentsline {subsubsection}{Using components}{17} +\contentsline {subsubsection}{Building components}{17} +\contentsline {section}{\numberline {7}Customizing and extending Cheetah}{18} +\contentsline {subsection}{\numberline {7.1}Custom handling of unknown placeholder names}{18} +\contentsline {subsection}{\numberline {7.2}Plugins}{18} +\contentsline {subsubsection}{The PSP plugin}{18} +\contentsline {subsection}{\numberline {7.3}Custom variable-tags}{18} +\contentsline {subsection}{\numberline {7.4}Custom directives}{18} +\contentsline {subsection}{\numberline {7.5}Custom error handlers}{18} +\contentsline {subsection}{\numberline {7.6}Safe delegation}{18} +\contentsline {section}{\numberline {8}Examples}{19} +\contentsline {subsection}{\numberline {8.1}Syntax examples}{19} +\contentsline {subsection}{\numberline {8.2}Webware Examples}{19} diff --git a/docs/src/webware.aux b/docs/src/webware.aux new file mode 100644 index 0000000..c6f2d6e --- /dev/null +++ b/docs/src/webware.aux @@ -0,0 +1,34 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {6}Using Cheetah with Webware}{15}} +\newlabel{webware}{{6}{15}} +\@writefile{toc}{\contentsline {subsection}{\numberline {6.1}Background}{15}} +\@writefile{toc}{\contentsline {subsection}{\numberline {6.2}Using the Inheritance approach}{15}} +\@writefile{toc}{\contentsline {subsubsection}{The \#data directive}{17}} +\@writefile{toc}{\contentsline {subsubsection}{The SkeletonPage framework}{17}} +\@writefile{toc}{\contentsline {subsubsection}{PlateKit}{17}} +\@writefile{toc}{\contentsline {subsection}{\numberline {6.3}Using the Containment approach}{17}} +\@writefile{toc}{\contentsline {subsection}{\numberline {6.4}User interaction in either approach}{17}} +\@writefile{toc}{\contentsline {subsection}{\numberline {6.5}Components}{17}} +\@writefile{toc}{\contentsline {subsubsection}{What are components?}{17}} +\@writefile{toc}{\contentsline {subsubsection}{Using components}{17}} +\@writefile{toc}{\contentsline {subsubsection}{Building components}{17}} +\@setckpt{webware}{ +\setcounter{page}{18} +\setcounter{equation}{0} +\setcounter{enumi}{2} +\setcounter{enumii}{6} +\setcounter{enumiii}{0} +\setcounter{enumiv}{0} +\setcounter{footnote}{1} +\setcounter{mpfootnote}{0} +\setcounter{part}{0} +\setcounter{section}{6} +\setcounter{subsection}{5} +\setcounter{subsubsection}{0} +\setcounter{paragraph}{0} +\setcounter{subparagraph}{0} +\setcounter{figure}{0} +\setcounter{table}{0} +\setcounter{LT@tables}{0} +\setcounter{LT@chunks}{0} +} diff --git a/docs/src/webware.tex b/docs/src/webware.tex new file mode 100644 index 0000000..074bd0c --- /dev/null +++ b/docs/src/webware.tex @@ -0,0 +1,144 @@ +\section{Using Cheetah with Webware} +\label{webware} + +\subsection{Background} +Webware is a 'Python-Powered Internet Platform' that uses servlets in a manner +similar to Java servlets. 'WebKit' is the name of Webware's application server. +For more details please visit http://webware.sourceforge.net. + +As Cheetah's core is flexible there are many ways to use it with Webware +servlets. There are two broad categories: the Inheritance approach and +the Containment approach. In the Inheritance approach a servlet is created that +subclasses both the Template class and Webware's HTTPServlet class. The +Template instance IS the servlet and its .respond() method is +automatically called by WebKit for each request. All pre-request processing is +handled via Cheetah. + +In the Containment approach an instance of the Template class is wrapped +up inside of a Webware servlet class. Instances of the servlet class must +explicitly call the Template instance's .respond(), or .__str__(), method +for each request. In this case the servlet class can handle whatever +per-request processing needs to be done before it calls Cheetah.respond(). + +The Inheritance approach is the simplest and is best suited for building sites +from scratch. + +The Containment approach is slightly more complex and is best suited for use +with existing Webware servlets. It is also ideal for cases where you wish to +use Cheetah for only a portion of the servlet's output, such as a +discussion-forum table at the bottom of a webpage. + +\subsection{Using the Inheritance approach} +Cheetah.Servlet provides a servlet class that can be subclassed to create +Webware servlets, as in the trivial example below. + +\begin{verbatim} +## FILE: hello_world.py ## +template = """ +<HTML> +<HEAD><TITLE>'Hello World - Test Servlet</TITLE></HEAD> +<BODY> +Hello World! +</BODY> +</HTML> +""" +from Cheetah.Servlet import TemplateServlet +class hello_world(TemplateServlet): + def __init__(self): + TemplateServlet.__init__(self, template) +\end{verbatim} + +TemplateServlet's constructor method (\code{TemplateServlet.__init__()}) adds +the attribute dictionary of the servlet to the searchList that +\code{\$placeholder} variables can extracted from. Thus, attributes and methods +of the servlet object can be interpolated into the template like this: + +\begin{verbatim} +## FILE: hello_world.py ## +template = """ +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> +$contents +</BODY> +</HTML> +""" +from Cheetah.Servlet import TemplateServlet +class hello_world(TemplateServlet): + title = 'Hello World - Test Servlet' + def __init__(self): + TemplateServlet.__init__(self, template) + + def contents(self): + return 'Hello World!' +\end{verbatim} + +This process can be simplified for non-programmers. All the Python wrapper code +in these examples can be generated automatically by \code{cheetah-compile}, a small program +that is installed with Cheetah. \code{cheetah-compile} parses Template Definitions +written in files with the .tmpl extension and generates a Webware servlet file +with the .py extension for each .tmpl file. Type \code{cheetah-compile} after +installing Cheetah to get information on how to use it. + +Here's the first example as a .tmpl file: + +\begin{verbatim} +## FILE: hello_world.tmpl ## +<HTML> +<HEAD><TITLE>Hello World - Test Servlet</TITLE></HEAD> +<BODY> +Hello World! +</BODY> +</HTML> +\end{verbatim} + +Here's the second example as a .tmpl file. Note that all lines that begin with +\code{\#\#} are comment lines. + +\begin{verbatim} +## FILE: hello_world.tmpl ## +##=================================== +#data +title = 'Hello World - Test Servlet' +def contents(): + return 'Hello World!' +#/data +##=================================== +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> +$contents +</BODY> +</HTML> +\end{verbatim} + +\subsubsection{The \#data directive} +The previous example introduced the \code{\#data} directive, a shortcut means +of adding data into the servlet's attribute dictionary and thus into +the Template's {\bf searchList}. The \code{\#data} directive can be used in +any Template Definition, regardless of whether the definition is in a .py or +.tmpl file. + +\code{\#data} directives can contain any valid Python code + +\subsubsection{The SkeletonPage framework} + +\subsubsection{PlateKit} +Forget what you knew about PlateKit. It's undergoing some changes and is out of +action for the time-being. + +\subsection{Using the Containment approach} + + +\subsection{User interaction in either approach} + + +\subsection{Components} + +\subsubsection{What are components?} + +\subsubsection{Using components} + +\subsubsection{Building components} + + diff --git a/examples/webware_examples/cheetahSite/SiteTemplate.tmpl b/examples/webware_examples/cheetahSite/SiteTemplate.tmpl new file mode 100644 index 0000000..69fd500 --- /dev/null +++ b/examples/webware_examples/cheetahSite/SiteTemplate.tmpl @@ -0,0 +1,427 @@ +#extend Cheetah.Templates.SkeletonPage + +##============================================================================## +## macros for the site + +#macro Python() +<A HREF="http://www.python.org">Python</A> +#end macro + +#macro insetBox(width=170, boxTitle='', boxContents='') +<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$width> + <TR> + <TD COLSPAN=3 HEIGHT=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD> + </TR> + <TR> + <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD> + <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> ## force full width + <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD> + </TR> + <TR> + <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD> + <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.innerFrameColor"> ## force full width + <DIV CLASS="insetBoxTitleContainer"><DIV CLASS="insetBoxTitle">$boxTitle</DIV></DIV> + </TD> + <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD> + </TR> + <TR> + <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD> + <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> ## force full width + <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD> + </TR> + <TR> + <TD COLSPAN=3 HEIGHT=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD> + </TR> +</TABLE> + +<TABLE VALIGN="top" WIDTH=$*width BORDER=0 CELLPADDING=0 CELLSPACING=0> + <TR> + <TD WIDTH=1 bgcolor="$*insetBoxes.frameColor">#spacer()</TD> + <TD WIDTH=900 BGCOLOR="$*insetBoxes.bgcolor"> ## force to full width + ## now begin the contents cell of the insetBox + $boxContents + ## end insetBox + </TD> + <TD WIDTH=1 BGCOLOR="$*insetBoxes.frameColor">#spacer()</TD> + </TR> +</TABLE> + +<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$width> + <TR> + <TD HEIGHT=1 BGCOLOR="$*insetBoxes.frameColor">#spacer()</TD> + </TR> +</TABLE> +#end macro +##============================================================================## + + + +##============================================================================## +#data + +title = "Cheetah - The Python-Powered Template Engine" +siteDomainName = "cheetahtemplate.sourceforge.net" +siteCopyrightName= "The Cheetah Development Team" + + +bodyTagAttribs = {"bgcolor": "black", + "text":"#000033", #333333 + } + +navBar = {"bgcolor":"#3366CC", #EBEBEB #E0E0E0 + } + +locationBar = {"bgcolor":"#3366CC", #EBEBEB #E0E0E0 + "frameColor":"#CCCCCC", + } + +mainTbl = {"width":"90%", + "align":"center", + "contentsBgColor":"white", + "outerFrameColor":"#6699FF", #FFC850 #BDD4F1 + "innerFrameColor":"#3366CC", #0059B2 + "innerFrameWidth":8, + } + +insetBoxes = {"width":170, + "frameColor":"#DDDDDD", #000033 + "titleFrameColor":"black", #000033 + "bgcolor":"#FAFAFA", + } + + +_sanSerifFonts = """helvetica, arial, "lucida sans", sans-serif""" + +_stylesheets = { + "H1.mainText": {"margin":"7px 2px", + "font-size":"small", + "font-weight":"bold", + "font-family": _sanSerifFonts, + }, + "P, .mainText": {"margin-left":"10px"}, + + ".navBarContainer": {"padding": "4px 0px", "color":"white"}, + ".navBar, A.navBar:link, A.navBar:visited": {"font-size":"small", + "font-weight":"bold", + "color":"white", + "text-decoration":"none", + "font-family": _sanSerifFonts, + }, + "A.navBar:hover, A.navBar:active": {"text-decoration":"underline"}, + + ".locationBarContainer": {"padding": "2px 10px"}, + ".locationBar, A.locationBar:link, A.locationBar:visited": {"font-size":"x-small", + "color":"#666666", + "text-decoration":"none", + "font-family": _sanSerifFonts, + }, + "A.locationBar:hover, A.locationBar:active": {"text-decoration":"underline"}, + + ".copyrightFooter": {"color":"white", "font-size":"9pt"}, + + ".insetBoxTitleContainer":{"padding": "2px 5px", + "color":"white", + "font-weight":"bold", + }, + ".insetBoxTitle":{"color":"white", + "font-size":"x-small", + "font-weight":"bold", + "text-decoration":"none", + "font-family": _sanSerifFonts, + }, + ".insetBoxContainer":{"padding": "2px 2px 2px 5px"}, + ".insetBox":{"font-size":"x-small", + "font-family": _sanSerifFonts, + }, + "H1.insetBox":{"margin": "5px 5px", + "padding": "0px", + "font-size":"x-small", + "font-weight":"bold", + "font-family": _sanSerifFonts + }, + + "P.insetBox":{"margin": "2px 5px 7px 10px", + "padding": "0px", + "font-family": _sanSerifFonts + }, + "A.insetBox:link, A.insetBox:visited":{"font-size":"x-small", + "text-decoration":"none", + }, + "A.insetBox:hover, A.insetBox:active": {"text-decoration":"underline"}, + + ".listItems": {"margin-bottom":"200px"}, + } + +_stylesheetsOrder = ["H1.mainText", + "P, .mainText", + ".navBarContainer", + ".navBar, A.navBar:link, A.navBar:visited", + "A.navBar:hover, A.navBar:active", + ".locationBarContainer", + ".locationBar, A.locationBar:link, A.locationBar:visited", + "A.locationBar:hover, A.locationBar:active", + ".copyrightFooter", + ".insetBoxTitleContainer", + ".insetBoxTitle", + ".insetBoxContainer", + ".insetBox", + "H1.insetBox", + "P.insetBox", + "A.insetBox:link, A.insetBox:visited", + "A.insetBox:hover, A.insetBox:active", + ".listItems", + ] + +#end data +##============================================================================## + + +##============================================================================## +## from SkeletonPage +#redefine bodyContents + +##################### +#block header + +#end block header +##################### + +<TABLE ALIGN=$*mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width> + <TR> + <TD> + ##################### + #block mainTblOuterHeader + <DIV ALIGN="center"><IMG SRC="cheetah-face-black-medium.jpg" + ALT="">#spacer(35)<IMG SRC="banner_text.gif" + ALT="Cheetah: The Python-Powered Template Engine"></DIV> + ##spacer(1,10) + + #end block mainTblOuterHeader + ##################### + </TD> + </TR> + <TR> + <TD WIDTH=1 HEIGHT=1 bgcolor="$*mainTbl.outerFrameColor">#spacer()</TD> + ## top line of the mainTbl frame + </TR> +</TABLE> + + +<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width> + <TR> + <TD WIDTH=1 bgcolor="$*mainTbl.outerFrameColor">#spacer()</TD> + <TD WIDTH=900> ## force to full width in Netscape + ##################### + #block mainTblInnerHeader + + ############## + #block navBar + <TABLE WIDTH=100% BGCOLOR="$*navBar.bgcolor" BORDER=0 CELLPADDING=0 CELLSPACING=0> + <TR> + <TD> +#macro navDivider() +#spacer(35)|#spacer(35) +#end macro + + <DIV ALIGN="center" CLASS="navBarContainer"> + <A CLASS="navBar" HREF="index.html">Home</A>#navDivider() + <A CLASS="navBar" HREF="learn.html">Learn</A>#navDivider() + <A CLASS="navBar" HREF="download.html">Download</A>#navDivider() + <A CLASS="navBar" HREF="contribute.html">Contribute</A> + </DIV> + </TD> + </TR> + </TABLE> + #end block navBar + ############## + + #end block mainTblInnerHeader + ##################### + </TD> + <TD WIDTH="1" bgcolor="$*mainTbl.outerFrameColor">#spacer()</TD> + </TR> +</TABLE> + +<TABLE ALIGN=$*mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width> + <TR> + <TD WIDTH=1 BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD> + <TD WIDTH=$*mainTbl.innerFrameWidth bgcolor="$*mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH=900 HEIGHT=1 BGCOLOR="black">#spacer(100)</TD> ## force to full width in Netscape + <TD WIDTH=$*mainTbl.innerFrameWidth bgcolor="$*mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH="1" BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD> + </TR> +</TABLE> + + +## MainTbl - contents ## + +<TABLE ALIGN=$*mainTbl.align VALIGN="top" BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width> + <TR> + <TD WIDTH=1 BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD> + <TD WIDTH=$*mainTbl.innerFrameWidth bgcolor="$*mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH=1 BGCOLOR="black">#spacer()</TD> + + <TD BGCOLOR="$*mainTbl.contentsBgColor"> + + ################### + #block locationBar + <TABLE WIDTH=100% BGCOLOR="#EBEBEB" BORDER=0 CELLPADDING=0 CELLSPACING=0> + <TR> + <TD> + <DIV CLASS="locationBarContainer"> + ################### + #block locationBarContents + <A CLASS="locationBar" HREF="index.html">Home</A> > + <A CLASS="locationBar" HREF="download.html">Download</A> + #end block locationBarContents + ################### + </DIV> + </TD> + </TR> + <TR> + <TD HEIGHT=1 BGCOLOR="$*locationBar.frameColor">#spacer()</TD> + </TR> + </TABLE> + + #end block locationBar + ################### + + + + ###################### + #block mainTblContents + + #spacer(1,25) + + <TABLE VALIGN="top" WIDTH=100% BORDER=0 CELLPADDING=10 CELLSPACING=0> + <TR> + <TD VALIGN="top"> + ################ + #block leftSideBar + + ##<DIV ALIGN="center"><IMG SRC="cheetah-face_medium.jpg" ALT="" BORDER=3></DIV> + ##<BR> + + #* + ##====================================== + #callMacro insetBox(boxTitle="Sections") + #arg boxContents + <DIV CLASS="insetBox"> + + <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="learn">Learn</A></H1> + <P CLASS="insetBox"> + Users Guide<BR> + Tutorials<BR> + Tips and Tricks<BR> + Articles<BR> + Language Comparisons<BR> + Developer Information<BR> + </P> + + <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="download">Download</A></H1> + <P CLASS="insetBox"> + Stable Releases<BR> + CVS Snapshots<BR> + Third-Party Packages<BR> + Browse the CVS<BR> + </P> + + <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="contribute">Contribute</A></H1> + <P CLASS="insetBox"> + Join the Mailing Lists<BR> + Links to Webware Sites<BR> + Bug Reports<BR> + Patches and New Code<BR> + Tutorials<BR> + Tips and Tricks<BR> + Articles<BR> + Language Comparisons<BR> + </P> + </DIV> ## end insetBox + #end arg + #end callMacro + ##====================================== + *# + + #end block leftSideBar + ################ + + </TD> + <TD VALIGN="top"> + ######################## + #block mainContentsCell + #end block mainContentsCell + ######################## + </TD> + </TR> + </TABLE> + + #spacer(1,200) + #end block mainTblContents + ###################### + </TD> + + <TD WIDTH=1 BGCOLOR="black">#spacer()</TD> + <TD WIDTH=$*mainTbl.innerFrameWidth BGCOLOR="$*mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH="1" BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD> + </TR> +</TABLE> + + +## MainTbl - closure ## +<TABLE ALIGN=$*mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width> + <TR> + <TD WIDTH=1 BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD> + <TD WIDTH=$*mainTbl.innerFrameWidth bgcolor="$*mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH=900 BGCOLOR="black">#spacer(100)</TD> ## force to full width in Netscape + <TD WIDTH=$*mainTbl.innerFrameWidth bgcolor="$*mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH="1" BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD> + </TR> +</TABLE> + +<TABLE ALIGN=$*mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width> + <TR> + <TD WIDTH=1 BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD> + <TD WIDTH=900 BGCOLOR="$*mainTbl.innerFrameColor">#spacer(1,25)</TD> ## force to full width in Netscape + <TD WIDTH="1" BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD> + </TR> +</TABLE> + +<TABLE ALIGN=$*mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width> + <TR> + <TD WIDTH=1 HEIGHT=1 BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD> + </TR> + <TR> + <TD> + ##################### + #block mainTblOuterFooter + <DIV CLASS="copyrightFooter"> + Copyright © #currentYr() $*siteCopyrightName. All rights reserved. + </DIV> + <BR> + + <DIV CLASS="copyrightFooter" ALIGN="center">Hosted on<BR> + <a href=http://sourceforge.net><img + src=http://sourceforge.net/sflogo.php?group_id=28961&type=1" + alt="SourceForge Home" height=31 width=88 border=0></a> + </DIV> + + #end block mainTblOuterFooter + ##################### + </TD> + </TR> +</TABLE> + + +##################### +#block footer +<BR> +<BR> +<BR> +#end block footer +##################### + + +#end redefine bodyContents +## from SkeletonPage +##============================================================================## diff --git a/examples/webware_examples/cheetahSite/banner copy.jpg b/examples/webware_examples/cheetahSite/banner copy.jpg Binary files differnew file mode 100644 index 0000000..173f8c6 --- /dev/null +++ b/examples/webware_examples/cheetahSite/banner copy.jpg diff --git a/examples/webware_examples/cheetahSite/banner2.jpg b/examples/webware_examples/cheetahSite/banner2.jpg Binary files differnew file mode 100644 index 0000000..44face0 --- /dev/null +++ b/examples/webware_examples/cheetahSite/banner2.jpg diff --git a/examples/webware_examples/cheetahSite/banner3.jpg b/examples/webware_examples/cheetahSite/banner3.jpg Binary files differnew file mode 100644 index 0000000..8b48927 --- /dev/null +++ b/examples/webware_examples/cheetahSite/banner3.jpg diff --git a/examples/webware_examples/cheetahSite/banner_cheetah.jpg b/examples/webware_examples/cheetahSite/banner_cheetah.jpg Binary files differnew file mode 100644 index 0000000..acb4439 --- /dev/null +++ b/examples/webware_examples/cheetahSite/banner_cheetah.jpg diff --git a/examples/webware_examples/cheetahSite/banner_text.gif b/examples/webware_examples/cheetahSite/banner_text.gif Binary files differnew file mode 100644 index 0000000..98f33db --- /dev/null +++ b/examples/webware_examples/cheetahSite/banner_text.gif diff --git a/examples/webware_examples/cheetahSite/cheetah-face-black-medium.jpg b/examples/webware_examples/cheetahSite/cheetah-face-black-medium.jpg Binary files differnew file mode 100644 index 0000000..fb4ad32 --- /dev/null +++ b/examples/webware_examples/cheetahSite/cheetah-face-black-medium.jpg diff --git a/examples/webware_examples/cheetahSite/cheetah-face-black.jpg b/examples/webware_examples/cheetahSite/cheetah-face-black.jpg Binary files differnew file mode 100644 index 0000000..ace02c8 --- /dev/null +++ b/examples/webware_examples/cheetahSite/cheetah-face-black.jpg diff --git a/examples/webware_examples/cheetahSite/cheetah-face-black.psd b/examples/webware_examples/cheetahSite/cheetah-face-black.psd Binary files differnew file mode 100644 index 0000000..7c765c6 --- /dev/null +++ b/examples/webware_examples/cheetahSite/cheetah-face-black.psd diff --git a/examples/webware_examples/cheetahSite/cheetah-face-original.jpg b/examples/webware_examples/cheetahSite/cheetah-face-original.jpg Binary files differnew file mode 100644 index 0000000..a63cd21 --- /dev/null +++ b/examples/webware_examples/cheetahSite/cheetah-face-original.jpg diff --git a/examples/webware_examples/cheetahSite/cheetah-face.jpg b/examples/webware_examples/cheetahSite/cheetah-face.jpg Binary files differnew file mode 100644 index 0000000..dace106 --- /dev/null +++ b/examples/webware_examples/cheetahSite/cheetah-face.jpg diff --git a/examples/webware_examples/cheetahSite/cheetah-face_medium.jpg b/examples/webware_examples/cheetahSite/cheetah-face_medium.jpg Binary files differnew file mode 100644 index 0000000..261888b --- /dev/null +++ b/examples/webware_examples/cheetahSite/cheetah-face_medium.jpg diff --git a/examples/webware_examples/cheetahSite/contribute.tmpl b/examples/webware_examples/cheetahSite/contribute.tmpl new file mode 100644 index 0000000..6256758 --- /dev/null +++ b/examples/webware_examples/cheetahSite/contribute.tmpl @@ -0,0 +1,93 @@ +#extend SiteTemplate + +##============================================================================## +#data + +#end data +##============================================================================## + + +##============================================================================## +## from SiteTemplate +#redefine locationBarContents + <A CLASS="locationBar" HREF="index.html">Home</A> > + <A CLASS="locationBar" HREF="contribute.html">Contribute</A> +#end redefine locationBarContents +##============================================================================## + + +##============================================================================## +## from SiteTemplate +#redefine mainContentsCell + +<H2>How can I contribute? +</H2> + +<P> Cheetah is the work of many volunteers. If you use Cheetah, share +your experiences, tricks, customizations, and frustrations. Please join the <a +href=http://lists.sourceforge.net/mailman/listinfo/cheetahtemplate-discuss>mailing +list</a>. + +<P> + +<H3>Bug reports and patches</A> +</H3> + +<P> +If you think there is a bug in Cheetah, send a message to the email list +with the following information: + +<P> + +<OL> +<LI>a description of what you were trying to do and what happened +</LI> +<LI>all tracebacks and error output +</LI> +<LI>your version of Cheetah +</LI> +<LI>your version of Python +</LI> +<LI>your operating system +</LI> +<LI>whether you have changed anything in the Cheetah installation +</LI> +</OL> + +<P> + +<H3>Example sites and tutorials</A> +</H3> +If you're developing a website with Cheetah, please send a link to the +email list so we can keep track of Cheetah sites. Also, if you discover +new and interesting ways to use Cheetah please share your experience and +write a quick tutorial about your technique. + +<P> + +<H3>Macro libraries</A></H3> +We hope to build up a framework of macros +libraries (see the Users Guide) to distribute with Cheetah and would +appreciate any contributions. + +<P> + +<H3>Test cases</A> +</H3> +Cheetah is packaged with a regression testing suite that is run with each +new release to ensure that everything is working as expected and that recent +changes haven't broken anything. The test cases are in the Cheetah.Tests +module. If you find a reproduceable bug please consider writing a test case +that will pass only when the bug is fixed. Send any new test cases to the email +list with the subject-line ``new test case for Cheetah.'' + +<P> + +<H3>Publicity</A> +</H3> +Help spread the word ... recommend it to others, write articles about it, etc. + +<P> + +#end redefine mainContentsCell +##============================================================================## diff --git a/examples/webware_examples/cheetahSite/download.tmpl b/examples/webware_examples/cheetahSite/download.tmpl new file mode 100644 index 0000000..688f18e --- /dev/null +++ b/examples/webware_examples/cheetahSite/download.tmpl @@ -0,0 +1,33 @@ +#extend SiteTemplate + +##============================================================================## +#data + +#end data +##============================================================================## + + +##============================================================================## +## from WebwareSiteTemplate +#redefine locationBarContents + <A CLASS="locationBar" HREF="index.html">Home</A> > + <A CLASS="locationBar" HREF="download.html">Download Cheetah</A> +#end redefine locationBarContents +## from WebwareSiteTemplate +##============================================================================## + + +##============================================================================## +## from WebwareSiteTemplate +#redefine mainContentsCell + +<P> +<A HREF="Downloads/Cheetah-LATEST.tar.gz">Here</A> is the current Cheetah +release (0.9.6). + +<P> +And <A HREF="Downloads/">here</A> are all the recent Cheetah releases. + +#end redefine mainContentsCell +## from WebwareSiteTemplate +##============================================================================## diff --git a/examples/webware_examples/cheetahSite/index.html b/examples/webware_examples/cheetahSite/index.html new file mode 100644 index 0000000..3af64b7 --- /dev/null +++ b/examples/webware_examples/cheetahSite/index.html @@ -0,0 +1,248 @@ + +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML> +<!-- This document was autogenerated by Cheetah. Don't edit it directly! + +Copyright 2001 - The Cheetah Development Team - All Rights Reserved. +Feel free to copy any javascript or html you like on this site, +provided you remove all links and/or references to cheetahtemplate.sourceforge.net +However, please do not copy any content or images without permission. + +Designed & Implemented by Tavis Rudd + +--> + + + +<HEAD> +<TITLE>Cheetah - The Python-Powered Template Engine</TITLE> + +<STYLE TYPE="text/css"><!-- + +H1.mainText {font-family: helvetica, arial, "lucida sans", sans-serif; margin: 7px 2px; font-weight: bold; font-size: small} +P, .mainText {margin-left: 10px} +.navBarContainer {color: white; padding: 4px 0px} +.navBar, A.navBar:link, A.navBar:visited {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-weight: bold; font-size: small; color: white} +A.navBar:hover, A.navBar:active {text-decoration: underline} +.locationBarContainer {padding: 2px 10px} +.locationBar, A.locationBar:link, A.locationBar:visited {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-size: x-small; color: #666666} +A.locationBar:hover, A.locationBar:active {text-decoration: underline} +.copyrightFooter {font-size: 9pt; color: white} +.insetBoxTitleContainer {padding: 2px 5px; font-weight: bold; color: white} +.insetBoxTitle {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-weight: bold; font-size: x-small; color: white} +.insetBoxContainer {padding: 2px 2px 2px 5px} +.insetBox {font-family: helvetica, arial, "lucida sans", sans-serif; font-size: x-small} +H1.insetBox {font-family: helvetica, arial, "lucida sans", sans-serif; margin: 5px 5px; font-weight: bold; font-size: x-small; padding: 0px} +P.insetBox {margin: 2px 5px 7px 10px; font-family: helvetica, arial, "lucida sans", sans-serif; padding: 0px} +A.insetBox:link, A.insetBox:visited {text-decoration: none; font-size: x-small} +A.insetBox:hover, A.insetBox:active {text-decoration: underline} +.listItems {margin-bottom: 200px} +//--></STYLE> + +</HEAD> + + + + +<BODY BGCOLOR="black" TEXT="#000033"> + + + + + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD> + <DIV ALIGN="center"><IMG SRC="cheetah-face-black-medium.jpg" + ALT=""><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT=""><IMG SRC="banner_text.gif" + ALT="Cheetah: The Python-Powered Template Engine"></DIV> + + </TD> + </TR> + <TR> + <TD WIDTH=1 HEIGHT=1 bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=900> + + <TABLE WIDTH=100% BGCOLOR="#3366CC" BORDER=0 CELLPADDING=0 CELLSPACING=0> + <TR> + <TD> + + <DIV ALIGN="center" CLASS="navBarContainer"> + <A CLASS="navBar" HREF="index.html">Home</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT=""> + <A CLASS="navBar" HREF="learn.html">Learn</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT=""> + <A CLASS="navBar" HREF="download.html">Download</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT=""> + <A CLASS="navBar" HREF="contribute.html">Contribute</A> + </DIV> + </TD> + </TR> + </TABLE> + + </TD> + <TD WIDTH="1" bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=900 HEIGHT=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=100 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + + + +<TABLE ALIGN=center VALIGN="top" BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + + <TD BGCOLOR="white"> + + + + + + + <IMG SRC="spacer.gif" WIDTH=1 HEIGHT=25 ALT=""> + + <TABLE VALIGN="top" WIDTH=100% BORDER=0 CELLPADDING=10 CELLSPACING=0> + <TR> + <TD VALIGN="top"> + <BR> + <a href=http://lists.sourceforge.net/mailman/listinfo/cheetahtemplate-discuss><B>Mailing + List</B></a><BR> + <A HREF="http://sourceforge.net/cvs/?group_id=28961"><B>CVS</B></A><BR> + + </TD> + <TD VALIGN="top"> + +<P> +<B>Cheetah</B> is a <A HREF="http://www.python.org">Python</A>-powered template engine and code-generator. It aims to: +<ul> + <li> make it easy to separate content, graphic design, and program code. + <li> make it easy to integrate content, graphic design, and program code. + <li> provide template designers with a small set of <i>display + logic</i> programming structures such as conditional blocks and for loops + <li> be equally well-suited for any text format including HTML, + SGML, XML, SQL, Postscript, e-mail and LaTeX + <li> achieve all these aims in a manner that is efficient, flexible, and extendable. +</ul> +</P> + + +<P> +<B>Cheetah</B> achieves these aims by: + +<UL> +<LI>blending the power and flexibility of Python with the simplicity of a + small Template Definition language that non-programmers can understand. +<LI>giving template designers a simple way of accessing Python variables, + objects, and functions in their templates. + +<LI>providing a modular, object-orientated framework that makes it easy to + create and maintain large websites. + +<LI>compiling 'Template Definitions' into native Python code at startup. + Thereafter this code is executed for each request. This approach is + dramatically faster than the string substitution approach used by many + templating engines. + +</LI> +<LI>providing a very simple, yet powerful, caching mechanism that can + significantly increase the responsiveness of a dynamic website. +</LI> +</UL> + +<P><B>Cheetah</B> requires <A HREF="http://www.python.org">Python</A> 2.0 or greater. It can be used from ordinary Python +programs, but also provides integration with <a +href=http://webware.sourceforge.net/>Webware for Python</a>.</P> + +<P><B>Cheetah</B> is Open Source Software, released under the terms of the Python +License. + +<P><B>Cheetah</B> is alpha software as some aspects of its design are still subject to +change. We plan to release a stable version later this summer. + +<P> The discussion list is +<a href=mailto:cheetahtemplate-discuss@lists.sourceforge.net +>cheetahtemplate-discuss@lists.sourceforge.net</a>. +You can join <a href=http://lists.sourceforge.net/mailman/listinfo/cheetahtemplate-discuss>here</a> +and view the archives <a +href=http://www.geocrawler.com/redir-sf.php3?list=cheetahtemplate-discuss>here</a>. +</P> + + + </TD> + </TR> + </TABLE> + + <IMG SRC="spacer.gif" WIDTH=1 HEIGHT=200 ALT=""> + </TD> + + <TD WIDTH=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 BGCOLOR="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=900 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=100 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=900 BGCOLOR="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=25 ALT=""></TD> + <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 HEIGHT=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> + <TR> + <TD> + <DIV CLASS="copyrightFooter"> + Copyright © 2001 The Cheetah Development Team. All rights reserved. + </DIV> + <BR> + + <DIV CLASS="copyrightFooter" ALIGN="center">Hosted on<BR> + <a href=http://sourceforge.net><img + src=http://sourceforge.net/sflogo.php?group_id=28961&type=1" + alt="SourceForge Home" height=31 width=88 border=0></a> + </DIV> + + </TD> + </TR> +</TABLE> + + +<BR> +<BR> +<BR> + + + + +</BODY> +</HTML> diff --git a/examples/webware_examples/cheetahSite/index.tmpl b/examples/webware_examples/cheetahSite/index.tmpl new file mode 100644 index 0000000..d573a07 --- /dev/null +++ b/examples/webware_examples/cheetahSite/index.tmpl @@ -0,0 +1,82 @@ +#extend SiteTemplate + +################### +#redefine locationBar + +#end redefine locationBar +################### + + +##============================================================================## +## from SiteTemplate +#redefine leftSideBar + <BR> + <a href=http://lists.sourceforge.net/mailman/listinfo/cheetahtemplate-discuss><B>Mailing + List</B></a><BR> + <A HREF="http://sourceforge.net/cvs/?group_id=28961"><B>CVS</B></A><BR> +#end redefine leftSideBar +##============================================================================## + + +######################## +#redefine mainContentsCell + +##<H1 CLASS="mainText">What is Cheetah?</H1> +<P> +<B>Cheetah</B> is a #Python()-powered template engine and code-generator. It aims to: +<ul> + <li> make it easy to separate content, graphic design, and program code. + <li> make it easy to integrate content, graphic design, and program code. + <li> provide template designers with a small set of <i>display + logic</i> programming structures such as conditional blocks and for loops + <li> be equally well-suited for any text format including HTML, + SGML, XML, SQL, Postscript, e-mail and LaTeX + <li> achieve all these aims in a manner that is efficient, flexible, and extendable. +</ul> +</P> + + +<P> +<B>Cheetah</B> achieves these aims by: + +<UL> +<LI>blending the power and flexibility of Python with the simplicity of a + small Template Definition language that non-programmers can understand. +<LI>giving template designers a simple way of accessing Python variables, + objects, and functions in their templates. + +<LI>providing a modular, object-orientated framework that makes it easy to + create and maintain large websites. + +<LI>compiling 'Template Definitions' into native Python code at startup. + Thereafter this code is executed for each request. This approach is + dramatically faster than the string substitution approach used by many + templating engines. + +</LI> +<LI>providing a very simple, yet powerful, caching mechanism that can + significantly increase the responsiveness of a dynamic website. +</LI> +</UL> + +<P><B>Cheetah</B> requires #Python() 2.0 or greater. It can be used from ordinary Python +programs, but also provides integration with <a +href=http://webware.sourceforge.net/>Webware for Python</a>.</P> + +<P><B>Cheetah</B> is Open Source Software, released under the terms of the Python +License. + +<P><B>Cheetah</B> is alpha software as some aspects of its design are still subject to +change. We plan to release a stable version later this summer. + +<P> The discussion list is +<a href=mailto:cheetahtemplate-discuss@lists.sourceforge.net +>cheetahtemplate-discuss@lists.sourceforge.net</a>. +You can join <a href=http://lists.sourceforge.net/mailman/listinfo/cheetahtemplate-discuss>here</a> +and view the archives <a +href=http://www.geocrawler.com/redir-sf.php3?list=cheetahtemplate-discuss>here</a>. +</P> + + +#end redefine mainContentsCell +######################## diff --git a/examples/webware_examples/cheetahSite/learn.html b/examples/webware_examples/cheetahSite/learn.html new file mode 100644 index 0000000..f6c907e --- /dev/null +++ b/examples/webware_examples/cheetahSite/learn.html @@ -0,0 +1,1668 @@ + +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<HTML> +<!-- This document was autogenerated by Cheetah. Don't edit it directly! + +Copyright 2001 - The Cheetah Development Team - All Rights Reserved. +Feel free to copy any javascript or html you like on this site, +provided you remove all links and/or references to cheetahtemplate.sourceforge.net +However, please do not copy any content or images without permission. + +Designed & Implemented by Tavis Rudd + +--> + + + +<HEAD> +<TITLE>Cheetah - The Python-Powered Template Engine</TITLE> + +<STYLE TYPE="text/css"><!-- + +H1.mainText {font-family: helvetica, arial, "lucida sans", sans-serif; margin: 7px 2px; font-weight: bold; font-size: small} +P, .mainText {margin-left: 10px} +.navBarContainer {color: white; padding: 4px 0px} +.navBar, A.navBar:link, A.navBar:visited {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-weight: bold; font-size: small; color: white} +A.navBar:hover, A.navBar:active {text-decoration: underline} +.locationBarContainer {padding: 2px 10px} +.locationBar, A.locationBar:link, A.locationBar:visited {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-size: x-small; color: #666666} +A.locationBar:hover, A.locationBar:active {text-decoration: underline} +.copyrightFooter {font-size: 9pt; color: white} +.insetBoxTitleContainer {padding: 2px 5px; font-weight: bold; color: white} +.insetBoxTitle {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-weight: bold; font-size: x-small; color: white} +.insetBoxContainer {padding: 2px 2px 2px 5px} +.insetBox {font-family: helvetica, arial, "lucida sans", sans-serif; font-size: x-small} +H1.insetBox {font-family: helvetica, arial, "lucida sans", sans-serif; margin: 5px 5px; font-weight: bold; font-size: x-small; padding: 0px} +P.insetBox {margin: 2px 5px 7px 10px; font-family: helvetica, arial, "lucida sans", sans-serif; padding: 0px} +A.insetBox:link, A.insetBox:visited {text-decoration: none; font-size: x-small} +A.insetBox:hover, A.insetBox:active {text-decoration: underline} +.listItems {margin-bottom: 200px} +//--></STYLE> + +</HEAD> + + + + +<BODY BGCOLOR="black" TEXT="#000033"> + + + + + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD> + <DIV ALIGN="center"><IMG SRC="cheetah-face-black-medium.jpg" + ALT=""><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT=""><IMG SRC="banner_text.gif" + ALT="Cheetah: The Python-Powered Template Engine"></DIV> + + </TD> + </TR> + <TR> + <TD WIDTH=1 HEIGHT=1 bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=900> + + <TABLE WIDTH=100% BGCOLOR="#3366CC" BORDER=0 CELLPADDING=0 CELLSPACING=0> + <TR> + <TD> + + <DIV ALIGN="center" CLASS="navBarContainer"> + <A CLASS="navBar" HREF="index.html">Home</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT=""> + <A CLASS="navBar" HREF="learn.html">Learn</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT=""> + <A CLASS="navBar" HREF="download.html">Download</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT=""> + <A CLASS="navBar" HREF="contribute.html">Contribute</A> + </DIV> + </TD> + </TR> + </TABLE> + + </TD> + <TD WIDTH="1" bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=900 HEIGHT=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=100 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + + + +<TABLE ALIGN=center VALIGN="top" BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + + <TD BGCOLOR="white"> + + <TABLE WIDTH=100% BGCOLOR="#EBEBEB" BORDER=0 CELLPADDING=0 CELLSPACING=0> + <TR> + <TD> + <DIV CLASS="locationBarContainer"> + <A CLASS="locationBar" HREF="index.html">Home</A> > + <A CLASS="locationBar" HREF="learn.html">Learn</A> + </DIV> + </TD> + </TR> + <TR> + <TD HEIGHT=1 BGCOLOR="#CCCCCC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> + </TABLE> + + + + + + <IMG SRC="spacer.gif" WIDTH=1 HEIGHT=25 ALT=""> + + <TABLE VALIGN="top" WIDTH=100% BORDER=0 CELLPADDING=10 CELLSPACING=0> + <TR> + <TD VALIGN="top"> + <BR> + <A HREF="Downloads/users_guide.pdf"><B>PDF</B></A><BR> + <A HREF="Downloads/users_guide.ps"><B>PostScript</B></A><BR> + <A HREF="Downloads/users_guide.txt"><B>Plain Text</B></A><BR> + + </TD> + <TD VALIGN="top"> + +<div class="titlepage"> +<center> +<h1>Cheetah Users Guide</h1> +<p><b><font size='+2'>The Cheetah Development Team</font></b></p> +<p><span class="email">cheetahtemplate-devel@lists.sourceforge.net</span></p> +<p> +</center> +</div> + +<P> +<BR> +<BR><H2><A NAME="SECTION000100000000000000000"> +Contents</A> +</H2> +<!--Table of Contents--> + +<UL CLASS="TofC"> +<LI><A NAME="tex2html2" + HREF="users_guide.html">1 Introduction</A> +<UL> +<LI><A NAME="tex2html3" + HREF="#SECTION000210000000000000000">1.1 What is Cheetah?</A> +<LI><A NAME="tex2html4" + HREF="#SECTION000220000000000000000">1.2 Why is it called Cheetah?</A> +<LI><A NAME="tex2html5" + HREF="#SECTION000230000000000000000">1.3 Who developed Cheetah?</A> +<LI><A NAME="tex2html6" + HREF="#SECTION000240000000000000000">1.4 How mature is Cheetah?</A> +<LI><A NAME="tex2html7" + HREF="#SECTION000250000000000000000">1.5 Where can I get releases?</A> +<LI><A NAME="tex2html8" + HREF="#SECTION000260000000000000000">1.6 Where can I get news?</A> +<LI><A NAME="tex2html9" + HREF="#SECTION000270000000000000000">1.7 How can I contribute?</A> +<LI><A NAME="tex2html10" + HREF="#SECTION000280000000000000000">1.8 Acknowledgements</A> +<LI><A NAME="tex2html11" + HREF="#SECTION000290000000000000000">1.9 License</A> +</UL> +<BR> +<LI><A NAME="tex2html12" + HREF="#SECTION000300000000000000000">2 Getting Started</A> +<UL> +<LI><A NAME="tex2html13" + HREF="#SECTION000310000000000000000">2.1 Requirements</A> +<LI><A NAME="tex2html14" + HREF="#SECTION000320000000000000000">2.2 Installation</A> +<LI><A NAME="tex2html15" + HREF="#SECTION000330000000000000000">2.3 Testing your installation</A> +<LI><A NAME="tex2html16" + HREF="#SECTION000340000000000000000">2.4 Quickstart tutorial</A> +</UL> +<BR> +<LI><A NAME="tex2html17" + HREF="#SECTION000400000000000000000">3 Template Objects</A> +<UL> +<LI><A NAME="tex2html18" + HREF="#SECTION000410000000000000000">3.1 The Template class</A> +<LI><A NAME="tex2html19" + HREF="#SECTION000420000000000000000">3.2 Constructing Template objects</A> +<LI><A NAME="tex2html20" + HREF="#SECTION000430000000000000000">3.3 Using Template objects</A> +<LI><A NAME="tex2html21" + HREF="#SECTION000440000000000000000">3.4 Modifying Template objects</A> +</UL> +<BR> +<LI><A NAME="tex2html22" + HREF="#SECTION000500000000000000000">4 The Template Definition Language</A> +<UL> +<LI><A NAME="tex2html23" + HREF="#SECTION000510000000000000000">4.1 Placeholder tags</A> +<LI><A NAME="tex2html24" + HREF="#SECTION000520000000000000000">4.2 Directive tags</A> +<LI><A NAME="tex2html25" + HREF="#SECTION000530000000000000000">4.3 Comment directives</A> +<LI><A NAME="tex2html26" + HREF="#SECTION000540000000000000000">4.4 #raw directives</A> +<LI><A NAME="tex2html27" + HREF="#SECTION000550000000000000000">4.5 #include directives</A> +<LI><A NAME="tex2html28" + HREF="#SECTION000560000000000000000">4.6 #cache directives</A> +<LI><A NAME="tex2html29" + HREF="#SECTION000570000000000000000">4.7 Display logic directives</A> +<LI><A NAME="tex2html30" + HREF="#SECTION000580000000000000000">4.8 #block directives</A> +</UL> +<BR> +<LI><A NAME="tex2html31" + HREF="#SECTION000600000000000000000">5 Macros</A> +<UL> +<LI><A NAME="tex2html32" + HREF="#SECTION000610000000000000000">5.1 What are macros?</A> +<LI><A NAME="tex2html33" + HREF="#SECTION000620000000000000000">5.2 Using macros</A> +<LI><A NAME="tex2html34" + HREF="#SECTION000630000000000000000">5.3 Defining macros</A> +<LI><A NAME="tex2html35" + HREF="#SECTION000640000000000000000">5.4 Macro libraries</A> +</UL> +<BR> +<LI><A NAME="tex2html36" + HREF="#SECTION000700000000000000000">6 Using Cheetah with Webware</A> +<UL> +<LI><A NAME="tex2html37" + HREF="#SECTION000710000000000000000">6.1 Background</A> +<LI><A NAME="tex2html38" + HREF="#SECTION000720000000000000000">6.2 Using the Inheritance approach</A> +<LI><A NAME="tex2html39" + HREF="#SECTION000730000000000000000">6.3 Using the Containment approach</A> +<LI><A NAME="tex2html40" + HREF="#SECTION000740000000000000000">6.4 User interaction in either approach</A> +<LI><A NAME="tex2html41" + HREF="#SECTION000750000000000000000">6.5 Components</A> +</UL> +<BR> +<LI><A NAME="tex2html42" + HREF="#SECTION000800000000000000000">7 Customizing and extending Cheetah</A> +<UL> +<LI><A NAME="tex2html43" + HREF="#SECTION000810000000000000000">7.1 Custom handling of unknown placeholder names</A> +<LI><A NAME="tex2html44" + HREF="#SECTION000820000000000000000">7.2 Plugins</A> +<LI><A NAME="tex2html45" + HREF="#SECTION000830000000000000000">7.3 Custom variable-tags</A> +<LI><A NAME="tex2html46" + HREF="#SECTION000840000000000000000">7.4 Custom directives</A> +<LI><A NAME="tex2html47" + HREF="#SECTION000850000000000000000">7.5 Custom error handlers</A> +<LI><A NAME="tex2html48" + HREF="#SECTION000860000000000000000">7.6 Safe delegation</A> +</UL> +<BR> +<LI><A NAME="tex2html49" + HREF="#SECTION000900000000000000000">8 Examples</A> +<UL> +<LI><A NAME="tex2html50" + HREF="#SECTION000910000000000000000">8.1 Syntax examples</A> +<LI><A NAME="tex2html51" + HREF="#SECTION000920000000000000000">8.2 Webware Examples</A> +</UL> +<BR> +<LI><A NAME="tex2html52" + HREF="#SECTION0001000000000000000000">About this document ...</A> +</UL> +<!--End of Table of Contents--> +<P> +©Copyright 2001, The Cheetah Development Team. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.1 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + +<P> + +<H1><A NAME="SECTION000200000000000000000"> </A> +<BR> +1 Introduction +</H1> + +<P> + +<H2><A NAME="SECTION000210000000000000000"> </A> +<BR> +1.1 What is Cheetah? +</H2> + +<P> +Cheetah is a Python-based template engine and code-generator. It aims: + +<P> + +<UL> +<LI><B>to make it easy to separate content, graphic design, and program code.</B> + +<P> +Program code should not pollute HTML and HTML should not pollute program + code. Nor should content pollute the structure of complex HTML designs and + vice versa. + +<P> +There should be no need for a designer to work through a programmer to + change a website's design or to use dynamic components that have already + been coded. Likewise, content-providers should not have to work through a + 'webmaster' to add new content to a site. All members of the team should + be able to work independently and in parallel. + +<P> +A clean separation makes it easier for a team of content-providers, + designers, and programmers to work together without stepping on each + other's toes and polluting each other's work. Other advantages include + faster development time; HTML and program code that are easier to + understand and maintain; content that can be displayed in a variety of + non-HTML formats such as PDF; and highly modular, flexible, and reusable + site architectures. + +<P> +</LI> +<LI><B>to make it easy to integrate content, graphic design, and program code.</B> + +<P> +While it should be easy to develop content, graphic design, and program + code separately, it should NOT be difficult to integrate them as part of a + website. There should be no difficult hoops to jump through. + +<P> +It should be easy: + +<UL> +<LI>for programmers to create reusable components and functions that are + accessible and understandable to designers. +</LI> +<LI>for designers to mark out placeholders for content and dynamic components + in their templates. +</LI> +<LI>for designers to soft-code aspects of their design that are either + repeated in several places or are subject to change. +</LI> +<LI>for designers to extend and customize existing templates and thus minimize + duplication of effort and code. +</LI> +<LI>and, of course, for content-providers to use the templates that + designers have created. + +</LI> +</UL> + +<P> +</LI> +<LI><B>to provide template designers with a small set of 'Display Logic' + programming structures such as conditional blocks and + for loops</B> + +<P> +Graphic designers often do tasks that would be easier, faster, and less + error prone if they had access to <B>conditional blocks</B> and <B>for + loops</B>. However, a full programming language would be overkill for these + simple tasks and most designers don't have the time or desire to learn one. + +<P> +</LI> +<LI><B>to be equally well-suited for HTML, SGML, XML, SQL, Postscript, form + email, LaTeX, or any other text-based format.</B> + +<P> +Although it was designed with dynamic websites and web applications in mind, + Cheetah is not HTML-specific. + +<P> +</LI> +<LI><B>to achieve all these aims in a manner that is efficient, flexible, and + extendable.</B> + +<P> +</LI> +</UL> + +<P> +Cheetah achieves these aims by: + +<P> + +<UL> +<LI>blending the power and flexibility of Python with the simplicity of a + small Template Definition language that non-programmers can understand. + +<P> +</LI> +<LI>giving template designers a simple way of accessing Python variables, + objects, and functions in their templates. + +<P> +</LI> +<LI>providing a modular, object-orientated framework that makes it easy to + create and maintain large websites. + +<P> +</LI> +<LI>compiling 'Template Definitions' into native Python code at startup. + Thereafter this code is executed for each request. This approach is + dramatically faster than the string substitution approach used by many + templating engines. + +<P> +</LI> +<LI>providing a very simple, yet powerful, caching mechanism that can + significantly increase the responsiveness of a dynamic website. + +<P> +</LI> +</UL> + +<P> + +<H2><A NAME="SECTION000220000000000000000"> </A> +<BR> +1.2 Why is it called Cheetah? +</H2> + +<P> +Cheetah is fast, flexible, agile and graceful - like its namesake. + +<P> + +<H2><A NAME="SECTION000230000000000000000"> </A> +<BR> +1.3 Who developed Cheetah? +</H2> + +<P> +Cheetah is one of several templating frameworks that grew out of a 'templates' +thread on the 'Webware For Python' email list. Tavis Rudd, Mike Orr, Chuck +Esterbrook, Ian Bicking and Tom Schwaller are the core developers. + +<P> + +<H2><A NAME="SECTION000240000000000000000"> </A> +<BR> +1.4 How mature is Cheetah? +</H2> + +<P> +Cheetah is alpha/beta software as this User's Guide is incomplete and several aspects +of the design are still subject to change. However, it has been tested +extensively and has few known issues. We are hoping to release production +version 1.0 in the summer of 2001. + +<P> +Here's a summary of known issues and aspects of the design that are in flux. + +<UL> +<LI>The #include directive is not working with relative path file includes + when used with Webware. This should be resolved soon. +</LI> +<LI>The #include directive might be reworked to monitor for changes in the + included file at run-time. It currently does the include once-off at + compile-time. +</LI> +<LI>The implementation of $placeholders(WithArgstrings) needs to be fleshed + out to handle nesting. +</LI> +</UL> + +<P> + +<H2><A NAME="SECTION000250000000000000000"> </A> +<BR> +1.5 Where can I get releases? +</H2> + +<P> +Cheetah releases can be downloaded from +<a class="url" href="http://CheetahTemplate.sourceforge.net">http://CheetahTemplate.sourceforge.net</a> +<P> + +<H2><A NAME="SECTION000260000000000000000"> </A> +<BR> +1.6 Where can I get news? +</H2> + +<P> +News and updates can be obtained from the the Cheetah website: +<a class="url" href="http://CheetahTemplate.sourceforge.net">http://CheetahTemplate.sourceforge.net</a> +<P> +Cheetah discussions take place on the list +<span class="email">cheetahtemplate-discuss@lists.sourceforge.net</span>. + +<P> +If you encounter difficulties, or are unsure about how to do something, +please post a detailed message to the list. + +<P> + +<H2><A NAME="SECTION000270000000000000000"> </A> +<BR> +1.7 How can I contribute? +</H2> + +<P> +Cheetah is the work of many volunteers. If you use Cheetah please share your +experiences, tricks, customizations, and frustrations. + +<P> + +<H3><A NAME="SECTION000271000000000000000"> +1.7.1 Bug reports and patches</A> +</H3> + +<P> +If you think there is a bug in Cheetah, send a message to the email list +with the following information: + +<P> + +<OL> +<LI>a description of what you were trying to do and what happened +</LI> +<LI>all tracebacks and error output +</LI> +<LI>your version of Cheetah +</LI> +<LI>your version of Python +</LI> +<LI>your operating system +</LI> +<LI>whether you have changed anything in the Cheetah installation +</LI> +</OL> + +<P> + +<H3><A NAME="SECTION000272000000000000000"> +1.7.2 Example sites and tutorials</A> +</H3> +If you're developing a website with Cheetah, please send a link to the +email list so we can keep track of Cheetah sites. Also, if you discover +new and interesting ways to use Cheetah please share your experience and +write a quick tutorial about your technique. + +<P> + +<H3><A NAME="SECTION000273000000000000000"> +1.7.3 Macro libraries</A> +</H3> +We hope to build up a framework of macros libraries (see section +<A HREF="users_guide.html#macros.libraries">5.4</A>) to distribute with Cheetah and would appreciate +any contributions. + +<P> + +<H3><A NAME="SECTION000274000000000000000"> +1.7.4 Test cases</A> +</H3> +Cheetah is packaged with a regression testing suite that is run with each +new release to ensure that everything is working as expected and that recent +changes haven't broken anything. The test cases are in the Cheetah.Tests +module. If you find a reproduceable bug please consider writing a test case +that will pass only when the bug is fixed. Send any new test cases to the email +list with the subject-line ``new test case for Cheetah.'' + +<P> + +<H3><A NAME="SECTION000275000000000000000"> +1.7.5 Publicity</A> +</H3> +Help spread the word ... recommend it to others, write articles about it, etc. + +<P> + +<H2><A NAME="SECTION000280000000000000000"> </A> +<BR> +1.8 Acknowledgements +</H2> + +<P> +We'd like to thank the following people for contributing valuable advice, code +and encouragement: Geoff Talvola, Jay Love, Terrel Shumway, Sasa Zivkov, Arkaitz +Bitorika, Jeremiah Bellomy, Baruch Even, Paul Boddie, Stephan Diehl, and Geir +Magnusson. + +<P> +The Velocity, WebMacro and Smarty projects provided inspiration and design +ideas. Cheetah has benefited from the creativity and energy of their +developers. Thank you. + +<P> + +<H2><A NAME="SECTION000290000000000000000"> </A> +<BR> +1.9 License +</H2> + +<P> +Cheetah is released for unlimited distribution under the terms of the +Python license. + +<H1><A NAME="SECTION000300000000000000000"> </A> +<BR> +2 Getting Started +</H1> + +<P> + +<H2><A NAME="SECTION000310000000000000000"> +2.1 Requirements</A> +</H2> +Cheetah requires Python release 2.0 or greater and should run on any +operating system that Python 2.0 runs on. + +<P> + +<H2><A NAME="SECTION000320000000000000000"> +2.2 Installation</A> +</H2> + +<P> +To install Cheetah for a single user: + +<OL> +<LI>copy the 'Cheetah' sub-directory to a location that is in the user's + PYTHON_PATH +</LI> +</OL> + +<P> +To install Cheetah for system-wide use: + +<OL> +<LI>on POSIX systems (AIX, Solaris, Linux, IRIX, etc.) become the 'root' user + and run: python ./setup.py install + +<P> +</LI> +<LI>On non-POSIX systems, such as Windows NT, login as an administrator and + type this at the command-line: python setup.py install +</LI> +</OL> + +<P> +On POSIX systems, the system-wide installation will also install the Cheetah's +command-line compiler program, TScompile, to a system-wide executable path such as +/usr/local/bin. + +<P> + +<H2><A NAME="SECTION000330000000000000000"> +2.3 Testing your installation</A> +</H2> +You can run the test suite to insure that your installation is correct by +following these steps: + +<OL> +<LI>CD into the directory ./Cheetah +</LI> +<LI>type: <code>python Tests.py</code> +</LI> +</OL> + +<P> +If any of the tests fail please send a message to the email list with a copy of +the test output and the following details about your installation: + +<P> + +<OL> +<LI>your version of Cheetah +</LI> +<LI>your version of Python +</LI> +<LI>your operating system +</LI> +<LI>whether you have changed anything in the Cheetah installation +</LI> +</OL> + +<P> + +<H2><A NAME="SECTION000340000000000000000"> +2.4 Quickstart tutorial</A> +</H2> +This tutorial briefly introduces the basic usage of Cheetah. See the +following chapters for more detailed explanations. + +<P> +<B>This tutorial will be fleshed out further at later date.</B> + +<P> +The core of Cheetah is the <code>Template</code> class in the <code>Cheetah.Template</code> +module. The following example shows how to use the <code>Template</code> class from an +interactive Python session. Lines prefixed with <code>»></code> and <code>...</code> are +user input. The remaining lines are Python output. + +<P> +<dl><dd><pre class="verbatim"> +>>> from Cheetah.Template import Template +>>> templateDef = """ +... <HTML> +... <HEAD><TITLE>$title</TITLE></HEAD> +... <BODY> +... $contents +... </BODY> +... </HTML>""" +>>> nameSpace = {'title': 'Hello World Example', 'contents': 'Hello World!'} +>>> templateObj = Template(templateDef, nameSpace) +>>> print templateObj + +<HTML> +<HEAD><TITLE>Hello World Example</TITLE></HEAD> +<BODY> +Hello World! +</BODY> +</HTML> +>>> print templateObj # templateObj can be printed as many times as you need + +<HTML> +<HEAD><TITLE>Hello World Example</TITLE></HEAD> +<BODY> +Hello World! +</BODY> +</HTML> +</pre></dl> + +<P> + +<H1><A NAME="SECTION000400000000000000000"> </A> +<BR> +3 Template Objects +</H1> + +<P> + +<H2><A NAME="SECTION000410000000000000000"> +3.1 The Template class</A> +</H2> +The Template class is the heart of Cheetah. It parses and compiles +Template Definitions into python code and serves the filled template output to +any client that requests it. + +<P> + +<H2><A NAME="SECTION000420000000000000000"> +3.2 Constructing Template objects</A> +</H2> + +<P> + +<H2><A NAME="SECTION000430000000000000000"> +3.3 Using Template objects</A> +</H2> + +<P> + +<H2><A NAME="SECTION000440000000000000000"> +3.4 Modifying Template objects</A> +</H2> + +<P> + +<H1><A NAME="SECTION000500000000000000000"> +4 The Template Definition Language</A> +</H1> + +<P> +<B>Template definitions</B> are text strings, or files, that have been marked up +with Cheetah tags for special processing. Cheetah has 2 types of tags: + +<P> + +<OL> +<LI><B>placeholders</B>: for marking areas of the template that should be + replaced with something. + +<P> +Placeholders begin with a dollar sign (<code>$varName</code>). + +<P> +</LI> +<LI><B>directives</B>: for everything else: + + <OL> +<LI><B>raw text</B> for marking verbatim blocks should not be parsed for + Cheetah tags. +</LI> +<LI><B>comments</B> that should not appear in the output +</LI> +<LI><B>includes</B> to include external text. The text can be included + verbatim or with parsing for Cheetah tags. +</LI> +<LI><B>display logic</B> such as <B>conditional blocks</B> (if-blocks) and + <B>for loops</B> +</LI> +<LI><B>blocks</B>, which are named sections of a template that can be + redefined (overridden) in a subclass or by template users +</LI> +<LI>etc. + +</LI> +</OL> +<P> +Directives begin with a hash character (#). + +<P> +</LI> +</OL> + +<P> + +<H2><A NAME="SECTION000510000000000000000"> +4.1 Placeholder tags</A> +</H2> +Cheetah uses placeholder tags in the form <code>$varName</code> to mark out +areas of the template definition that should be replaced with something. Placeholders are +equivalent to <B>fields</B> on a form. Placeholders can be replaced with plain +content or variables, objects and function output from Python. + +<P> +The example below demonstrates the use of placeholders in an HTML document. + +<P> +<dl><dd><pre class="verbatim"> +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> +$contents +</BODY> +</HTML> +</pre></dl> + +<P> +When this template is filled in, the placeholders <code>$title</code> and +<code>$content</code> will be replaced with the values of the variables <code>title</code> +and <code>content</code>. + +<P> + +<H3><A NAME="SECTION000511000000000000000"> +4.1.1 Rules for placeholder names</A> +</H3> + +<UL> +<LI>Cheetah ignores all dollar signs (<code>$</code>) that are not followed + by a letter or an underscore. As a corollary, dollar amounts + (<code>$2.50</code>)are not placeholders but are instead output literally as + they should be. Cheetah also ignores any placeholder escaped by a + backslash (<code><SPAN CLASS="MATH"><IMG + WIDTH="12" HEIGHT="31" ALIGN="MIDDLE" BORDER="0" + SRC="img1.gif" + ALT="$ackslash$"></SPAN>$placeholderName</code>). + +<P> +</LI> +<LI>The first character of a placeholder name must be either an underscore or + a letter. Valid characters for the rest of the name are underscores, + letters, numbers and periods. These names are valid: <code>$a</code>, + <code>$_</code>, <code>$var</code>, <code>$_var</code>, <code>$var1</code>, <code>$_1var</code>, + <code>$var2_</code>, <code>$dict.key</code>, <code>$list.item</code>, + <code>$object.method</code>. These names are not: <code>$1</code>, <code>$var@2</code>, + <code>$var</code>. + +<P> +</LI> +<LI>names are case-sensitive. <code>$var</code> does not equal <code>$Var</code> + or <code>$vAr</code> or <code>$VAR</code>. + +<P> +</LI> +<LI>Trailing periods are ignored. Cheetah will recognize that + placeholder name in <code>$varName.</code> is <code>varName</code> and the period will + be left alone in the filled template output. + +<P> +</LI> +<LI>Placeholders can also be written in the form <code>${placeholderName}</code>. + This is useful for cases where there is no whitespace between the + placeholder and surrounding text (<code>surrounding${embeddedVar}text</code>). +</LI> +</UL> + +<P> + +<H3><A NAME="SECTION000512000000000000000"> +4.1.2 The searchList</A> +</H3> +A <B>namespace</B> is a Python dictionary that links names to values. Each +template definition that has been loaded into a <code>Template</code> objet is +associated with an ordered list of namespaces in which values for placeholder +variable names can be stored. This list is called the <B>searchList</B>. + +<P> +The searchList can contain one or more namespaces. In most cases only one +namespace will be in the searchList unless you explicitly load extra ones. When +Cheetah fills in <code>$content</code> in previous example it searchs +sequentially through the searchList until it finds a value for <code>$content</code>. +Thus, if three namespaces are loaded and two of them contain a value for +<code>$content</code>, the value for <code>content</code> from the namespace that is +closest to the start of the searchList will be returned. + +<P> +If you add a Python object to the searchList, its attributes and methods will be +accessible as placeholder names. For example, <code>myObject</code> contains +<code>myAttrib</code> and <code>myMethod</code>. If <code>myObject</code> is added to the +searchList, <code>$myAttrib</code> and <code>$myMethod</code> can be used as placeholder +names. + +<P> +The default namespace in every searchList is the <code>Template</code> object itself. +This means that any attributes or methods that are added to classes that inherit +from <code>Template</code> can be accessed in templates via <code>$placeholders</code>. New +namespaces can be added to the searchList at any time using the +<code>Template.addToSearchList()</code> method. See section <A HREF="users_guide.html#webware">6</A> for more +information on how to use namespaces and the searchList. + +<P> + +<H3><A NAME="SECTION000513000000000000000"> +4.1.3 Placeholder values</A> +</H3> + +<P> +Placeholder names can map to Python text strings, numbers, dictionaries, lists +(arrays), functions, objects, or even nested Cheetah templates. If the value is +not a String, Cheetah will call <code>str()</code> on it to obtain a string +representation that can be inserted into the template. But if the value is a +callable object (e.g., a function or a method), Cheetah will first call it and +then call <code>str()</code> on the result. You may include or omit the () after +callable placeholders: Cheetah will call it (without arguments) in either case. + +<P> +<dl><dd><pre class="verbatim"> +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> + +$aString ## these names are chosen to indicate what +$aNumber ## type of value they map to. They could be +$aList ## any valid placeholder name. +$aDictionary +$aFunction +$anObject + +</BODY> +</HTML> +</pre></dl> + +<P> +Note that you don't need to include () after <code>$aFunction</code>. Cheetah +will recognize callable variables like functions and methods, call them and +interpolate their return value in the filled template output. + +<P> +You cannot pass arguments to a function or method called this way. (Actually, +you can, but this feature may be removed soon, because it introduces too much +complexity that is better done in Python outside the template.) The function or +method must either accept no arguments, or all its arguments must have default +values. Macro calls (see the Macros section) can accept arguments, but we +aren't talking about macros here. + +<P> +Templates can be nested. It is valid to embed a placeholder name that maps to +another <code>Template</code> object. The filled output of the nested template will be +interpolated into the top-level template's output. There is no limit on the +depth of template nesting. + +<P> + +<H3><A NAME="SECTION000514000000000000000"> +4.1.4 Placeholders with no value defined</A> +</H3> +If there is no value defined in the searchList for a placeholder name, +Cheetah will search for an leading underscore version of the name. For +example, if it can't find <code>$varName</code> it will attempt to find +<code>$_varName</code>. If that fails, Cheetah will include the placeholder +tag verbatim in the filled template output. + +<P> +This behaviour can be customized and a default value can be set for names +that are not found. See section <A HREF="users_guide.html#customizing">7</A> for more details. + +<P> + +<H3><A NAME="SECTION000515000000000000000"> +4.1.5 Dotted notation</A> +</H3> +Placeholder names can also use <B>dotted notation</B> to access entries in +dictionaries, items in lists, and the attributes and methods of objects. +Cheetah uses a consistent dotted notation syntax to access the contents +of all types of containers. This is unlike Python, C++, Java, and other +languages where dotted notation can only be used to access the attributes and +methods of objects. <A NAME="tex2html1" + HREF="#foot343"><SUP>1</SUP></A> +<P> +<dl><dd><pre class="verbatim"> +$aDictionary.keyName ## must be a valid key of the dictionary +$aList.3 ## must be a valid index (0-based like in Python) +$anObject.attributeName ## must be a valid attribute name +$anObject.method ## must be a valid method name, + ## leave off the parentheses on method() +</pre></dl> + +<P> +Dotted notation can be used on nested containers of any depth. +<dl><dd><pre class="verbatim"> +$dict1.dict2.dict3.keyName ## nested dictionaries +$anObject.nestedDict.keyName ## if the object contains a dictionary +</pre></dl> + +<P> + +<H3><A NAME="SECTION000516000000000000000"> +4.1.6 Caching</A> +</H3> + +<P> +By default the value of each <code>$placeholder</code> is updated for each request. +If you want to statically cache the value of the <code>$placeholder</code> upon +startup, add an asterisk after the $ sign. <code>$var</code> becomes <code>$*var</code>. +See the section on the <code>#cache</code> directive below for more information. + +<P> +If you only need to update the value of the <code>$placeholders</code> at specific time intervals use +this form: <code>$variable</code> becomes <code>$*15*variable</code> , where 15 is the time interval in +minutes. The time interval can also be specified in fractions of a minute such +as <code>$*0.5*variable</code>. + +<P> +<dl><dd><pre class="verbatim"> +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> + +$var ## dynamic - will be reinterpolated for each request +$*var2 ## static - will be interpolated only once at start-up +$*5*var3 ## timed refresh - will be updated every 5 minutes. + +</BODY> +</HTML> +</pre></dl> + +<P> + +<H2><A NAME="SECTION000520000000000000000"> +4.2 Directive tags</A> +</H2> +Directives tags are used for all functionality that cannot be handled with +simple placeholders and are enclosed in <code>#</code> and <code>/#</code>. +Cheetah does not use HTML/XML style tags because they would be hard to +distinguish from real HTML tags and would not be visible in rendered HTML when +something goes wrong. + +<P> +Some directives consist of a single tag while others have <B>start</B> and +<B>end</B> tags that surround a chunk of text. End tags are written in the form +<code>#end nameOfTheDirective/#</code>. + +<P> + +<H3><A NAME="SECTION000521000000000000000"> +4.2.1 Escaping directives</A> +</H3> +Directives can be escaped by placing a backslash (<SPAN CLASS="MATH"><IMG + WIDTH="12" HEIGHT="31" ALIGN="MIDDLE" BORDER="0" + SRC="img1.gif" + ALT="$ackslash$"></SPAN>) before them. +Escaped directives will be printed verbatim. + +<P> + +<H3><A NAME="SECTION000522000000000000000"> +4.2.2 Tag closures: explicit and implicit</A> +</H3> +Directive tags can closed explicitly with <code>/#</code> or implicitly with the end +of the line if you're feeling lazy. +<dl><dd><pre class="verbatim"> +#block /# +Text in the contents area of the +block directive +#end block /# +</pre></dl> +or +<dl><dd><pre class="verbatim"> +#block +Text in the contents area of the +block directive +#end block +</pre></dl> + +<P> + +<H3><A NAME="SECTION000523000000000000000"> +4.2.3 Whitespace handling</A> +</H3> + +<P> + +<H2><A NAME="SECTION000530000000000000000"> +4.3 Comment directives</A> +</H2> + +<P> +Comment directives are used to mark notes, explanations, and decorative text +that should not appear in the output. There are two forms of the comment +directive: single-line and multi-line. + +<P> +All text in a template definition that lies between 2 hash characters +(<code>##</code>) and the end of the line is treated as a single-line comment and +will not show up in the output, unless the 2 hash characters are escaped with a +backslash. +<dl><dd><pre class="verbatim"> +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> +##==================================== a decorative comment +$contents ## an end-of-line comment +##==================================== +</BODY> +</HTML> +</pre></dl> + +<P> +Any text between <code>#*</code> and <code>*#</code> will be treated as a multi-line +comment. +<dl><dd><pre class="verbatim"> +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> +#* + Here is some multiline + comment text +*# +##==================================== a decorative comment +$contents ## an end-of-line comment +##==================================== +</BODY> +</HTML> +</pre></dl> + +<P> + +<H2><A NAME="SECTION000540000000000000000"> +4.4 #raw directives</A> +</H2> +Any section of a template definition that is delimeted by <code>#raw</code> and +<code>#end raw</code> will be printed verbatim without any parsing of +$placeholders or other directives. This can be very useful for debugging or +writing Cheetah examples and tutorials. + +<P> + +<H2><A NAME="SECTION000550000000000000000"> +4.5 #include directives</A> +</H2> + +<P> +<code>#include</code> directives are used to include text from outside the template +definition. The text can come from <code>$placeholder</code> variables or from +external files. The example below demonstrates use with <code>$placeholder</code> +variables. + +<P> +<dl><dd><pre class="verbatim"> +#include $myParseText +</pre></dl> + +<P> +This example demonstrates its use with external files. +<dl><dd><pre class="verbatim"> +#include "includeFileName.txt" +</pre></dl> + +<P> +By default, included text will be parsed for Cheetah tags. The keyword +<B>raw</B> can be used to mark the text for verbatim inclusion without any tag +parsing. + +<P> +<dl><dd><pre class="verbatim"> +#include raw $myParseText +#include raw "includeFileName.txt" +</pre></dl> + +<P> +<code>Template</code> uses its .getFileContents(fileName) method to locate the file to +be included. This method can be overriden in subclasses if you want to modify +or extend its behaviour. It is possible to implement the logic for getting +remote files such as <code>http://myserver.com/file.txt</code>. + +<P> + +<H2><A NAME="SECTION000560000000000000000"> +4.6 #cache directives</A> +</H2> + +<P> + +<H2><A NAME="SECTION000570000000000000000"> +4.7 Display logic directives</A> +</H2> + +<P> + +<H3><A NAME="SECTION000571000000000000000"> +4.7.1 Conditional blocks</A> +</H3> + +<P> + +<H3><A NAME="SECTION000572000000000000000"> +4.7.2 For loops</A> +</H3> + +<P> + +<H2><A NAME="SECTION000580000000000000000"> +4.8 #block directives</A> +</H2> + +<P> + +<H3><A NAME="SECTION000581000000000000000"> +4.8.1 #redefine directives</A> +</H3> + +<P> + +<H1><A NAME="SECTION000600000000000000000"> </A> +<BR> +5 Macros +</H1> + +<P> + +<H2><A NAME="SECTION000610000000000000000"> </A> +<BR> +5.1 What are macros? +</H2> + +<P> + +<H2><A NAME="SECTION000620000000000000000"> </A> +<BR> +5.2 Using macros +</H2> + +<P> + +<H2><A NAME="SECTION000630000000000000000"> </A> +<BR> +5.3 Defining macros +</H2> + +<P> + +<H2><A NAME="SECTION000640000000000000000"> </A> +<BR> +5.4 Macro libraries +</H2> + +<H1><A NAME="SECTION000700000000000000000"> </A> +<BR> +6 Using Cheetah with Webware +</H1> + +<P> + +<H2><A NAME="SECTION000710000000000000000"> +6.1 Background</A> +</H2> +Webware is a 'Python-Powered Internet Platform' that uses servlets in a manner +similar to Java servlets. 'WebKit' is the name of Webware's application server. +For more details please visit http://webware.sourceforge.net. + +<P> +As Cheetah's core is flexible there are many ways to use it with Webware +servlets. There are two broad categories: the Inheritance approach and +the Containment approach. In the Inheritance approach a servlet is created that +subclasses both the Template class and Webware's HTTPServlet class. The +Template instance IS the servlet and its .respond() method is +automatically called by WebKit for each request. All pre-request processing is +handled via Cheetah. + +<P> +In the Containment approach an instance of the Template class is wrapped +up inside of a Webware servlet class. Instances of the servlet class must +explicitly call the Template instance's .respond(), or .__str__(), method +for each request. In this case the servlet class can handle whatever +per-request processing needs to be done before it calls Cheetah.respond(). + +<P> +The Inheritance approach is the simplest and is best suited for building sites +from scratch. + +<P> +The Containment approach is slightly more complex and is best suited for use +with existing Webware servlets. It is also ideal for cases where you wish to +use Cheetah for only a portion of the servlet's output, such as a +discussion-forum table at the bottom of a webpage. + +<P> + +<H2><A NAME="SECTION000720000000000000000"> +6.2 Using the Inheritance approach</A> +</H2> +Cheetah.Servlet provides a servlet class that can be subclassed to create +Webware servlets, as in the trivial example below. + +<P> +<dl><dd><pre class="verbatim"> +## FILE: hello_world.py ## +template = """ +<HTML> +<HEAD><TITLE>'Hello World - Test Servlet</TITLE></HEAD> +<BODY> +Hello World! +</BODY> +</HTML> +""" +from Cheetah.Servlet import TemplateServlet +class hello_world(TemplateServlet): + def __init__(self): + TemplateServlet.__init__(self, template) +</pre></dl> + +<P> +TemplateServlet's constructor method (<code>TemplateServlet.__init__()</code>) adds +the attribute dictionary of the servlet to the searchList that +<code>$placeholder</code> variables can extracted from. Thus, attributes and methods +of the servlet object can be interpolated into the template like this: + +<P> +<dl><dd><pre class="verbatim"> +## FILE: hello_world.py ## +template = """ +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> +$contents +</BODY> +</HTML> +""" +from Cheetah.Servlet import TemplateServlet +class hello_world(TemplateServlet): + title = 'Hello World - Test Servlet' + def __init__(self): + TemplateServlet.__init__(self, template) + + def contents(self): + return 'Hello World!' +</pre></dl> + +<P> +This process can be simplified for non-programmers. All the Python wrapper code +in these examples can be generated automatically by <code>cheetah-compile</code>, a small program +that is installed with Cheetah. <code>cheetah-compile</code> parses Template Definitions +written in files with the .tmpl extension and generates a Webware servlet file +with the .py extension for each .tmpl file. Type <code>cheetah-compile</code> after +installing Cheetah to get information on how to use it. + +<P> +Here's the first example as a .tmpl file: + +<P> +<dl><dd><pre class="verbatim"> +## FILE: hello_world.tmpl ## +<HTML> +<HEAD><TITLE>Hello World - Test Servlet</TITLE></HEAD> +<BODY> +Hello World! +</BODY> +</HTML> +</pre></dl> + +<P> +Here's the second example as a .tmpl file. Note that all lines that begin with +<code>##</code> are comment lines. + +<P> +<dl><dd><pre class="verbatim"> +## FILE: hello_world.tmpl ## +##=================================== +#data +title = 'Hello World - Test Servlet' +def contents(): + return 'Hello World!' +#/data +##=================================== +<HTML> +<HEAD><TITLE>$title</TITLE></HEAD> +<BODY> +$contents +</BODY> +</HTML> +</pre></dl> + +<P> + +<H3><A NAME="SECTION000721000000000000000"> +6.2.1 The #data directive</A> +</H3> +The previous example introduced the <code>#data</code> directive, a shortcut means +of adding data into the servlet's attribute dictionary and thus into +the Template's <B>searchList</B>. The <code>#data</code> directive can be used in +any Template Definition, regardless of whether the definition is in a .py or +.tmpl file. + +<P> +<code>#data</code> directives can contain any valid Python code + +<P> + +<H3><A NAME="SECTION000722000000000000000"> +6.2.2 The SkeletonPage framework</A> +</H3> + +<P> + +<H3><A NAME="SECTION000723000000000000000"> +6.2.3 PlateKit</A> +</H3> +Forget what you knew about PlateKit. It's undergoing some changes and is out of +action for the time-being. + +<P> + +<H2><A NAME="SECTION000730000000000000000"> +6.3 Using the Containment approach</A> +</H2> + +<P> + +<H2><A NAME="SECTION000740000000000000000"> +6.4 User interaction in either approach</A> +</H2> + +<P> + +<H2><A NAME="SECTION000750000000000000000"> +6.5 Components</A> +</H2> + +<P> + +<H3><A NAME="SECTION000751000000000000000"> +6.5.1 What are components?</A> +</H3> + +<P> + +<H3><A NAME="SECTION000752000000000000000"> +6.5.2 Using components</A> +</H3> + +<P> + +<H3><A NAME="SECTION000753000000000000000"> +6.5.3 Building components</A> +</H3> + +<P> + +<H1><A NAME="SECTION000800000000000000000"> </A> +<BR> +7 Customizing and extending Cheetah +</H1> + +<P> + +<H2><A NAME="SECTION000810000000000000000"> +7.1 Custom handling of unknown placeholder names</A> +</H2> + +<P> + +<H2><A NAME="SECTION000820000000000000000"> +7.2 Plugins</A> +</H2> + +<H3><A NAME="SECTION000821000000000000000"> +7.2.1 The PSP plugin</A> +</H3> + +<P> + +<H2><A NAME="SECTION000830000000000000000"> +7.3 Custom variable-tags</A> +</H2> + +<P> + +<H2><A NAME="SECTION000840000000000000000"> +7.4 Custom directives</A> +</H2> + +<P> + +<H2><A NAME="SECTION000850000000000000000"> +7.5 Custom error handlers</A> +</H2> + +<P> + +<H2><A NAME="SECTION000860000000000000000"> +7.6 Safe delegation</A> +</H2> +Safe Delegation, as provided by Zope and Allaire's Spectra, is not a core aim of +Cheetah. However, several hooks were built into Cheetah so that Safe +Delegation can be implemented at a later date. + +<P> + +<H1><A NAME="SECTION000900000000000000000"> </A> +<BR> +8 Examples +</H1> + +<P> +The Cheetah distribution comes with an 'examples' directory. Browse the +files in this directory and its subdirectories for examples of how +Cheetah can be used. + +<P> + +<H2><A NAME="SECTION000910000000000000000"> +8.1 Syntax examples</A> +</H2> +Cheetah's <code>Tests</code> module contains a large number of test cases +that can double as examples of how the Template Definition Language works. +To view these cases go to the base directory of your Cheetah distribution +and open the file Cheetah/Tests.py in a text editor. + +<P> + +<H2><A NAME="SECTION000920000000000000000"> +8.2 Webware Examples</A> +</H2> +The 'examples' directory has a subdirectory called 'webware_examples'. It +contains example servlets that use Webware. + +<P> +A subdirectory titled 'webwareSite' contains a complete website example. This +site is my proposal for the new Webware website. The site demonstrates the +advanced Cheetah features such as the <code>#data</code> and <code>#redefine</code> +directives. It also demonstrates how the TScompile program can be used to +generate Webware .py servlet files from .tmpl Template Definition files. + +<P> + +<H1><A NAME="SECTION0001000000000000000000"> +About this document ...</A> +</H1> + <strong>Cheetah Users Guide</strong> +<p> This document was generated using the <a + href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/"> + <strong>LaTeX</strong>2<tt>HTML</tt></a> translator. +</p> + +<p> <a + href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/"> + <strong>LaTeX</strong>2<tt>HTML</tt></a> is Copyright © + 1993, 1994, 1995, 1996, 1997, <a + href="http://cbl.leeds.ac.uk/nikos/personal.html">Nikos + Drakos</a>, Computer Based Learning Unit, University of + Leeds, and Copyright © 1997, 1998, <a + href="http://www.maths.mq.edu.au/~ross/">Ross + Moore</a>, Mathematics Department, Macquarie University, + Sydney. +</p> + +<p> The application of <a + href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/"> + <strong>LaTeX</strong>2<tt>HTML</tt></a> to the Python + documentation has been heavily tailored by Fred L. Drake, + Jr. Original navigation icons were contributed by Christopher + Petrilli. +</p> +<BR><HR><H4>Footnotes</H4> +<DL> +<DT><A NAME="foot343">... objects.</A><A NAME="foot343" + HREF="users_guide.html#tex2html1"><SUP>1</SUP></A> +<DD>Cheetah uses a Python module called + NameMapper to handle this style of dotted notation. NameMapper is distributed + as part of the Cheetah package and can be used as a stand-alone tool. + NameMapper was inspired by Chuck Esterbrook's NamedValueAccess module + + </TD> + </TR> + </TABLE> + + <IMG SRC="spacer.gif" WIDTH=1 HEIGHT=200 ALT=""> + </TD> + + <TD WIDTH=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 BGCOLOR="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=900 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=100 HEIGHT=1 ALT=""></TD> + <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + <TD WIDTH=900 BGCOLOR="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=25 ALT=""></TD> + <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> +</TABLE> + +<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%> + <TR> + <TD WIDTH=1 HEIGHT=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD> + </TR> + <TR> + <TD> + <DIV CLASS="copyrightFooter"> + Copyright © 2001 The Cheetah Development Team. All rights reserved. + </DIV> + <BR> + + <DIV CLASS="copyrightFooter" ALIGN="center">Hosted on<BR> + <a href=http://sourceforge.net><img + src=http://sourceforge.net/sflogo.php?group_id=28961&type=1" + alt="SourceForge Home" height=31 width=88 border=0></a> + </DIV> + + </TD> + </TR> +</TABLE> + + +<BR> +<BR> +<BR> + + + + +</BODY> +</HTML> diff --git a/examples/webware_examples/cheetahSite/learn.tmpl b/examples/webware_examples/cheetahSite/learn.tmpl new file mode 100644 index 0000000..7ef7ff1 --- /dev/null +++ b/examples/webware_examples/cheetahSite/learn.tmpl @@ -0,0 +1,43 @@ +#extend SiteTemplate + +##============================================================================## +#data + +def usersGuideTxt(): + USERS_GUIDE_PATH = '/home/tavis/Cheetah/docs/users_guide_html/index.html' + txt = open(USERS_GUIDE_PATH).read() + start = txt.find('<div class="titlepage">') + end = txt.find('</DL>\\n<DIV CLASS="navigation">') - 3 + return txt[start:end] + +#end data +##============================================================================## + + +##============================================================================## +## from SiteTemplate +#redefine locationBarContents + <A CLASS="locationBar" HREF="index.html">Home</A> > + <A CLASS="locationBar" HREF="learn.html">Learn</A> +#end redefine locationBarContents +##============================================================================## + + +##============================================================================## +## from SiteTemplate +#redefine leftSideBar + <BR> + <A HREF="Downloads/users_guide.pdf"><B>PDF</B></A><BR> + <A HREF="Downloads/users_guide.ps"><B>PostScript</B></A><BR> + <A HREF="Downloads/users_guide.txt"><B>Plain Text</B></A><BR> +#end redefine leftSideBar +##============================================================================## + +##============================================================================## +## from SiteTemplate +#redefine mainContentsCell + +$*usersGuideTxt + +#end redefine mainContentsCell +##============================================================================## diff --git a/examples/webware_examples/cheetahSite/sourceforge_logo.gif b/examples/webware_examples/cheetahSite/sourceforge_logo.gif Binary files differnew file mode 100644 index 0000000..08589d2 --- /dev/null +++ b/examples/webware_examples/cheetahSite/sourceforge_logo.gif diff --git a/examples/webware_examples/cheetahSite/spacer.gif b/examples/webware_examples/cheetahSite/spacer.gif Binary files differnew file mode 100644 index 0000000..5bfd67a --- /dev/null +++ b/examples/webware_examples/cheetahSite/spacer.gif diff --git a/examples/webware_examples/hello_world.py b/examples/webware_examples/hello_world.py new file mode 100644 index 0000000..a3f6aa5 --- /dev/null +++ b/examples/webware_examples/hello_world.py @@ -0,0 +1,8 @@ +templateDef = ''' +Hello World! +''' + +from Cheetah.Templates.SkeletonPage import SkeletonPage +class hello_world(SkeletonPage): + def initializeTemplate(self): + self.extendTemplate(templateDef) diff --git a/examples/webware_examples/webwareSite/WebwareSiteTemplate.tmpl b/examples/webware_examples/webwareSite/WebwareSiteTemplate.tmpl new file mode 100644 index 0000000..7f39859 --- /dev/null +++ b/examples/webware_examples/webwareSite/WebwareSiteTemplate.tmpl @@ -0,0 +1,459 @@ +#extend Cheetah.Templates.SkeletonPage + +##============================================================================## +## macros for the Webware site + +#macro Python() +<A HREF="http://www.python.org">Python</A> +#end macro + +#macro PHP() +<A HREF="http://www.php.net">PHP</A> +#end macro + +#macro Zope() +<A HREF="http://www.zope.org">Zope</A> +#end macro + +#macro ASP() +<A HREF="http://www.asp.net">ASP</A> +#end macro + +#macro JSP() +<A HREF="http://java.sun.com/products/jsp/">JSP</A> +#end macro + +#macro ColdFusion() +<A HREF="http://www.allaire.com">ColdFusion</A> +#end macro + +#macro ASP() +<A HREF="http://www.asp.net">ASP</A> +#end macro + +#macro insetBox(width=170, boxTitle='', boxContents='') +#cache +<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$width> + <TR> + <TD COLSPAN=3 HEIGHT=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD> + </TR> + <TR> + <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD> + <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> ## force full width + <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD> + </TR> + <TR> + <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD> + <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.innerFrameColor"> ## force full width + <DIV CLASS="insetBoxTitleContainer"><DIV CLASS="insetBoxTitle">$boxTitle</DIV></DIV> + </TD> + <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD> + </TR> + <TR> + <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD> + <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> ## force full width + <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD> + </TR> + <TR> + <TD COLSPAN=3 HEIGHT=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD> + </TR> +</TABLE> + +<TABLE VALIGN="top" WIDTH=$width BORDER=0 CELLPADDING=0 CELLSPACING=0> + <TR> + <TD WIDTH=1 bgcolor="$insetBoxes.frameColor">#spacer()</TD> + <TD WIDTH=900 BGCOLOR="$insetBoxes.bgcolor"> ## force to full width + ## now begin the contents cell of the insetBox +#end cache + $boxContents +#cache + ## end insetBox + </TD> + <TD WIDTH=1 BGCOLOR="$insetBoxes.frameColor">#spacer()</TD> + </TR> +</TABLE> + +<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$width> + <TR> + <TD HEIGHT=1 BGCOLOR="$insetBoxes.frameColor">#spacer()</TD> + </TR> +</TABLE> +#end cache +#end macro +##============================================================================## + + + +##============================================================================## +#data + +title = "Webware - The Python-Powered Internet Platform" +siteDomainName = "webware.sourceforge.net" +siteCopyrightName= "The Webware Development Team" + + +bodyTagAttribs = {"bgcolor": "black", + "text":"#000033", #333333 + } + +navBar = {"bgcolor":"#3366CC", #EBEBEB #E0E0E0 + } + +locationBar = {"bgcolor":"#3366CC", #EBEBEB #E0E0E0 + "frameColor":"#CCCCCC", + } + +mainTbl = {"width":"90%", + "align":"center", + "contentsBgColor":"white", + "outerFrameColor":"#6699FF", #FFC850 #BDD4F1 + "innerFrameColor":"#3366CC", #0059B2 + "innerFrameWidth":8, + } + +insetBoxes = {"width":170, + "frameColor":"#DDDDDD", #000033 + "titleFrameColor":"black", #000033 + "bgcolor":"#FAFAFA", + } + + +_sanSerifFonts = """helvetica, arial, "lucida sans", sans-serif""" + +_stylesheets = { + "H1.mainText": {"margin":"7px 2px", + "font-size":"small", + "font-weight":"bold", + "font-family": _sanSerifFonts, + }, + ".mainText": {"margin-left":"10px"}, + + ".navBarContainer": {"padding": "4px 0px", "color":"white"}, + ".navBar, A.navBar:link, A.navBar:visited": {"font-size":"small", + "font-weight":"bold", + "color":"white", + "text-decoration":"none", + "font-family": _sanSerifFonts, + }, + "A.navBar:hover, A.navBar:active": {"text-decoration":"underline"}, + + ".locationBarContainer": {"padding": "2px 10px"}, + ".locationBar, A.locationBar:link, A.locationBar:visited": {"font-size":"x-small", + "color":"#666666", + "text-decoration":"none", + "font-family": _sanSerifFonts, + }, + "A.locationBar:hover, A.locationBar:active": {"text-decoration":"underline"}, + + ".copyrightFooter": {"color":"white", "font-size":"9pt"}, + + ".insetBoxTitleContainer":{"padding": "2px 5px", + "color":"white", + "font-weight":"bold", + }, + ".insetBoxTitle":{"color":"white", + "font-size":"x-small", + "font-weight":"bold", + "text-decoration":"none", + "font-family": _sanSerifFonts, + }, + ".insetBoxContainer":{"padding": "2px 2px 2px 5px"}, + ".insetBox":{"font-size":"x-small", + "font-family": _sanSerifFonts, + }, + "H1.insetBox":{"margin": "5px 5px", + "padding": "0px", + "font-size":"x-small", + "font-weight":"bold", + "font-family": _sanSerifFonts + }, + + "P.insetBox":{"margin": "2px 5px 7px 10px", + "padding": "0px", + "font-family": _sanSerifFonts + }, + "A.insetBox:link, A.insetBox:visited":{"font-size":"x-small", + "text-decoration":"none", + }, + "A.insetBox:hover, A.insetBox:active": {"text-decoration":"underline"}, + + ".listItems": {"margin-bottom":"200px"}, + } + +_stylesheetsOrder = ["H1.mainText", + ".mainText", + ".navBarContainer", + ".navBar, A.navBar:link, A.navBar:visited", + "A.navBar:hover, A.navBar:active", + ".locationBarContainer", + ".locationBar, A.locationBar:link, A.locationBar:visited", + "A.locationBar:hover, A.locationBar:active", + ".copyrightFooter", + ".insetBoxTitleContainer", + ".insetBoxTitle", + ".insetBoxContainer", + ".insetBox", + "H1.insetBox", + "P.insetBox", + "A.insetBox:link, A.insetBox:visited", + "A.insetBox:hover, A.insetBox:active", + ".listItems", + ] + +#end data +##============================================================================## + + +##============================================================================## +## from SkeletonPage +#redefine bodyContents + +##################### +#block header + +#end block header +##################### + +<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width> + <TR> + <TD> + ##################### + #block mainTblOuterHeader + <DIV ALIGN="center"><IMG SRC="webware_title_red-white_fat.gif" ALT="Webware"></DIV> + <DIV ALIGN="center"><IMG SRC="webware_subtitle_black.gif" ALT="The + Python-Powered Internet Platform"></DIV> + #spacer(1,5) + + #end block mainTblOuterHeader + ##################### + </TD> + </TR> + <TR> + <TD WIDTH=1 HEIGHT=1 bgcolor="$mainTbl.outerFrameColor">#spacer()</TD> + ## top line of the mainTbl frame + </TR> +</TABLE> + + +<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width> + <TR> + <TD WIDTH=1 bgcolor="$mainTbl.outerFrameColor">#spacer()</TD> + <TD WIDTH=900> ## force to full width in Netscape + ##################### + #block mainTblInnerHeader + + ############## + #block navBar + <TABLE WIDTH=100% BGCOLOR="$navBar.bgcolor" BORDER=0 CELLPADDING=0 CELLSPACING=0> + <TR> + <TD> + <DIV ALIGN="center" CLASS="navBarContainer"> + <A CLASS="navBar" HREF="index.py">Home</A> | + <A CLASS="navBar" HREF="learn.py">Learn</A> | + <A CLASS="navBar" HREF="download.py">Download</A> | + <A CLASS="navBar" HREF="contribute.py">Contribute</A> | + <A CLASS="navBar" HREF="communicate.py">Communicate</A> + </DIV> + </TD> + </TR> + </TABLE> + #end block navBar + ############## + + #end block mainTblInnerHeader + ##################### + </TD> + <TD WIDTH="1" bgcolor="$mainTbl.outerFrameColor">#spacer()</TD> + </TR> +</TABLE> + +<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width> + <TR> + <TD WIDTH=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> + <TD WIDTH=$mainTbl.innerFrameWidth bgcolor="$mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH=900 HEIGHT=1 BGCOLOR="black">#spacer(100)</TD> ## force to full width in Netscape + <TD WIDTH=$mainTbl.innerFrameWidth bgcolor="$mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH="1" BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> + </TR> +</TABLE> + + +## MainTbl - contents ## + +<TABLE ALIGN=$mainTbl.align VALIGN="top" BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width> + <TR> + <TD WIDTH=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> + <TD WIDTH=$mainTbl.innerFrameWidth bgcolor="$mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH=1 BGCOLOR="black">#spacer()</TD> + + <TD BGCOLOR="$mainTbl.contentsBgColor"> + + ################### + #block locationBar + <TABLE WIDTH=100% BGCOLOR="#EBEBEB" BORDER=0 CELLPADDING=0 CELLSPACING=0> + <TR> + <TD> + <DIV CLASS="locationBarContainer"> + ################### + #block locationBarContents + <A CLASS="locationBar" HREF="index.py">Home</A> > + <A CLASS="locationBar" HREF="download.py">Download</A> + #end block locationBarContents + ################### + </DIV> + </TD> + </TR> + <TR> + <TD HEIGHT=1 BGCOLOR="$locationBar.frameColor">#spacer()</TD> + </TR> + </TABLE> + + #end block locationBar + ################### + + + + ###################### + #block mainTblContents + + #spacer(1,5) + + <TABLE VALIGN="top" WIDTH=100% BORDER=0 CELLPADDING=10 CELLSPACING=0> + <TR> + <TD VALIGN="top"> + ################ + #block leftSideBar + <DIV ALIGN="center"><IMG SRC="onion_blue_on_white.gif" ALT=""></DIV> + + <BR> + + ##====================================== + #callMacro insetBox(boxTitle="Sections") + #arg boxContents + <DIV CLASS="insetBox"> + + <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="learn.py">Learn</A></H1> + <P CLASS="insetBox"> + Manual<BR> + Tutorials<BR> + Tips and Tricks<BR> + Articles<BR> + Language Comparisons<BR> + Developer Information<BR> + </P> + + <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="download.py">Download</A></H1> + <P CLASS="insetBox"> + Stable Releases<BR> + CVS Snapshots<BR> + Third-Party Packages<BR> + Browse the CVS<BR> + </P> + + <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="contribute.py">Contribute</A></H1> + <P CLASS="insetBox"> + Links to Webware Sites<BR> + Bug Reports<BR> + Patches and New Code<BR> + Tutorials<BR> + Tips and Tricks<BR> + Articles<BR> + Language Comparisons<BR> + </P> + + <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="communicate.py">Communicate</A></H1> + <P CLASS="insetBox"> + News<BR> + Mailing Lists<BR> + The Developers<BR> + Introduce Yourself<BR> + </P> + + </DIV> ## end insetBox + #end arg + #end callMacro + ##====================================== + + #end block leftSideBar + ################ + + </TD> + <TD VALIGN="top" WIDTH=80%> + ######################## + #block mainContentsCell + #end block mainContentsCell + ######################## + </TD> + </TR> + </TABLE> + + #spacer(1,200) + #end block mainTblContents + ###################### + </TD> + + <TD WIDTH=1 BGCOLOR="black">#spacer()</TD> + <TD WIDTH=$mainTbl.innerFrameWidth BGCOLOR="$mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH="1" BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> + </TR> +</TABLE> + + +## MainTbl - closure ## +<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width> + <TR> + <TD WIDTH=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> + <TD WIDTH=$mainTbl.innerFrameWidth bgcolor="$mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH=900 BGCOLOR="black">#spacer(100)</TD> ## force to full width in Netscape + <TD WIDTH=$mainTbl.innerFrameWidth bgcolor="$mainTbl.innerFrameColor">#spacer()</TD> + <TD WIDTH="1" BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> + </TR> +</TABLE> + +<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width> + <TR> + <TD WIDTH=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> + <TD WIDTH=900 BGCOLOR="$mainTbl.innerFrameColor">#spacer(1,25)</TD> ## force to full width in Netscape + <TD WIDTH="1" BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> + </TR> +</TABLE> + +<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width> + <TR> + <TD WIDTH=1 HEIGHT=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> + </TR> + <TR> + <TD> + ##################### + #block mainTblOuterFooter + <DIV CLASS="copyrightFooter"> + Copyright © 1999 - #currentYr() The Webware Development Team. All rights reserved. + </DIV> + <BR> + + <DIV CLASS="copyrightFooter" ALIGN="center">Hosted on<BR> + <A HREF="http://sourceforge.net"><IMG BORDER=0 SRC="sourceforge_logo.gif" ALT="sourceforge.net"></A> + </DIV> + + ##<DIV ALIGN="center"><IMG SRC="webware_logo.gif"></DIV> + ##<DIV ALIGN="center"><IMG SRC="webware_slogan_bw.gif"></DIV> + + #end block mainTblOuterFooter + ##################### + </TD> + </TR> +</TABLE> + + +##################### +#block footer +<BR> +<BR> +<BR> +#end block footer +##################### + + +#end redefine bodyContents +## from SkeletonPage +##============================================================================## diff --git a/examples/webware_examples/webwareSite/communicate.tmpl b/examples/webware_examples/webwareSite/communicate.tmpl new file mode 100644 index 0000000..0d56a5b --- /dev/null +++ b/examples/webware_examples/webwareSite/communicate.tmpl @@ -0,0 +1,29 @@ +#extend WebwareSiteTemplate + +##============================================================================## +#data + +title = "Webware - The Python-Powered Internet Platform - Communicate" + +#end data +##============================================================================## + + +##============================================================================## +## from WebwareSiteTemplate +#redefine locationBarContents + <A CLASS="locationBar" HREF="index.py">Home</A> > + <A CLASS="locationBar" HREF="communicate.py">Communicate</A> +#end redefine locationBarContents +## from WebwareSiteTemplate +##============================================================================## + + +##============================================================================## +## from WebwareSiteTemplate +#redefine mainContentsCell +This is the "communicate" page, and it\'s blank for now! + +#end redefine mainContentsCell +## from WebwareSiteTemplate +##============================================================================## diff --git a/examples/webware_examples/webwareSite/contribute.tmpl b/examples/webware_examples/webwareSite/contribute.tmpl new file mode 100644 index 0000000..593e61d --- /dev/null +++ b/examples/webware_examples/webwareSite/contribute.tmpl @@ -0,0 +1,29 @@ +#extend WebwareSiteTemplate + +##============================================================================## +#data + +title = "Webware - The Python-Powered Internet Platform - Contribute" + +#end data +##============================================================================## + + +##============================================================================## +## from WebwareSiteTemplate +#redefine locationBarContents + <A CLASS="locationBar" HREF="index.py">Home</A> > + <A CLASS="locationBar" HREF="contribute.py">Contribute</A> +#end redefine locationBarContents +## from WebwareSiteTemplate +##============================================================================## + + +##============================================================================## +## from WebwareSiteTemplate +#redefine mainContentsCell +This is the "contribute" page, and it\'s blank for now! + +#end redefine mainContentsCell +## from WebwareSiteTemplate +##============================================================================## diff --git a/examples/webware_examples/webwareSite/download.tmpl b/examples/webware_examples/webwareSite/download.tmpl new file mode 100644 index 0000000..71485aa --- /dev/null +++ b/examples/webware_examples/webwareSite/download.tmpl @@ -0,0 +1,29 @@ +#extend WebwareSiteTemplate + +##============================================================================## +#data + +title = "Webware - The Python-Powered Internet Platform - Download" + +#end data +##============================================================================## + + +##============================================================================## +## from WebwareSiteTemplate +#redefine locationBarContents + <A CLASS="locationBar" HREF="index.py">Home</A> > + <A CLASS="locationBar" HREF="download.py">Download</A> +#end redefine locationBarContents +## from WebwareSiteTemplate +##============================================================================## + + +##============================================================================## +## from WebwareSiteTemplate +#redefine mainContentsCell +This is the download page, and it\'s blank for now! + +#end redefine mainContentsCell +## from WebwareSiteTemplate +##============================================================================## diff --git a/examples/webware_examples/webwareSite/index.tmpl b/examples/webware_examples/webwareSite/index.tmpl new file mode 100644 index 0000000..5c7b82b --- /dev/null +++ b/examples/webware_examples/webwareSite/index.tmpl @@ -0,0 +1,138 @@ +#extend WebwareSiteTemplate + +################### +#redefine locationBar + +#end redefine locationBar +################### + + +######################## +#redefine mainContentsCell + +<H1 CLASS="mainText">What is Webware?</H1> + +<P CLASS="mainText"> +Webware is an internet development platform powered by +#Python(), the +Open Source programming language developed by Guido van Rossum.</P> + +<H1 CLASS="mainText">What does Webware provide?</H1> +<UL> + <LI>The strength of <A HREF="http://jakarta.apache.org">server-side Java</A>, + without its complexity.</LI> + + <LI>The power of server-side <A HREF="http://perl.apache.org/">Perl</A>, + without its syntax.</LI> + + <LI>The Python of #Zope(), without its headaches.</LI> + <LI>The flexibility of <A HREF="http://hoohoo.ncsa.uiuc.edu/cgi/intro.html">CGI</A>, + without its overhead.</LI> + + <LI>The simplicity of #PHP(), without its limitations.</LI> + <LI>The capabilities of #ASP() and #ColdFusion(), without their + price tag.</LI> +</UL> +<P CLASS="mainText"> +...The power of the web, with the ease of #Python()! +</P> + + +<H1 CLASS="mainText">Cut the marketing-speak! I want details.</H1> + +<P CLASS="mainText"> +ok ... Webware provides: +<UL> + <LI CLASS=".listItems">a fast, reliable, extensible, and scalable application server.</LI> + + <LI CLASS=".listItems">coding in #Python(): a powerful object-orientated language. + Python dramatically improves developer productivity and code readible over Java, C++, and + other compiled languages. Furthermore, it's easier + to learn than other interpreted languages.</LI> + + <LI CLASS=".listItems">support for several development approaches: + using pure Python Servlets as + in Java Servlets; using embedded PSP script as in ASP, PHP, JSP, and embed-Perl; + using templating languages as in Velocity and WebMacro; + and any combination of the above.</LI> + + <LI CLASS=".listItems">full access to all Python modules. + Webware doesn't require you jump through hoops to import relevant Python modules.</LI> + + <LI CLASS=".listItems">an extensive suite of add-ons and tools.</LI> + + <LI CLASS=".listItems">automatic management of sessions, cookies, HTTP headers, etc.</LI> + + <LI CLASS=".listItems">extensive support for XML, SOAP, XML-RPC, + COM, and CORBA through Python.</LI> + + <LI CLASS=".listItems">structured exception handling through Python. + This makes Webware much suitable for large complex projects than environments + without structured exception handling, like PHP.</LI> + + <LI CLASS=".listItems">full object persistance and caching. + This feature of the appserver a major advantage over solutions + that are embedded in the webserver process, such as PHP where + it costs over $2000US to purchase an add-on that only caches the + byte-compiled code.</LI> + + <LI CLASS=".listItems">database connection persistance and pooling.</LI> + + <LI CLASS=".listItems">portability. + It works anywhere Python works: Linux, Unixes, Windows, OS2, etc.</LI> + + <LI CLASS=".listItems">web server portability. + It works with Apache, IIS, and most others.</LI> + + <LI CLASS=".listItems">web server independence. + The application server runs in a separate process and can + even run on separate machines. One webserver can communicate with multiple application + servers, and vice-versa. It work crash your web-server and your webserver + wont crash it. Load-balancing is easy.</LI> + + <LI CLASS=".listItems">protocol independence. + Although the application server was developed with HTTP in mind, it can be + extended to work with any TCP/IP based network protocol, + even <A HREF="http://www.blug.linux.no/rfc1149/">CPIP</A> ;-).</LI> + + <LI CLASS=".listItems">a powerful template framework that separates the page logic + from its design/display and supports a reusable component-based design.</LI> + + <LI CLASS=".listItems">interopability with other programming languages, + such as C, C++ and Java, through Python's extension framework.</LI> + + <LI CLASS=".listItems">full source code. + All code is licensed under the 'Open Source' Python license, which + allows for unrestricted use, modification, and redistribution. Unlike the + GNU GPL, it allows the code to be used in commercial products. + </LI> + + <LI CLASS=".listItems">and most importantly, an active and supportive developer community!</LI> + +</UL> +</P> + + +<H1 CLASS="mainText">What's with the onion?!</H1> +<P CLASS="mainText"> Onions are +ubiquitous. They grow almost anywhere and can be used in almost any dish. You'll +find them from bachelor pads to haut cuisine restaurants, because they work well with +others and are hard to botch, yet offer a surprising array of taste in the right +hands.</P> + +<P CLASS="mainText">Onions are layered. Thus, they can be transformed into an +endless range of shapes. You can cook with an entire onion, or even several, but you +can also just slice off a few rings. </P> + +<P CLASS="mainText">Onions are easy to understand. You don't need expensive training +to cook with them.</P> + +<P CLASS="mainText"> So it should be with internet development platforms. They +should be hardy and work well with others; be easy to use for simple and complex +tasks alike; should allow you to use a little or alot, depending on your tastes, and +they shouldn't require a computer science degree to be understood. But, unlike +onions, they shouldn't they make you cry ;-)</P> + + +#end redefine mainContentsCell +######################## diff --git a/examples/webware_examples/webwareSite/learn.tmpl b/examples/webware_examples/webwareSite/learn.tmpl new file mode 100644 index 0000000..18bf8f4 --- /dev/null +++ b/examples/webware_examples/webwareSite/learn.tmpl @@ -0,0 +1,33 @@ +#extend WebwareSiteTemplate + +##============================================================================## +#data + +title = "Webware - The Python-Powered Internet Platform - Learn" +#end data +#data +def testFunc(): + return 1234 + +#end data +##============================================================================## + + +##============================================================================## +## from WebwareSiteTemplate +#redefine locationBarContents + <A CLASS="locationBar" HREF="index.py">Home</A> > + <A CLASS="locationBar" HREF="learn.py">Learn Webware</A> +#end redefine locationBarContents +## from WebwareSiteTemplate +##============================================================================## + + +##============================================================================## +## from WebwareSiteTemplate +#redefine mainContentsCell +This is the "Learn Webware" page, and it\'s blank for now! +<BR>Testing $testFunc +#end redefine mainContentsCell +## from WebwareSiteTemplate +##============================================================================## diff --git a/examples/webware_examples/webwareSite/onion_blue_on_white.gif b/examples/webware_examples/webwareSite/onion_blue_on_white.gif Binary files differnew file mode 100644 index 0000000..2e9c585 --- /dev/null +++ b/examples/webware_examples/webwareSite/onion_blue_on_white.gif diff --git a/examples/webware_examples/webwareSite/onion_blue_on_white_large.gif b/examples/webware_examples/webwareSite/onion_blue_on_white_large.gif Binary files differnew file mode 100644 index 0000000..1690cc0 --- /dev/null +++ b/examples/webware_examples/webwareSite/onion_blue_on_white_large.gif diff --git a/examples/webware_examples/webwareSite/sourceforge_logo.gif b/examples/webware_examples/webwareSite/sourceforge_logo.gif Binary files differnew file mode 100644 index 0000000..08589d2 --- /dev/null +++ b/examples/webware_examples/webwareSite/sourceforge_logo.gif diff --git a/examples/webware_examples/webwareSite/spacer.gif b/examples/webware_examples/webwareSite/spacer.gif Binary files differnew file mode 100644 index 0000000..5bfd67a --- /dev/null +++ b/examples/webware_examples/webwareSite/spacer.gif diff --git a/examples/webware_examples/webwareSite/webWare.gif b/examples/webware_examples/webwareSite/webWare.gif Binary files differnew file mode 100644 index 0000000..cf48c95 --- /dev/null +++ b/examples/webware_examples/webwareSite/webWare.gif diff --git a/examples/webware_examples/webwareSite/webWare_blueBG.gif b/examples/webware_examples/webwareSite/webWare_blueBG.gif Binary files differnew file mode 100644 index 0000000..606e8b7 --- /dev/null +++ b/examples/webware_examples/webwareSite/webWare_blueBG.gif diff --git a/examples/webware_examples/webwareSite/webWare_subtitle.gif b/examples/webware_examples/webwareSite/webWare_subtitle.gif Binary files differnew file mode 100644 index 0000000..de314c4 --- /dev/null +++ b/examples/webware_examples/webwareSite/webWare_subtitle.gif diff --git a/examples/webware_examples/webwareSite/webonions.gif b/examples/webware_examples/webwareSite/webonions.gif Binary files differnew file mode 100644 index 0000000..40e8510 --- /dev/null +++ b/examples/webware_examples/webwareSite/webonions.gif diff --git a/examples/webware_examples/webwareSite/webware_logo.gif b/examples/webware_examples/webwareSite/webware_logo.gif Binary files differnew file mode 100644 index 0000000..58a4eb3 --- /dev/null +++ b/examples/webware_examples/webwareSite/webware_logo.gif diff --git a/examples/webware_examples/webwareSite/webware_slogan_bw.gif b/examples/webware_examples/webwareSite/webware_slogan_bw.gif Binary files differnew file mode 100644 index 0000000..8d7a41f --- /dev/null +++ b/examples/webware_examples/webwareSite/webware_slogan_bw.gif diff --git a/examples/webware_examples/webwareSite/webware_subtitle_black.gif b/examples/webware_examples/webwareSite/webware_subtitle_black.gif Binary files differnew file mode 100644 index 0000000..491284a --- /dev/null +++ b/examples/webware_examples/webwareSite/webware_subtitle_black.gif diff --git a/examples/webware_examples/webwareSite/webware_subtitle_inner_frame.gif b/examples/webware_examples/webwareSite/webware_subtitle_inner_frame.gif Binary files differnew file mode 100644 index 0000000..28e32dc --- /dev/null +++ b/examples/webware_examples/webwareSite/webware_subtitle_inner_frame.gif diff --git a/examples/webware_examples/webwareSite/webware_title_bw.gif b/examples/webware_examples/webwareSite/webware_title_bw.gif Binary files differnew file mode 100644 index 0000000..338cda4 --- /dev/null +++ b/examples/webware_examples/webwareSite/webware_title_bw.gif diff --git a/examples/webware_examples/webwareSite/webware_title_red-white.gif b/examples/webware_examples/webwareSite/webware_title_red-white.gif Binary files differnew file mode 100644 index 0000000..366ddab --- /dev/null +++ b/examples/webware_examples/webwareSite/webware_title_red-white.gif diff --git a/examples/webware_examples/webwareSite/webware_title_red-white_fat.gif b/examples/webware_examples/webwareSite/webware_title_red-white_fat.gif Binary files differnew file mode 100644 index 0000000..8f4b79a --- /dev/null +++ b/examples/webware_examples/webwareSite/webware_title_red-white_fat.gif diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..b3c784c --- /dev/null +++ b/setup.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# $Id: setup.py,v 1.1 2001/06/13 03:50:36 tavis_rudd Exp $ +"""A setup module for the Cheetah package, based on the disutils module + +Meta-Data +========== +Author: Tavis Rudd <tavis@calrudd.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:36 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## +import os +import os.path +from distutils.core import setup +from distutils.command.sdist import sdist + +################################################## +## CONSTANTS & GLOBALS ## + +True = (1==1) +False = (0==1) + + +################################################## +## CLASSES ## + +class sdist_docs(sdist): + """a setup command that will rebuild Users Guide""" + def run(self): + try: + currentDir = os.getcwd() + os.chdir(os.path.join(currentDir,'docs','src')) + os.system('make -f Makefile') + os.chdir(currentDir) + except: + print "The sdist_docs command couldn't rebuild the Users Guide" + + sdist.run(self) + + +################################################## +## if run from the command line ## + +if __name__ == '__main__': + from Cheetah.Version import version + + from Cheetah import __doc__ + README = open('README','w') + README.write(__doc__) + README.close() + synopsis = __doc__.split('\n')[0] + + packages = ['Cheetah', + 'Cheetah.Templates', + 'Cheetah.Plugins', + 'Cheetah.Macros', + ] + + setup (name = "Cheetah", + author = "The Cheetah Development Team", + author_email = "cheetahtemplate-devel@sourceforge.net", + version = version, + license = "The Python License", + description = synopsis, + long_description = __doc__, + maintainer = "Tavis Rudd", + url = "http://www.calrudd.com/tavis", + + packages = packages, + package_dir = {'Cheetah':'src'}, + + scripts = ['bin/cheetah-compile',], + + cmdclass = { 'sdist_docs' : sdist_docs }, + ) + + diff --git a/setup_all.py b/setup_all.py new file mode 100755 index 0000000..c9e5edf --- /dev/null +++ b/setup_all.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +"""This version of the setup.py script is used to install Cheetah + all third_party_packages + +""" +import string, os, sys +cwd = os.getcwd() + +### install the main package +execfile('setup.py') + +### then install all the third_party_packages +packages=[] +for p in packages: + d = os.path.join(cwd, 'third_party_packages', p) + os.chdir(d) + print "Working on", d, "(", os.getcwd(), ")" + os.system(sys.executable+' setup.py '+string.join(sys.argv[1:],' ')) diff --git a/src/CodeGenerator.py b/src/CodeGenerator.py new file mode 100644 index 0000000..55b26da --- /dev/null +++ b/src/CodeGenerator.py @@ -0,0 +1,851 @@ +#!/usr/bin/env python +# $Id: CodeGenerator.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ +"""Utilities, processors and filters for Cheetah's codeGenerator + +Cheetah's codeGenerator is designed to be extensible with plugin +functions. This module contains the default plugins. + +Meta-Data +================================================================================ +Author: Tavis Rudd <tavis@calrudd.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## + +import re +import types +from time import time as currentTime # used in the cache refresh code + +# intra-package imports ... +import NameMapper +from Validators import \ + validateDisplayLogicCode, \ + validateArgStringInPlaceholderTag, \ + validateIncludeDirective, \ + validateMacroDirective, \ + validateSetDirective + +from Delimeters import delimeters +from Components import Component +import Template +from Utilities import lineNumFromPos +################################################## +## CONSTANTS & GLOBALS ## + +True = (1==1) +False = (0==1) + + +# cacheType's for $placeholders +NO_CACHE = 0 +STATIC_CACHE = 1 +TIMED_REFRESH_CACHE = 2 + +################################################## +## CLASSES ## + +class Error(Exception): + pass + +class NoDefault: + pass + +################################################## +## FUNCTIONS ## + +def swapDelims(string, delimStruct, newStartDelim, newEndDelim, + unescapeEscaped=True): + """return a copy of 'string' with the delimeters specified in delimStruct + replaced with the newStartDelim and newEndDelim""" + + def replaceDelims(match, newStartDelim=newStartDelim, + newEndDelim=newEndDelim): + + return newStartDelim + match.group(1) + newEndDelim + + startDelim = delimStruct['start'] + endDelim = delimStruct['end'] + startDelimEscaped = delimStruct['startEscaped'] + endDelimEscaped = delimStruct['endEscaped'] + placeholderRE = delimStruct['placeholderRE'] + + if startDelimEscaped: + string = string.replace(startDelimEscaped,'<startDelimEscaped>') + + if endDelimEscaped: + string = string.replace(endDelimEscaped,'<endDelimEscaped>') + + ## do the relacement ## + string = placeholderRE.sub(replaceDelims, string) + + if startDelimEscaped: + if unescapeEscaped: + string = string.replace('<startDelimEscaped>', startDelim) + else: + string = string.replace('<startDelimEscaped>', startDelimEscaped) + + if endDelimEscaped: + if unescapeEscaped: + string = string.replace('<endDelimEscaped>', endDelim) + else: + string = string.replace('<endDelimEscaped>', endDelimEscaped) + + ## + return string + +def separateTagsFromText(initialText, placeholderRE): + """breaks a string up into a textVsTagsList where the odd items are plain + text and the even items are the contents of the tags matched by + placeholderRE""" + + textVsTagsList = [] + position = [0,] + + def subber(match, textVsTagsList=textVsTagsList, + position=position, initialText=initialText): + + textVsTagsList.append( initialText[position[0]:match.start()] ) + position[0] = match.end() + textVsTagsList.append(match.group(1)) + return '' # dummy output that is ignored + + placeholderRE.sub(subber, initialText) # ignoring the return value + textVsTagsList.append(initialText[position[0]:]) + return textVsTagsList + + +def processTextVsTagsList(textVsTagsList, tagProcessorFunction): + """loops through textVsTagsList - the output from separateTagsFromText() - + and filters all the tag items with the tagProcessorFunction""" + + ## odd items are plain text, even ones are tags + processedList = textVsTagsList[:] + for i in range(1, len(processedList), 2): + processedList[i] = tagProcessorFunction(processedList[i]) + return processedList + + + +## codeGenerator plugins ## + +def preProcessComments(templateObj, templateDef): + """cut comments out of the templateDef""" + def subber(match): + #commentString = match.group(1) + return '' + + for regex in templateObj._settings['extDelimeters']['comments']: + templateDef = regex.sub(subber, templateDef) + + return templateDef + +def preProcessSlurpDirective(templateObj, templateDef): + """cut #slurp's out of the templateDef""" + def subber(match): + return '' + + for regex in templateObj._settings['extDelimeters']['slurp']: + templateDef = regex.sub(subber, templateDef) + return templateDef + +def preProcessDataDirectives(templateObj, templateDef): + + def dataDirectiveProcessor(match, templateObj=templateObj): + """process any #data directives that are found in the template + extension""" + + args = match.group('args').split(',') + contents = match.group('contents') + + newDataDict = {'self':templateObj} + exec contents in {}, newDataDict + + del newDataDict['self'] + if not 'overwrite' in args: + templateObj.mergeNewTemplateData(newDataDict) + else: + for key, val in newDataDict.items(): + setattr(templateObj,key,val) + + return '' # strip the directive from the extension + + for RE in templateObj._settings['extDelimeters']['dataDirective']: + templateDef = RE.sub(dataDirectiveProcessor, templateDef) + return templateDef + +def preProcessMacroDirectives(templateObj, templateDef): + """handle any inline #macro definitions """ + + if not hasattr(templateObj, '_macros'): + templateObj._macros = {} + + def handleMacroDefs(match, templateObj=templateObj): + """process each match of the macro definition regex""" + macroSignature = match.group(1) + validateMacroDirective(templateObj, macroSignature) + + firstParenthesis = macroSignature.find('(') + macroArgstring = macroSignature[firstParenthesis+1:-1] + macroName = macroSignature[0:firstParenthesis] + + argStringChunks = [chunk.strip() for chunk in macroArgstring.split(',')] + argNamesList = [(chunk.split('='))[0] for chunk in argStringChunks] + #@@tr: not safe if the default args have commas or = in them!!! + + macroBody = match.group(2) + + def handleArgsUsedInBody(match, argNamesList=argNamesList): + """check each $var in the macroBody to see if it is in this macro's + argNamesList and needs substituting""" + + if match.group(1) in argNamesList: + return "''' + str(" + match.group(1) + ") + '''" + else: + return match.group() + + for delimStruct in \ + templateObj._settings['codeGenerator']['coreTags']['placeholders']['delims']: + + regex = delimStruct['placeholderRE'] + macroBody = regex.sub(handleArgsUsedInBody, + macroBody.replace("'''","\'\'\'")) + + if macroName not in vars().keys(): + macroFuncName = macroName + else: + macroFuncName = 'macroFunction' + + macroCode = "def " + macroFuncName + "(" + macroArgstring + "):\n" + \ + " return '''" + macroBody + "'''\n" + + exec macroCode in None, None + exec "templateObj._macros[macroName] = " + macroFuncName in vars() + + return '' + + for RE in templateObj._settings['extDelimeters']['macroDirective']: + templateDef = RE.sub(handleMacroDefs, templateDef) + return templateDef + +def preProcessLazyMacroCalls(templateObj, templateDef): + """Handle any calls to macros that are already defined.""" + + def handleMacroCalls(match, templateObj=templateObj): + """for each macro call that is found in the template, substitute it with + the macro's output""" + + macroSignature = match.group(1)[1:] + firstParenthesis = macroSignature.find('(') + macroArgstring = macroSignature[firstParenthesis+1:-1] + macroName = macroSignature[0:firstParenthesis] + + try: + macroArgstring = processPlaceholdersInString(templateObj, macroArgstring, + replaceWithValue=True, + wrapInQuotes=True) + except NameMapper.NotFound, name: + line = lineNumFromPos(match.string, match.start()) + raise Error('Undeclared variable $' + str(name) + \ + ' used in macro call #'+ macroSignature + ' on line ' + + str(line)) + + + validateMacroDirective(templateObj, macroArgstring) + if macroName in templateObj._macros.keys(): + return eval('templateObj._macros[macroName](' + macroArgstring + ')', + vars()) + else: + raise Error('The macro ' + macroName + \ + ' was called, but it does not exist') + + for RE in templateObj._settings['extDelimeters']['lazyMacroCalls']: + templateDef = RE.sub(handleMacroCalls, templateDef) + return templateDef + + +def preProcessExplicitMacroCalls(templateObj, templateDef): + """process the explicit callMacro directives""" + + def subber(match, templateObj=templateObj): + macroName = match.group('macroName').strip() + argString = match.group('argString') + extendedArgString = match.group('extendedArgString') + + try: + argString = processPlaceholdersInString(templateObj, argString, + replaceWithValue=True, + wrapInQuotes=True) + except NameMapper.NotFound, name: + line = lineNumFromPos(match.string, match.start()) + raise Error('Undeclared variable $' + str(name) + + ' used in macro call #'+ macroSignature + + ' on line ' + str(line)) + + extendedArgsDict = {} + + def processExtendedArgs(match, extendedArgsDict=extendedArgsDict): + """check each $var in the macroBody to see if it is in this macro's + argNamesList and needs substituting""" + extendedArgsDict[ match.group('argName') ] = match.group('argValue') + return '' + + regex = templateObj._settings['extDelimeters']['callMacroArgs'] + regex.sub(processExtendedArgs, extendedArgString) + + + fullArgString = argString + if fullArgString: + fullArgString += ', ' + for argName in extendedArgsDict.keys(): + fullArgString += argName + '=extendedArgsDict["' + argName + \ + '"]' + ', ' + + validateMacroDirective(templateObj, fullArgString) + if macroName in templateObj._macros.keys(): + return eval('templateObj._macros[macroName](' + fullArgString + ')', vars()) + else: + raise Error('The macro ' + macroName + \ + ' was called, but it does not exist') + + for RE in templateObj._settings['extDelimeters']['callMacro']: + templateDef = RE.sub(subber, templateDef) + + return templateDef + + +def preProcessRawDirectives(templateObj, templateDef): + """extract all chunks of the template that have been escaped with the #raw + directive""" + def subber(match, templateObj=templateObj): + unparsedBlock = match.group(1) + blockID = str(id(unparsedBlock)) + templateObj._unparsedBlocks[blockID]= unparsedBlock + return '<CheetahUnparsedBlock>' + blockID + \ + '</CheetahUnparsedBlock>' + + if not hasattr(templateObj, '_unparsedBlocks'): + templateObj._unparsedBlocks = {} + + for RE in templateObj._settings['extDelimeters']['rawDirective']: + templateDef = RE.sub(subber, templateDef) + return templateDef + +def postProcessRawDirectives(templateObj, templateDef): + """reinsert all chunks that were removed in the preProcessing stage by the + #raw directive""" + + def subber(match, templateObj=templateObj): + blockID = match.group(1) + return templateObj._unparsedBlocks[blockID] + + templateDef = re.sub(r'<CheetahUnparsedBlock>(.+)' + + r'</CheetahUnparsedBlock>', + subber, templateDef) + return templateDef + + +def preProcessIncludeDirectives(templateObj, templateDef, RE): + """replace any #include statements with their substitution value. This method + can handle includes from file (absolute paths only at the moment) and from + placeholders such as $getBodyTemplate""" + + def subber(match, templateObj=templateObj): + args = match.group(1).strip() + # do a safety/security check on this tag + validateIncludeDirective(templateObj, args) + includeString = match.group(1).strip() + + if args.split()[0] == 'raw': + return '<CheetahRawInclude>' + \ + re.sub(r'(?:(?<=\A)|(?<!\\))\$',r'\$', + ' '.join(args.split()[1:] ) ) + \ + '</CheetahRawInclude>' + else: + + if args[0] == '$': + # it's a placeholder, substitute its value + if args.find('${') == -1: # it's a single $placeholder tag + includeString = templateObj.mapName(args[1:], + executeCallables=True) + else: # it's a ${...} placeholder tag + includeString = templateObj.mapName(parseArgs[2:-1], + executeCallables=True) + + elif args.startswith('"') or args.startswith("'"): + fileName = args[1:-1] + includeString = templateObj.getFileContents( fileName ) + + return includeString + + templateDef = RE.sub(subber, templateDef) + return templateDef + +def preProcessBlockDirectives(templateObj, templateDef): + """process the block directives""" + if not hasattr(templateObj, '_blocks'): + templateObj._blocks = {} + + def handleBlock(blockName, startTagMatch, endTagRE, + templateDef=templateDef, templateObj=templateObj): + + endTagMatch = endTagRE.search(templateDef) + blockContents = templateDef[startTagMatch.end() : endTagMatch.start()] + + if not hasattr(templateObj, '_blocks'): + templateObj._blocks = {} + + if not templateObj._blocks.has_key(blockName): + templateObj._blocks[blockName] = blockContents + + if templateObj._settings['includeBlockMarkers']: + markerStart = templateObj._settings['blockMarkerStart'] + markerEnd = templateObj._settings['blockMarkerEnd'] + + replaceString = markerStart[0] + blockName + markerStart[1] + \ + '#parse $blocks.' + blockName + '/#' + \ + markerEnd[0] + blockName + markerEnd[1] + else: + replaceString = '#include $blocks.' + blockName + '/#' + + return templateDef[0:startTagMatch.start()] + replaceString + \ + templateDef[endTagMatch.end():] + + ## handle the whitespace-gobbling blocks + + for startTagRE in templateObj._settings['extDelimeters']['blockDirectiveStart']: + + while startTagRE.search(templateDef): + startTagMatch = startTagRE.search(templateDef) + blockName = startTagMatch.group('blockName') + endTagRE = re.compile(r'^[\t ]*#end block[\t ]+' + blockName + + r'[\t ]*(?:\r\n|\n|\Z)|'+ + r'#end block[\t ]+' + blockName + + r'[\t ]*(?:/#|\r\n|\n|\Z)', + re.DOTALL | re.MULTILINE) + templateDef = handleBlock(blockName, startTagMatch, endTagRE, + templateDef=templateDef) + + return templateDef + + +def preProcessSetDirectives(templateObj, templateDef): + """escape $vars in the directives, so the placeholderTagProcessor doesn't + picked them up""" + + def subber(match): + directive = match.group() + return re.sub(r'(?:(?<=\A)|(?<!\\))\$',r'\$',match.group()) + + for delimStruct in \ + templateObj._settings['codeGenerator']['coreTags']['setDirective']['delims']: + + regex = delimStruct['placeholderRE'] + templateDef = regex.sub(subber, templateDef) + + return templateDef + +def postProcessRawIncludeDirectives(templateObj, templateDef): + """replace any include statements with their substitution value. This + method can handle includes from file (absolute paths only at the moment) and + from $placeholders such as $getBodyTemplate""" + + def subber(match, templateObj=templateObj): + args = match.group(1).strip() + # do a safety/security check on this tag + validateIncludeDirective(templateObj, args) + + includeString = match.group(1).strip() + + if args[0] == '$': + # it's a $placeholder tag, substitute its value + if args.find('${') == -1: + # it's a single $placeholder tag + includeString = templateObj.mapName(args[1:], + executeCallables=True) + else: # it's a ${...} placeholder tag + includeString = templateObj.mapName(args[2:-1], + executeCallables=True) + elif args.startswith('"') or args.startswith("'"): + fileName = args[1:-1] + includeString = templateObj.getFileContents( fileName ) + + return includeString + + templateDef = re.sub(r'<CheetahRawInclude>(.+)' + + r'</CheetahRawInclude>', + subber, templateDef) + return templateDef + + +def preProcessDisplayLogic(templateObj, templateDef): + """swap $ for \$ in the displayLogic, so the placeholderTagProcessor doesn't + picked them up""" + + def subber(match): + return re.sub(r'(?:(?<=\A)|(?<!\\))\$',r'\$',match.group()) + + for delim in \ + templateObj._settings['codeGenerator']['coreTags']['displayLogic']['delims']: + + templateDef = delim['placeholderRE'].sub(subber, templateDef) + + return templateDef + + +## codeGenerator plugins for processing each of the token-prefixed tags ## + +def cacheDirectiveStartTagProcessor(templateObj, directive): + if not templateObj._codeGeneratorState.has_key('defaultCacheType'): + templateObj._codeGeneratorState['defaultCacheType'] = NoDefault + + directive = directive.strip() + + if not directive: + templateObj._codeGeneratorState['defaultCacheType'] = STATIC_CACHE + else: + templateObj._codeGeneratorState['defaultCacheType'] = TIMED_REFRESH_CACHE + templateObj._codeGeneratorState['cacheRefreshInterval'] = float(directive) + return "''" + +def cacheDirectiveEndTagProcessor(templateObj, directive): + templateObj._codeGeneratorState['defaultCacheType'] = NoDefault + return "''" + +def setDirectiveTagProcessor(templateObj, directive): + """generate python code from setDirective tags, and register the vars with + placeholderTagProcessor as local vars.""" + validateSetDirective(templateObj, directive) + + firstEqualSign = directive.find('=') + varName = directive[0: firstEqualSign].replace('$','').strip() + valueString = directive[firstEqualSign+1:] + valueString = processPlaceholdersInString(templateObj, valueString) + templateObj._localVarsList.append(varName) + + indent = templateObj._settings['codeGenerator']['indentationStep'] + if not templateObj._codeGeneratorState.has_key('indentLevel'): + templateObj._codeGeneratorState['indentLevel'] = \ + templateObj._settings['codeGenerator']['initialIndentLevel'] + + return indent*(templateObj._codeGeneratorState['indentLevel']) + varName + \ + "=" + valueString + "\n" + \ + indent * templateObj._codeGeneratorState['indentLevel'] + +def placeholderTagProcessor(templateObj, tag, convertToString=True, + cacheType=NoDefault, cacheRefreshInterval=15): + """generate the python code that will be evaluated for an embedded name + during each request. + + By default all $vars are cached except 'components', nested templates, and + vars that have been set locally in for loops or with the #set directive. To + make the $var completely dynamic so it is reinterpolated during each request + use the form $*var. For timed-refresh interpolation used + $*[timeInterval]*var -> $*15*var. """ + + ## setup a reference to templateObj so $placeholders in argstrings can be eval'd here + self = templateObj + + ## do the rest of the setup + if not hasattr(templateObj,'_perResponseSetupCodeChunks'): + templateObj._perResponseSetupCodeChunks = {} + if not templateObj._perResponseSetupCodeChunks.has_key('placeholders'): + ## setup the code to be included at the beginning of each response ## + indent = templateObj._settings['codeGenerator']['indentationStep'] * \ + templateObj._settings['codeGenerator']['initialIndentLevel'] + + templateObj._perResponseSetupCodeChunks['placeholders'] = \ + indent + "if self._checkForCacheRefreshes:\n"\ + + indent * 2 + "currTime = currentTime()\n"\ + + indent * 2 + "self._timedRefreshList.sort()\n"\ + + indent * 2 + "if currTime >= self._timedRefreshList[0][1]:\n"\ + + indent * 3 + " self._timedRefresh(currTime)\n"\ + + indent + " \n" \ + + indent + "timedRefreshCache = self._timedRefreshCache\n" \ + + indent + "callableNames = self._callableNamesCache\n" \ + + indent + "nestedTemplates = self._nestedTemplatesCache\n" \ + + indent + "components = self._componentsDict\n" + + ## initialize the caches, the localVarsList, and the timedRefreshList + templateObj._timedRefreshCache = {} # caching timedRefresh vars + templateObj._callableNamesCache = {} # caching name mappings that are callable + templateObj._nestedTemplatesCache = {} # caching references to nested templates + templateObj._componentsDict = {} # you get the idea... + templateObj._timedRefreshList = [] + templateObj._checkForCacheRefreshes = False + if not hasattr(templateObj, '_localVarsList'): + # may have already been set by #set or #for + templateObj._localVarsList = [] + + if not templateObj._codeGeneratorState.has_key('defaultCacheType'): + templateObj._codeGeneratorState['defaultCacheType'] = NoDefault + + ## Check for an argString in the tag ## + firstParenthesis = tag.find('(') + if firstParenthesis != -1: + argString = tag[firstParenthesis+1:-1] + + #@@ disabled for the time-being + #argString = processPlaceholdersInString(templateObj, argString) + validateArgStringInPlaceholderTag(templateObj, argString) + varName = tag[0:firstParenthesis] + else: + varName = tag + argString = '' + + ## check for caching of the $placeholder ## + if cacheType == NoDefault: + if not templateObj._codeGeneratorState['defaultCacheType'] == NoDefault: + cacheType = templateObj._codeGeneratorState['defaultCacheType'] + if cacheType == TIMED_REFRESH_CACHE: + cacheRefreshInterval = \ + templateObj._codeGeneratorState['cacheRefreshInterval'] + else: + cacheType = NO_CACHE + + if varName.find('*') != -1: + if re.match(r'\*[A-Za-z_]',varName.strip()): + # it's a static cached $placeholder + cacheType = STATIC_CACHE + varName = (varName.split('*'))[1] + else: + # timedRefresh dynamic var: $*interval*varName -> $*15*var + cacheType = TIMED_REFRESH_CACHE + cacheRefreshInterval, varName = varName[1:].split('*') + cacheRefreshInterval = float(cacheRefreshInterval) + + if cacheType == TIMED_REFRESH_CACHE: + templateObj._setTimedRefresh(varName, cacheRefreshInterval) + + ##deal with local vars from #set and #for directives + splitVarName = varName.split('.') + if varName in templateObj._localVarsList: + if argString: + processedTag = varName + '(' + argString + ')' + else: + processedTag = varName + if convertToString: + processedTag = 'str(' + processedTag + ')' + return processedTag + + elif splitVarName[0] in templateObj._localVarsList: + if argString: + processedTag = 'valueForName(' + splitVarName[0] + ',"""' + \ + '.'.join(splitVarName[1:]) + '""")(' + argString + ')' + else: + processedTag = 'valueForName(' + splitVarName[0] + ',"""' + \ + '.'.join(splitVarName[1:]) + '""", exectuteCallables=True)' + if convertToString: + processedTag = 'str(' + processedTag + ')' + return processedTag + + + ## find a value for the $placeholder + try: + binding = templateObj.mapName(varName) + except NameMapper.NotFound: + return templateObj._settings['varNotFound_handler'](templateObj, tag) + + + ## generate the Python code that will evaluate to the value of the placeholder + + if isinstance(binding, Component): + templateObj._componentsDict[varName] = binding + processedTag = 'components["' + varName + '"](trans, templateObj=self)' + + elif (type(binding) == types.MethodType and isinstance(binding.im_self, + Component)): + # it's a method of a component + templateObj._componentsDict[varName] = binding + processedTag = 'components["' + varName + '"](trans, templateObj=self)' + + elif isinstance(binding, Template.Template): + templateObj._nestedTemplatesCache[varName] = binding.__str__ + processedTag = 'nestedTemplates["' + varName + '"](trans, iAmNested=True)' + + elif cacheType == STATIC_CACHE: + if callable(binding): + if argString: + value = "'''" + str(eval('binding(' + argString + \ + ')')).replace("'''",r"\'\'\'") + "'''" + else: + value = "'''" + str(binding()).replace("'''",r"\'\'\'") + "'''" + else: + value = "'''" + str(binding).replace("'''",r"\'\'\'") + "'''" + processedTag = value + + elif cacheType == TIMED_REFRESH_CACHE: + if callable(binding): + if argString: + varName = varName + '(' + argString + ')' + binding = templateObj._timedRefreshCache[varName] = \ + str(eval('binding(' + argString + ')')) + else: + binding = templateObj._timedRefreshCache[varName] = str(binding()) + else: + templateObj._timedRefreshCache[varName] = str(binding) + + processedTag = 'timedRefreshCache["""' + varName + '"""]' + + elif callable(binding): + templateObj._callableNamesCache[varName] = binding + if argString: + processedTag = 'str(callableNames["' + varName + '"](' + \ + argString + '))' + else: + if (not type(binding()) == types.StringType) and convertToString: + processedTag = 'str(callableNames["' + varName + '"]())' + else: + processedTag = 'callableNames["' + varName + '"]()' + + else: + if (not type(binding) == types.StringType) and convertToString: + processedTag ='str( self.mapName("' + varName + '") )' + else: + processedTag ='self.mapName("' + varName + '")' + ## + return processedTag + + +def displayLogicTagProcessor(templateObj, displayLogic): + """process display logic embedded in the template""" + + settings = templateObj._settings + indent = settings['codeGenerator']['indentationStep'] + + displayLogic = displayLogic.strip() + validateDisplayLogicCode(templateObj, displayLogic) + + if not hasattr(templateObj, '_localVarsList'): + # may have already been set by #set or #for + templateObj._localVarsList = [] + + if not templateObj._codeGeneratorState.has_key('indentLevel'): + templateObj._codeGeneratorState['indentLevel'] = \ + settings['codeGenerator']['initialIndentLevel'] + + if displayLogic.lower() in \ + settings['codeGenerator']['displayLogicblockEndings']: + + templateObj._codeGeneratorState['indentLevel'] -= 1 + outputCode = indent*templateObj._codeGeneratorState['indentLevel'] + + elif displayLogic.lower()[0:4] in ('else','elif'): + displayLogic = displayLogic.replace('else if','elif') + + if displayLogic.lower()[0:4] == 'elif': + displayLogic = processPlaceholdersInString(templateObj, displayLogic) + + outputCode = indent*(templateObj._codeGeneratorState['indentLevel']-1) + \ + displayLogic +":\n" + \ + indent*templateObj._codeGeneratorState['indentLevel'] + + elif re.match(r'if +|for +', displayLogic): # it's the start of a new block + templateObj._codeGeneratorState['indentLevel'] += 1 + + if displayLogic[0:3] == 'for': + ##translate this #for $i in $list/# to this #for i in $list/# + INkeywordPos = displayLogic.find(' in ') + displayLogic = displayLogic[0:INkeywordPos].replace('$','') + \ + displayLogic[INkeywordPos:] + + ## register the local vars in the loop with the templateObj ## + # so placeholderTagProcessor will recognize them + # and handle their use appropriately + localVars, restOfForStatement = displayLogic[3:].split(' in ') + localVarsList = [localVar.strip() for localVar in + localVars.split(',')] + templateObj._localVarsList += localVarsList + + displayLogic = processPlaceholdersInString(templateObj, displayLogic) + outputCode = indent*(templateObj._codeGeneratorState['indentLevel']-1) + \ + displayLogic + ":\n" + \ + indent*templateObj._codeGeneratorState['indentLevel'] + + else: # it's a chunk of plain python code + outputCode = indent*(templateObj._codeGeneratorState['indentLevel']) + \ + displayLogic + \ + "\n" + indent*templateObj._codeGeneratorState['indentLevel'] + + return outputCode + +def processPlaceholdersInString(templateObj, string, replaceWithValue=False, + wrapInQuotes=False): + """search string for placeholders and return the python code needed to + access those placeholders, or the value of the placeholders if + replaceWithValue==True""" + + def subber(match, templateObj=templateObj, replaceWithValue=replaceWithValue, + wrapInQuotes=wrapInQuotes): + + if replaceWithValue: + if wrapInQuotes: + return '"""' + str( + templateObj.mapName(match.group(1), + executeCallables=True) + ) + '"""' + else: + return templateObj.mapName(match.group(1), + executeCallables=True) + else: + return placeholderTagProcessor(templateObj, match.group(1), + convertToString=False, + cacheType=NO_CACHE) + + for delimStruct in \ + templateObj._settings['codeGenerator']['coreTags']['placeholders']['delims']: + + string = delimStruct['placeholderRE'].sub(subber, string) + + return string + + +## codeGenerator plugins for final filtering of the generated code ## + +def addPerResponseCode(templateObj, generatedCode): + """insert the setup code that must be executed at the beginning of each + request. + + This code has been contributed by the tagProcessors and is stored as chunks + in the dictionary templateObj._perResponseSetupCodeChunks""" + + if not hasattr(templateObj,'_perResponseSetupCodeChunks'): + return generatedCode + + indent = templateObj._settings['codeGenerator']['indentationStep'] * \ + templateObj._settings['codeGenerator']['initialIndentLevel'] + perResponseSetupCode = '' + for tagProcessor, codeChunk in templateObj._perResponseSetupCodeChunks.items(): + perResponseSetupCode += codeChunk + + def insertCode(match, perResponseSetupCode=perResponseSetupCode): + return match.group() + perResponseSetupCode + + return re.sub(r'#setupCodeInsertMarker\n', insertCode , generatedCode) + + +def removeEmptyStrings(templateObj, generatedCode): + """filter out the empty-string entries that creep in between adjacent + tags""" + + generatedCode = generatedCode.replace(", '''''', ",', ') + generatedCode = generatedCode.replace("''', '''",'') + generatedCode = re.sub(r"\s*outputList \+= \['''''',\]\n", '\n', + generatedCode) + return generatedCode + + +## varNotFound handlers ## +def varNotFound_echo(templateObj, tag): + return "'''$" + tag + "'''" + +def varNotFound_bigWarning(templateObj, tag): + return "'''" + "="*15 + "<$" + tag + " could not be found>" + \ + "="*15 + "'''" diff --git a/src/Compiler.py b/src/Compiler.py new file mode 100644 index 0000000..1d11754 --- /dev/null +++ b/src/Compiler.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python +# $Id: Compiler.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ +"""A command line compiler for turning Cheetah files (.tmpl) into Webware +servlet files (.py). + +Meta-Data +================================================================================ +Author: Tavis Rudd <tavis@calrudd.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## + +import sys +import re +import os +import getopt +from os.path import \ + walk as pathWalk, \ + split as pathSplit, \ + splitext as pathSplitext, \ + exists + +from glob import glob + +#intra-package imports ... +from Delimeters import delimeters +from Version import version + +################################################## +## GLOBALS & CONTANTS ## + +True = (1==1) +False = (1==0) + +CHEETAH_EXTENSION = '.tmpl' +SERVLET_EXTENSION = '.py' +SERVLET_BACKUP_EXT = '.py_bak' + +extendDirectiveRE = delimeters['extendDirective'] + +################################################## +## FUNCTIONS ## + +def wrapTemplateCode(templateExt, name): + """Wrap a template definition string in the boiler-plate code needed to create a + Webware servlet.""" + + parentTemplate = [False,] + def extendDirectiveProcessor(match, parentTemplate=parentTemplate): + """process any #redefine directives that are found in the template extension""" + parentTemplate[0] = match.group('parent').strip() + return '' # strip the directive from the extension + + templateExt = extendDirectiveRE.sub(extendDirectiveProcessor, templateExt) + parentTemplate = parentTemplate[0] + + if parentTemplate: + parentServlet = parentTemplate.split('.')[-1] + servletCode = "templateExt = '''" + templateExt + servletCode += """''' +from %(parentModule)s import %(parentServlet)s + +class %(name)s(%(parentServlet)s): + def initializeTemplate(self): + %(parentServlet)s.initializeTemplate(self) + self.extendTemplate(templateExt) + +""" % {'parentModule': parentTemplate, + 'parentServlet': parentServlet, + 'name': name + } + else: + servletCode = "templateExt = '''" + templateExt + servletCode += """''' +from Cheetah.Servlet import TemplateServlet + +class %(name)s(TemplateServlet): + def __init__(self): + TemplateServlet.__init__(self, template=templateExt) + +""" % {'name':name} + + return servletCode + + +def compileDir(dirName='.', backupServletFiles=True): + """Compile all the Cheetah files in a directory.""" + + cheetahFiles = glob(dirName + '/*' + CHEETAH_EXTENSION) + namesMinusExt = [pathSplitext(fileName)[0] for fileName in cheetahFiles] + + if backupServletFiles: + for i in range(len(namesMinusExt)): + if exists( namesMinusExt[i] + SERVLET_EXTENSION): + print 'backing up', namesMinusExt[i] + SERVLET_EXTENSION + os.rename(namesMinusExt[i] + SERVLET_EXTENSION, + namesMinusExt[i] + SERVLET_BACKUP_EXT) + + for name in namesMinusExt: + compileFile(name) + +def compileFile(fileNameMinusExt): + """Compile an single Cheetah file. """ + + fp = open(fileNameMinusExt + CHEETAH_EXTENSION) + templateExt = fp.read() + fp.close() + servletName = pathSplit(fileNameMinusExt)[1] + servletCode = wrapTemplateCode(templateExt, servletName) + print 'compiled ', fileNameMinusExt + CHEETAH_EXTENSION, 'to', fileNameMinusExt + SERVLET_EXTENSION + + fp = open(fileNameMinusExt + SERVLET_EXTENSION,'w') + fp.write(servletCode) + fp.close + +def recursiveCompile(dir='.', backupServletFiles=True): + """Recursively walk through a directory tree and compile Cheetah files.""" + pending = [dir] + while pending: + dir = pending.pop() + ## add sub-dirs + for shortname in os.listdir(dir): + path = os.path.join(dir, shortname) + if os.path.isdir(path): + pending.append(path) + + ## do it! + compileDir(dir, backupServletFiles=backupServletFiles) + +class MainProgram: + """A command line interface class.""" + + def run(self): + """The main program controller.""" + try: + opts, args = getopt.getopt( sys.argv[1:], 'd:R:', []) + + except getopt.GetoptError: + # print help information and exit: + self.usage() + sys.exit(2) + + if not opts: + try: + fileName = args[0] + except: + self.usage() + sys.exit(2) + + servletName = pathSplitext(pathSplit(fileName)[1])[0] + fp = open(fileName) + templateExt = fp.read() + fp.close() + print wrapTemplateCode(templateExt, servletName) + + for o, a in opts: + if o in ('-h',): + self.usage() + sys.exit() + if o in ('-R',): + recursiveCompile(a) + sys.exit() + if o in ('-d',): + compileDir(a) + sys.exit() + + + def usage(self): + print \ +"""Cheetah %(version)s Command-Line Compiler by %(author)s + +Compiles Cheetah files (.tmpl) into Webware servlet files (.py) + +Usage: + %(scriptName)s filename ---> compile 'filename', output to stdout + %(scriptName)s -h ---> print this help and exit + %(scriptName)s -d dir ---> compile all files in dir, output to new files + %(scriptName)s -R dir ---> same as -d, but operates recursively on subdirs + +""" % {'scriptName':sys.argv[0], + 'version':version, + 'author':'Tavis Rudd', + } + + + +################################################## +## if run from the command line ## +if __name__ == '__main__': + + MainProgram().run() + + diff --git a/src/Components.py b/src/Components.py new file mode 100644 index 0000000..629ccc4 --- /dev/null +++ b/src/Components.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# $Id: Components.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ +"""Components that can be used with the Cheetah component framework + +See the Cheetah User's Guide for more information on components. + +Meta-Data +================================================================================ +Author: Tavis Rudd <tavis@calrudd.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + + +################################################## +## CLASSES ## + +class Component: + """an abstract base-class for Cheetah components""" + + def __call__(self, transaction=None, templateObj=None): + """this method is called by the Cheetah when a component is embedded + in a template. Each component must reimplement this method. + + In order to allow command-line debugging, the component should be equiped with + default settings to handle cases where the transaction==None. The code below + is a silly example""" + + if transaction: + return 'component called: ' + str( transaction.request().fields() ) + else: + return 'component called' + + diff --git a/src/Delimeters.py b/src/Delimeters.py new file mode 100644 index 0000000..e674fcc --- /dev/null +++ b/src/Delimeters.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python +# $Id: Delimeters.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ +"""A dictionary of delimeter regular expressions that are used in Cheetah + +Meta-Data +================================================================================ +Author: Tavis Rudd <tavis@calrudd.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + + +################################################## +## DEPENDENCIES ## + +import re + +################################################## +### CONSTANTS & GLOBALS ### + +delimeters = { + # A library of delimStructs that can be used for tagsets + # Each delimStruct contains the following items: + # start, startEscaped, end, endEscaped, and placeholderRE + + '$':{'start': r'$', + 'startEscaped': r'\$', + 'end': None, + 'endEscaped': None, + 'placeholderRE': re.compile(r"(?:(?<=\A)|(?<!\\))\$((?:\*|(?:\*[0-9\.]*\*)){0,1}" + + r"[A-Za-z_](?:[A-Za-z0-9_\.]*[A-Za-z0-9_]+)*" + + r"(?:\(.*?\))*)", + re.DOTALL) + }, + + '${,}':{'start': r'${', + 'startEscaped': r'\${', + 'end': r'}', + 'endEscaped': None, + 'placeholderRE': re.compile(r"(?:(?<=\A)|(?<!\\))\${(.+?)}",re.DOTALL) + }, + + '$[,]':{'start': r'$[', + 'startEscaped': r'\$[', + 'end': r']', + 'endEscaped': None, + 'placeholderRE': re.compile(r"\$\[(.+?)\]",re.DOTALL) + }, + + '$(,)':{'start': r'$(', + 'startEscaped': r'\$(', + 'end': r')', + 'endEscaped': None, + 'placeholderRE': re.compile(r"\$\((.+?)\)",re.DOTALL) + }, + + '[%,%]':{'start': r'[%', + 'startEscaped': r'\[%', + 'end': r'%]', + 'endEscaped': r'\%]', + 'placeholderRE': re.compile(r"\[%(.+?)%\]",re.DOTALL) + }, + + '{,}':{'start': r'{', + 'startEscaped': r'\{', + 'end': r'}', + 'endEscaped': r'\}', + 'placeholderRE': re.compile(r"{(.+?)}",re.DOTALL) + }, + + 'xml':{'start': r'<Cheetah>', + 'startEscaped': '<\Cheetah>', + 'end': r'</Cheetah>', + 'endEscaped': r'<\/Cheetah>', + 'placeholderRE': re.compile(r"<Cheetah>(.+?)</Cheetah>", + re.DOTALL) + }, # the tags must be exactly as shown here, no spaces + + '<%,%>':{'start': r'<%', + 'startEscaped': r'<\%', + 'end': r'%>', + 'endEscaped': r'\%>', + 'placeholderRE': re.compile(r"<%(.+?)%>",re.DOTALL) + }, + + '<#,#>':{'start': r'<#', + 'startEscaped': r'\<#', + 'end': r'#>', + 'endEscaped': r'\#>', + 'placeholderRE': re.compile(r"<#(.*?)#>",re.DOTALL) + }, + + + # the suffix _gobbleWS stands for gobble whitespace - any directive on a + # line by itself will have all preceeding and trailing WS on that line + # gobbled up with the directive + + 'slurpDirective_gobbleWS': re.compile(r'(?:\A|^)[\t ]*#slurp[\t ]*' + + r'(?:\r\n|\n|\Z)', + re.MULTILINE), + 'slurpDirective': re.compile(r'(?:(?<=\A)|(?<!\\))#slurp[\t ]*(?:\r\n|\n|\Z)'), + + 'singleLineComment':re.compile(r'(?:\A|^)[\t ]*##(.*?)\n|' + + r'(?:(?<=\A)|(?<!\\))##(.*?)$', #this one doesn't gobble the \n !!! + re.MULTILINE), + + 'multiLineComment': re.compile(r'(?:(?<=\A)|(?<!\\))#\*' + + r'(.*?)' + + r'\*#', + re.DOTALL | re.MULTILINE), + + 'displayLogic_gobbleWS':{'start': r'^#', + 'startEscaped': None, + 'end': r'\n', + 'endEscaped': None, + 'placeholderRE': re.compile(r'(?:\A|^)[\t ]*#(' + + r'if[\t ]+[^(?:/#)]+?|' + + r'else[\t ]*?|' + + r'else[\t ]if[\t ]+[^(?:/#)]+?|' + + r'elif[\t ]+[^(?:/#)]+?|' + + r'for[\t ][^(?:/#)]+?|' + + r'end if|' + + r'end for|' + + r')[\t ]*(?:\n|\r\n|\Z)', + re.MULTILINE + ) + }, + + 'displayLogic':{'start': r'#', + 'startEscaped': None, + 'end': r'/#', + 'endEscaped': r'\/#', + 'placeholderRE': re.compile(r'(?:(?<=\A)|(?<!\\))#(' + + r'if[\t ]+.+?|' + + r'else[\t ]*?|' + + r'else[\t ]if[\t ]+.+?|' + + r'elif[\t ]+.+?|' + + r'for[\t ].+?|' + + r'end if|' + + r'end for|' + + r')[\t ]*(?:/#|\n|\r\n|\Z)', + re.MULTILINE + ) + }, + + 'setDirective':{'start': r'#set', + 'startEscaped': r'\#set', + 'end': r'\n', + 'endEscaped': None, + 'placeholderRE': re.compile(r'(?:(?<=\A)|(?<!\\))#set[\t ]+(.+?)(?:/#|\r\n|\n|\Z)') + }, + + 'cacheDirectiveStartTag':{'start': r'#cache', + 'startEscaped': r'\#cache', + 'end': r'\n', + 'endEscaped': None, + 'placeholderRE': re.compile(r'(?:(?<=\A)|(?<!\\))' + + r'#cache(.*?)(?:/#|\r\n|\n|\Z)') + }, + + 'cacheDirectiveEndTag':{'start': r'#cache', + 'startEscaped': r'\#cache', + 'end': r'\n', + 'endEscaped': None, + 'placeholderRE': re.compile(r'(?:(?<=\A)|(?<!\\))' + + r'#end cache(.*?)(?:/#|\r\n|\n|\Z)') + }, + + ## The following directive delimeters are not intended to be used in the same manner + # as the rest of the delim structs above. The placeholderRE is the only item of + # interest here as they are only used in the pre/post-processing stages. + + 'includeDirective_gobbleWS': re.compile(r'(?:\A|^)[\t ]*#include[\t ]+' + + r'([^(?:/#)]+?)(?:\r\n|\n|\Z)', + re.MULTILINE), + 'includeDirective': re.compile(r'(?:(?<=\A)|(?<!\\))#include[\t ]+(.+?)(?:/#|\r\n|\n|\Z)'), + + + ## no gobbleWS for stop and restart directives!!! + # manage this explicitly if you need + 'rawDirective': re.compile(r'(?:(?<=\A)|(?<!\\))#raw[\t ]*(?:/#|\r\n|\n|\Z)(.*?)' + + r'(?:(?:#end raw[\t ]*(?:/#|\r\n|\n)|$)|\Z)', + re.DOTALL), + + ## there is also no gobbleWS for macro defs. + # They must be on lines by themselves!!! + # @@doc this + 'macroDirective': re.compile(r'(?:\A|^)[\t ]*#macro[\t ]+' + + r'(.+?)(?:/#|\r\n|\n)(.*?)' + + r'(?:\r\n|\n)[\t ]*#end macro[\t ]*(?:\r\n|\n|\Z)', + re.DOTALL | re.MULTILINE), + + # macroCalls should NOT gobbleWS - so that's why there aren't gobbleWS version + + 'callMacro': re.compile(r'(?:(?<=\A)|(?<!\\))#callMacro[\t ]+' + + r'(?P<macroName>[A-Za-z_][A-Za-z_0-9]*?)' + + r'\((?P<argString>.*?)\)[\t ]*(?:/#|\r\n|\n)' + + r'(?P<extendedArgString>.*?)' + + r'#end callMacro[\t ]*(?:/#|\r\n|\n|\Z)', + re.DOTALL | re.MULTILINE), + + 'callMacroArgs': re.compile(r'#arg[\t ]+' + + r'(?P<argName>[A-Za-z_][A-Za-z_0-9]*?)' + + r'[\t ]*(?:/#|\r\n|\n)' + + r'(?P<argValue>.*?)' + + r'(?:\r\n|\n)[\t ]*#end arg[\t ]*(?:/#|\r\n|\n)', + re.DOTALL | re.MULTILINE), + + 'lazyMacroCalls':re.compile(r'((?:(?<=\A)|(?<!\\))#[a-zA-Z_][a-zA-Z_0-9\.]*\(.*?\))'), + #'macroCalls':re.compile(r'((?<!#)#[a-zA-Z_][a-zA-Z_0-9\.]*\(.*?\))[\t ]*(?:\n|/#)'), + + + # the block directives are handled differently from the macro directives, etc. + # to avoid maximum recursion limit errors when the content of the block is + # large. The end tag is dynamically generated by the blockDirective processor + 'blockDirectiveStart_gobbleWS': re.compile(r'(?:\A|^)[\t ]*#block[\t ]+' + + r'(?P<blockName>[A-Za-z_][A-Za-z_0-9]*?)' + + r'[\t ]*(?:\r\n|\n)' , + re.DOTALL | re.MULTILINE), + + 'blockDirectiveStart': re.compile(r'(?:(?<=\A)|(?<!\\))#block[\t ]+' + + r'(?P<blockName>[A-Za-z_][A-Za-z_0-9]*?)' + + r'[\t ]*(?:/#|\r\n|\n)' , + re.DOTALL | re.MULTILINE), + + 'dataDirective_gobbleWS': re.compile(r'(?:\A|^)[\t ]*#data[\t ]*(?P<args>.*?)' + + r'(?:/#|\r\n|\n)' + + r'(?P<contents>.*?)' + + r'#end data[\t ]*(?:/#|\r\n|\n|\Z)', + re.DOTALL | re.MULTILINE), + + 'dataDirective': re.compile(r'(?:(?<=\A)|(?<!\\))#data[\t ]*(?P<args>.*?)' + + r'(?:/#|\r\n|\n)' + + r'(?P<contents>.*?)' + + r'#end data[\t ]*(?:/#|\r\n|\n|\Z)', + re.DOTALL | re.MULTILINE), + + 'extendDirective':re.compile(r'(?:(?<=\A)|(?<!\\))#extend[\t ]+(?P<parent>.*?)' + + r'[\t ]*(?:/#|\r\n|\n|\Z)', re.DOTALL), + } + + + diff --git a/src/ErrorHandlers.py b/src/ErrorHandlers.py new file mode 100644 index 0000000..e63a226 --- /dev/null +++ b/src/ErrorHandlers.py @@ -0,0 +1,265 @@ +"""Error Handlers for Cheetah + +Meta-Data +================================================================================ +Author: Tavis Rudd <tavis@calrudd.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## + +import re +import sys +import types +from traceback import format_tb, format_exception + + +#intra-package imports ... +from Utilities import insertLineNums, getLines, lineNumFromPos +from Version import version + +################################################## +## CONSTANTS & GLOBALS ## + +True = (1==1) +False = (0==1) + +################################################## +## CLASSES ## + +class ErrorHandler: + """An abstract base class for Cheetah ErrorHandlers""" + + def __init__(self): + """collect some information about the exception and the server""" + self._localvars = localvars = sys.exc_traceback.tb_frame.f_locals + self._server = server = localvars['self'] + self._debug = server._settings['debug'] + + def format_exc(self): + return ''.join( format_exception(sys.exc_type, sys.exc_value, + sys.exc_traceback) ) + + def __str__(self): + """print the error message""" + fillValues ={'ver':version, + 'introText': self.introText(), + 'debugSettingWarning':self._debugSettingWarning(), + 'excType':sys.exc_type, + 'excValue':sys.exc_value, + 'errorDetails': self.errorDetails(), + } + + return \ +"""================================================================================ + Cheetah Version %(ver)s + +%(introText)s +%(debugSettingWarning)s + +Exception Type: %(excType)s +Exception Value: %(excValue)s + +More information about the error is printed below. The most general information +from the high-level error handlers is printed first, followed by more specific +information from the lower-level error handlers. A Python traceback is printed +at the very end. There may be some repetition of information. + +================================================================================ +%(errorDetails)s +""" % fillValues + + def introText(self): + return '' + + def errorDetails(self): + return '' + + def _debugSettingWarning(self): + if not self._debug: + + return "\nNOTE: you have Cheetah's debug setting set to " + \ + "False. TemplateServer will\nusually be more helpful " + \ + "with problems if you set debug to True." + + else: + return '' + +class ResponseErrorHandler(ErrorHandler): + """An error handler for exceptions raised during the Template serving + cycle""" + + def __init__(self): + ErrorHandler.__init__(self) + + def introText(self): + return """Cheetah compiled this template successfully, but an error +(aka Exception) occurred when Cheetah was executing the generated code.""" + + def errorDetails(self): + msg = self.format_exc() + msg += "\nHere's a copy of the code that Cheetah Template generated:\n\n" + msg += insertLineNums( self._server._generatedCode ) + + self._server._errorMsgStack.append(msg) + self._server._errorMsgStack.reverse() + return '\n'.join( self._server._errorMsgStack ) + + + + +class CodeGeneratorErrorHandler(ErrorHandler): + """The master ErrorHandler for Cheetah's codeGenerator.""" + def __init__(self): + ErrorHandler.__init__(self) + server = self._server + self._stage = stage = self._localvars['stage'] + self._stageSettings = server._settings['codeGenerator']['stages'][stage] + + def introText(self): + fillValues = {'stage': self._stage, + 'stageDescription':self._stageSettings['description'], + 'stageTitle':self._stageSettings['title'], + } + return \ +"""An error (aka Exception) occurred during code generation stage %(stage)d. +Stage %(stage)s, '%(stageTitle)s', is when %(stageDescription)s""" % fillValues + + def errorDetails(self): + self._stageSettings['errorHandler']() + self._server._errorMsgStack.reverse() + return '\n'.join( self._server._errorMsgStack ) + + + + + + +class StageErrorHandler(ErrorHandler): + """ErrorHandler base class for stages of Cheetah's codeGenerator.""" + def __init__(self): + ErrorHandler.__init__(self) + self._server._errorMsgStack.append( self.generateErrMsg() ) + + def generateErrMsg(self): + pass + +class Stage1ErrorHandler(StageErrorHandler): + """ErrorHandler for stage 1 of Cheetah's codeGenerator.""" + + def generateErrMsg(self): + msg = '\n' + msg += "TemplateServer was executing the " + \ + self._localvars['preProcessor'][0] + \ + " preProcessor when the error occurred.\n" + + if not self._localvars['preProcessor'][0] == 'parseDirectiveLoop': + msg += "This was the state of the template when the error " + \ + "occurred:\n\n" + msg += insertLineNums( self._localvars['template'] ) + + msg += '-'*80 + return msg + + + +class Stage2ErrorHandler(StageErrorHandler): + """ErrorHandler for stage 2 of Cheetah's codeGenerator.""" + + def generateErrMsg(self): + msg = '\n' + msg += "Cheetah was processing " + self._localvars['token'] + \ + " tags when the error occurred.\n" + msg += "This was the state of the template when the error occurred:\n\n" + msg += insertLineNums( self._localvars['template'] ) + msg += '-'*80 + return msg + + +class Stage3ErrorHandler(StageErrorHandler): + """ErrorHandler for stage 3 of Cheetah's codeGenerator.""" + + def generateErrMsg(self): + msg = '\n' + msg += "Cheetah was executing the " + \ + self._localvars['postProcessor'][0] + \ + " postProcessor when the error occurred.\n" + msg += "This was the state of the template when the error occurred:\n\n" + msg += insertLineNums( self._localvars['template'] ) + msg += '-'*80 + return msg + + +class Stage4ErrorHandler(StageErrorHandler): + """ErrorHandler for stage 4 of Cheetah's codeGenerator.""" + + def generateErrMsg(self): + msg = '\n' + if self._localvars['subStage'] == 'a': + msg += "Cheetah was separating tags from the normal text " + \ + "when the error occurred.\n" + msg += "This was the state of the template when the error occurred:\n\n" + msg += insertLineNums( self._localvars['template'] ) + + elif self._localvars['subStage'] == 'b': + msg += "Cheetah was generating code for each of the tags " + \ + "when the error occurred.\n" + msg += "This was the textVsTagList being processed:\n\n" + msg += str( self._localvars['textVsTagsList'] ) + msg += '-'*80 + msg += "\nThis was the state of the template when the error occurred:\n\n" + msg += insertLineNums( self._localvars['template'] ) + + else: + msg += "Cheetah was joining the code from each of the tags into " + \ + "a string when the error occurred.\n" + msg += "This was the list of code chunks (even) and normal text (odd) " + \ + "being joined:\n\n" + msg += str( self._localvars['codePiecesFromTextVsTagsList'] ) + msg += '-'*80 + + return msg + + +class Stage5ErrorHandler(StageErrorHandler): + """ErrorHandler for stage 5 of Cheetah's codeGenerator.""" + + def generateErrMsg(self): + msg = '\n' + msg += "Cheetah was wrapping the code from the tags up in a " + \ + "function definition when the error occurred.\n" + msg += "This was the code from that came from the tag processors:\n\n" + msg += self._localvars['codeFromTextVsTagsList'] + msg += '-'*80 + return msg + + +class Stage6ErrorHandler(StageErrorHandler): + """ErrorHandler for stage 6 of Cheetah's codeGenerator.""" + + def generateErrMsg(self): + msg = '\n' + msg += "Cheetah was executing the " + self._localvars['filter'][0] + \ + " filter when the error occurred.\n" + msg += "This was the state of the generated when the error occurred:\n\n" + msg += insertLineNums( self._localvars['generatedCode'] ) + msg += '-'*80 + return msg + + +class Stage7ErrorHandler(StageErrorHandler): + """ErrorHandler for stage 7 of Cheetah's codeGenerator.""" + def generateErrMsg(self): + generatedCode = self._localvars['generatedCode'] + msg = '\n' + msg += "Here's a copy of the code that Cheetah generated:\n\n" + msg += insertLineNums(generatedCode) + msg += '-'*80 + return msg diff --git a/src/Macros/HTML.py b/src/Macros/HTML.py new file mode 100644 index 0000000..1491f82 --- /dev/null +++ b/src/Macros/HTML.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# $Id: HTML.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $ +"""HTML macros for use with Cheetah templates + +Meta-Data +========== +Author: Tavis Rudd <tavis@calrudd.com>, +Version: $Revision: 1.1 $ +Start Date: 2001/04/05 +Last Revision Date: $Date: 2001/06/13 03:50:40 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## + +import time +import types +import os + +################################################## +## GLOBALS AND CONSTANTS ## + +True = (1==1) +False = (0==1) + +################################################## +## FUNCTIONS ## + +def currentYr(): + return time.strftime("%Y",time.localtime(time.time())) + +def spacer(width=1,height=1): + return '<IMG SRC="spacer.gif" WIDTH=%s HEIGHT=%s ALT="">'% (str(width), str(height)) + +def formHTMLTag(tagName, attributes={}): + """returns a string containing an HTML <tag> """ + tagTxt = ['<', tagName.upper()] + for name, val in attributes.items(): + if type(val)==types.StringType: + val = '"' + val + '"' + tagTxt += [' ', name.upper(), '=', str(val)] + tagTxt.append('>') + return ''.join(tagTxt) + +def formatMetaTags(metaTags): + """format a dict of metaTag definitions into an HTML version""" + metaTagsTxt = [] + for http_equiv, contents in metaTags['HTTP_EQUIV'].items(): + metaTagsTxt += ['<META HTTP_EQUIV="', str(http_equiv), '" CONTENTS="', + str(contents), '">\n'] + + for name, contents in metaTags['NAME'].items(): + metaTagsTxt += ['<META NAME="', str(name), '" CONTENTS="', str(contents), + '">\n'] + + return ''.join(metaTagsTxt) diff --git a/src/Macros/__init__.py b/src/Macros/__init__.py new file mode 100644 index 0000000..f745909 --- /dev/null +++ b/src/Macros/__init__.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# $Id: __init__.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $ +"""Macros for use with Cheetah templates + +Meta-Data +========== +Author: Tavis Rudd <tavis@calrudd.com>, +Version: $Revision: 1.1 $ +Start Date: 2001/04/05 +Last Revision Date: $Date: 2001/06/13 03:50:40 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## + + +################################################## +## GLOBALS AND CONSTANTS ## + +True = (1==1) +False = (0==1) + + +################################################## +## FUNCTIONS ## + + +################################################## +## CLASSES ## diff --git a/src/NameMapper.py b/src/NameMapper.py new file mode 100644 index 0000000..34b5cfe --- /dev/null +++ b/src/NameMapper.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python +# $Id: NameMapper.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ + +"""Utilities for accessing the members of an object via string representations +of those members. Template processing is its primary intended use. + +Description +================================================================================ +@@ add basic desc. + +This module is similar to Webware's NamedValueAccess, but with the following +basic differences: + +- you don't need to inherit the NamedValueAccess mixin to use it + +- the functionality of NamedValueAccess was abstracted into two standalone + functions: valueForName() and valueForKey(). valueForName() is recursive and + relies on valueForKey. + +- the mappings aren't cached in this version. We are investigating ways of + caching mappings for mutatible objects only. + +- any mapping object or object that works with python's getattr() builtin + function can be searched (classes, instances, dictionaries, btrees?, etc.), + therefore locals(), globals(), and __builtins__ all work + +- NamedValueAccess only works with methods. NameMapper works with any callable + object, such as plain functions or classes. + +- if a name maps to a callable object (function, method, class, etc.) + valueForName returns a reference to the object is returned instead of the + object's return value. The caller is responsible for dealing with it. This + allows the caller to cache the reference and execute it when ever needed. + +and some extra features: + +- nested objects can be descended into, for any depth, so long as nested objects + are dicts or work with getattr(). NamedValueAccess was limited to ojects that + inherited the NamedValueAccess mixin. + +Like NamedValueAccess: + +- you can provide a default value to substitute for names that can't be found. + If you don't provide a default and the name can't be found the exception + NameMapper.NotFound will be raised. This is similar to the .get() method of + dictionaries. + + +Usage +================================================================================ +This module is not safe for 'from NameMapper import *'! + +See the example at the bottom of this file. +The TemplateServer module implements a less trivial example. + +Changes +================================================================================ +1.10- got rid of the NameMapper class. There are now two normal functions, + valueForName and valueForKey, which do all the work. This brings the core + down to less than 60 lines of code, and greatly simplifies the interface. + +TODO +================================================================================ +- write the test cases + +Meta-Data +================================================================================ +Authors: Tavis Rudd <tavis@calrudd.com>, + Chuck Esterbrook <echuck@mindspring.com> +License: This software is released for unlimited distribution + under the terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/04/03 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>," +\ + "\nChuck Esterbrook <echuck@mindspring.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## + +import types +from types import StringType, InstanceType, ClassType +import re +# it uses the string methods and list comprehensions added in recent versions of python + +################################################## +## GLOBALS AND CONSTANTS ## + +True = (1==1) +False = (0==1) +digitsRE = re.compile(r'[0-9]+') +isDigit = digitsRE.match +digits = ['0','1','2','3','4','5','6','7','8','9'] + + +class NotFound(Exception): + pass + +class NoDefault: + pass + +################################################## +## FUNCTIONS ## + +def valueForName(obj, name, default=NoDefault, exectuteCallables=False): + """Get the value for the specified name. This function can be called + recursively. """ + + if type(name)==StringType: + # then this is the first call to this function. + nameChunks=name.split('.') + else: + #if this function calls itself then name already be a list of nameChunks + nameChunks = name + + ## go get a binding for the key ## + firstKey = nameChunks[0] + if len(nameChunks) > 1: + # its a composite name like: nestedObject.item + binding = valueForKey(obj, firstKey, default) + if callable(binding) and type(binding) not in (InstanceType, ClassType): + # the type check allows access to the methods of instances + # of classes with __call__() defined + # and also allows obj.__class__.__name__ + binding = binding() + + return valueForName(binding, nameChunks[1:], default, + exectuteCallables=exectuteCallables) + else: + # its a single key like: nestedObject + binding = valueForKey(obj, firstKey, default) + if exectuteCallables and callable(binding): + binding = binding() + return binding + + + +def valueForKey(obj, key, default=NoDefault): + """Get the value of the specified key. The key can be a mapping item, an + attribute or an underscored attribute.""" + + if hasattr(obj, key): + binding = getattr(obj, key) + elif hasattr(obj, '_' + key): + binding = getattr(obj, '_' + key) + else: + try: + if key in digits: + binding = obj[int(key)] + else: + binding = obj.get(key, default) + except: + binding = default + + if binding==NoDefault: + raise NotFound + else: + # this is a value or a reference to a callable object + return binding + + +def determineNameType(obj, name): + """Return the type of a name or raise an exception if it can't be found. + + This function is useful for caching in situations where a caller needs to + access the a NameMapper-style name repeatedly. Using this information, the + name can be translated into a standard python representation by the caller + on the first request. Every subsequent request will eval() the python + representation of the name, with is more efficient than using + valueForName(). + + Names can be of the following types: + - a 'callable' object. + In which case we just retrieve a reference of it and call it when needed + - a 'plain_attribute' + - an 'underscored_attribute' + - a 'mapping_key' + - raise NotFound + """ + + mapping = valueForName(obj,name) + if callable(mapping): + return 'callable' + else: + nameChunks = name.split('.') + if len(nameChunks) == 1: + return determineKeyType(obj, name) + else: + numChunks = len(nameChunks) + return determineKeyType( + valueForName(obj, "".join(nameChunks[0:numChunks-1])), + nameChunks[numChunks-1]) + + +def determineKeyType(obj, key): + if hasattr(obj, key): + if callable(getattr(obj, key)): + return 'callable' + else: + return 'plain_attribute' + elif hasattr(obj, '_' + key): + if callable(getattr(obj, '_' + key)): + return 'callable' + else: + return 'underscored_attribute' + elif hasattr(obj,'has_key'): + try: + if callable(obj.get(key)): + return 'callable' + else: + return 'mapping_key' + except: + raise NotFound + + + +################################################## +## CLASSES ## + +class Mixin: + """@@ document me""" + def valueForName(self, name, handleArgStrings=False, localsDict=None): + return valueForName(self, name, handleArgStrings=handleArgStrings, + localsDict=localsDict) + + def valueForKey(self, key, handleArgStrings=False, localsDict=None): + return valueForKey(self, key, handleArgStrings=handleArgStrings, + localsDict=localsDict) + +################################################## +## TEST ROUTINES ## +def test(): + import NameMapper.Tests + NameMapper.Tests.run_tests() + +################################################## +## if run from the command line ## + +def example(): + class A(Mixin): + classVar = 'classVar val' + def method(self,arg='method 1 default arg'): + return arg + + def method2(self, arg='meth 2 default arg'): + return {'item1':arg} + + def method3(self, arg='meth 3 default'): + return arg + + class B(A): + classBvar = 'classBvar val' + _underScoreVar = '_underScoreVar val' + + def _underScoreMethod(self): + return '_underScoreMethod output' + + a = A() + a.one = 'valueForOne' + def function(whichOne='default'): + values = { + 'default': 'default output', + 'one': 'output option one', + 'two': 'output option two' + } + return values[whichOne] + + a.dic = { + 'func': function, + 'method': a.method3, + 'item': 'itemval', + 'subDict': {'nestedMethod':a.method3} + } + b = 'this is local b' + + alist = ['item0','item1','item2'] + + print determineNameType(vars(),'B.underScoreVar') + print valueForKey(a.dic,'subDict','NotFound') + print valueForName(a, 'dic.item','NotFound') + print valueForName(vars(), 'b','NotFound') + print valueForName(__builtins__, 'dir','NotFound')() + print valueForName(vars(), 'a.classVar','NotFound') + print valueForName(vars(), 'B.underScoreVar','NotFound') + print valueForName(vars(), 'a.dic.func','NotFound') + print valueForName(vars(), 'a.method2.item1','NotFound') + print valueForName(vars(), 'alist.0','NotFound') + +if __name__ == '__main__': + example() + + + diff --git a/src/Plugins/Debugger.py b/src/Plugins/Debugger.py new file mode 100644 index 0000000..fff4d54 --- /dev/null +++ b/src/Plugins/Debugger.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# $Id: Debugger.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $ +"""Debugging tools for the TemplateServer package + +THIS FILE IS NOT READY FOR USE!! +It was created for an earlier version of TemplateServer and has not been updated yet. + +Meta-Data +========== +Author: Tavis Rudd <tavis@calrudd.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:40 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + + + +################################################## +## DEPENDENCIES ## +import time +from TemplateServer.Version import version + +################################################## +## CONSTANTS & GLOBALS ## + +True = (1==1) +False = (0==1) +currentTime = time.time # used in the cache refresh code + + +################################################## +## FUNCTIONS ## + + +## methods for obtaining meta-information about the template being servered ## + +def _tags(server, removeDuplicates=True): + """return a list of all TemplateServer tags in the template.""" + tags = server._settings['internalDelims']['placeholderRE'].findall(server._internalTemplate) + + if removeDuplicates: + tags = removeDuplicateValues(tags) + + tokens = [] + tagsList = {} + + for tag in tags: + token, tag = tag.split(server._settings['codeGenerator']['tagTokenSeparator']) + if tagsList.has_key(token): + tagsList[token].append(tag) + else: + tagsList[token] = [tag,] + + return tagsList + +def _names(server, removeDuplicates=True): + """return a list of all the NameMapper vars embedded in the template.""" + names = server._tags()['nameMapper'] + + if removeDuplicates: + names = removeDuplicateValues(names) + return names + +def _cachedNames(server): + """return a list of all NameMapper vars in the template that are cached""" + names = [name for name in filter(lambda name: name.find('*')!=-1 , server._names()) ] + return names + +def _uncachedNames(server): + """return a list of all NameMapper vars in the template that aren't cached""" + names = filter(lambda name: name.find('*')==-1 , server._names()) + return names + + +## methods to handle command-line usage ## + +def runAsMainProgram(server): + """Enables the template to function as a standalone command-line program for + static page generation and testing/debugging""" + + import getopt, sys + from pprint import pprint + + try: + opts, args = getopt.getopt( + sys.argv[1:], 'horiptnvdV', ['output','raw','afterParsedIncludes','afterIncludes', + 'internalTemplate', + 'pythonCode','tags','names','cached','uncached', + 'allnames','values','doc','help','version']) + except getopt.GetoptError: + # print help information and exit: + server._TemplateServerUsage() + sys.exit(2) + if not opts: + print server + + for o, a in opts: + if o in ('-h', '--help'): + server._TemplateServerUsage() + + if o in ('-o', '--output'): + print server + if o in ('-r','--raw'): + print server._rawTemplate + if o in ('--afterParsedIncludes',): + print server._rawTemplateAfterParsedIncludes + if o in ('-i','--afterIncludes'): + print server._rawTemplateAfterPlainIncludes + + if o in ('--internalTemplate',): + print server._internalTemplate + + if o in ('-p','--pythonCode'): + print server._templateFunctionDef + + if o in ('-t','--tags'): + #pprint( server._names() ) + pprint( server._tags() ) + + if o in ('-n','--names'): + #pprint( server._names() ) + pprint( server._tags()['nameMapper'] ) + if o in ('--cached',): + pprint( server._cachedNames() ) + if o in ('--uncached',): + pprint( server._uncachedNames() ) + if o in ('--allnames',): + pprint( server._names(removeDuplicates=False) ) + + if o in ('-v','--values',): + + print "="*80 + print "staticValuesCache (all names prefixed with *)" + print "="*80 + print + pprint( server._staticValuesCache ) + print + + print "="*80 + print "callableNamesCache (all names that map to methods, functions, etc.)" + print "="*80 + print + pprint( server._callableNamesCache ) + print + + print "="*80 + print "pluginsCache (all names that map to TemplatServer plugins)" + print "="*80 + print + pprint( server._plugins ) + print + + print "="*80 + print "nestedTemplatesCache (all names that map to other templates)" + print "="*80 + print + pprint( server._nestedTemplatesCache ) + print + + print "="*80 + print "timedRefreshList (all cached vars that are scheduled for an update)" + print "="*80 + print "current time in epoch seconds:", currentTime() + print + print "name, nextUpdateTime(secs-since-epoch), updateInterval(minutes)" + print "-"*80 + pprint( server._timedRefreshList ) + print + + if o in ('-d','--doc'): + import os + os.system('pydoc ./' + server.__filename__) + if o in ('-V', '--version'): + print version + + +def _TemplateServerUsage(): + """Print the TemplateServer command line usage information.""" + print \ +"""Usage: python %(scriptName)s [OPTIONS] + +TemplateServer %(version)s by %(author)s + + -h, --help print this help and exit + + -o, --output print the template's default output + -r, --raw print the raw template after processing the includes + -b, --beforeIncludes + print the raw template before processing the includes + -i, --internal print template's tranlation to the internal delimeters and exit + -p, --pythonCode print template's tranlation to python code and exit + + The following options show the variable names embedded in the + template with their original order maintained. + --------------------------------------------------------------- + -t, --tags print a list of all the tags in the template, grouped by type + + -n, --names print a list of the variable names in the template + Names prefixed with * are statically cached. + --cached print a list of the cached variable names + --uncached print a list of the uncached variable names + --allnames same as names, but shows duplicates + + -v --values print data stored internally in the cache dictionaries/lists: + *staticValuesCache + *callableNamesCache + *nestedTemplatesCache + *timedRefreshList + + -d, --doc display the templates documentation using pydoc + The author must have set the Template's + __filename__ attribute for this to work. + If this is not set, the docs for the last base class that + did set this attribute will be shown instead. + > pydoc ./THISFILENAME does the same thing. + + -V, --version print version information + +""" % {'scriptName':sys.argv[0], + 'version':version, + 'author':'Tavis Rudd', + } diff --git a/src/Plugins/PSP.py b/src/Plugins/PSP.py new file mode 100644 index 0000000..78988e6 --- /dev/null +++ b/src/Plugins/PSP.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# $Id: PSP.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $ +"""A plugin that allows Cheetah to handle PythonServerPages style coding + +Meta-Data +========== +Author: Tavis Rudd <tavis@calrudd.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:40 $ +""" + +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## + +from Cheetah.Delimeters import delimeters +from Cheetah.Template import EXEC_TAG_TYPE + +################################################## +## CONSTANTS & GLOBALS ## + +True = (1==1) +False = (0==1) + +################################################## +## FUNCTIONS ## + + +def pythonScriptTagProcessor(templateObj, pythonScript): + """Process PSP style code that is embedded in the template definition.""" + + pythonScript = pythonScript.strip() + settings = templateObj._settings + indent = settings['codeGenerator']['indentationStep'] + if not templateObj._codeGeneratorState.has_key('indentLevel'): + templateObj._codeGeneratorState['indentLevel'] = \ + settings['codeGenerator']['initialIndentLevel'] + + if pythonScript.lower() == 'end': # move out one indent level + templateObj._codeGeneratorState['indentLevel'] -= 1 + outputCode = indent*templateObj._codeGeneratorState['indentLevel'] + + elif pythonScript.lower()[0:4] in ('else','elif') or \ + pythonScript.lower().startswith('except'): + # continuation of previous block + + outputCode = indent*(templateObj._codeGeneratorState['indentLevel']-1) + \ + pythonScript +"\n" + \ + indent*templateObj._codeGeneratorState['indentLevel'] + + elif pythonScript[len(pythonScript.strip()) -1] == ":": + # it's the start of a new block + templateObj._codeGeneratorState['indentLevel'] += 1 + outputCode = indent*(templateObj._codeGeneratorState['indentLevel']-1) + \ + pythonScript + "\n" + \ + indent*templateObj._codeGeneratorState['indentLevel'] + + elif pythonScript.lower().startswith('='): # it's a python value eval() + outputCode = indent*(templateObj._codeGeneratorState['indentLevel']) + \ + "outputList += [str(" + pythonScript[1:] + "),]\n" + \ + indent*templateObj._codeGeneratorState['indentLevel'] + else: # it's a chunk of plain python code + outputCode = indent*(templateObj._codeGeneratorState['indentLevel']) + \ + pythonScript + "\n" + \ + indent*templateObj._codeGeneratorState['indentLevel'] + + return outputCode + + +################################################## +## CLASSES ## + +class PSPplugin: + """A plugin for Cheetah that allows PythonServerPages code (<%...%>, + <%=...%>) in templates""" + + def bindToTemplateServer(self, templateObj): + """insert the settings neccessary for PSP into the templateObj""" + pythonScriptSettingsDict = {'type':EXEC_TAG_TYPE, + 'processor':pythonScriptTagProcessor, + 'delims':[delimeters['<%,%>'], ], + } + templateObj._settings['codeGenerator']['coreTags']['pythonScript'] = \ + pythonScriptSettingsDict + + +################################################## +## test from command-line ## +if __name__ == '__main__': + from Cheetah.Template import Template + + templateDef = """ + Testing Cheetah's PSP plugin: + + $testVar + <% pspVar = 'X' %> + #set $list = [1,2,3] + + #for $i in map(lambda x: x*x*x, $list) + $i + <%=self.mapName('testVar')*15%> + <%for i in range(15):%> <%=i*15%><%=pspVar%><%end%> + #end for + """ + + print Template(templateDef, {'testVar':1234}, plugins=[PSPplugin()]) + + diff --git a/src/Plugins/__init__.py b/src/Plugins/__init__.py new file mode 100644 index 0000000..2014d08 --- /dev/null +++ b/src/Plugins/__init__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# $Id: __init__.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $ + +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] diff --git a/src/Servlet.py b/src/Servlet.py new file mode 100644 index 0000000..6e24a8f --- /dev/null +++ b/src/Servlet.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# $Id: Servlet.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ +"""An abstract base class for Cheetah Servlets that can be used with Webware + +Meta-Data +================================================================================ +Author: Tavis Rudd <tavis@calrudd.com>, +Version: $Revision: 1.1 $ +Start Date: 2001/04/05 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## + +import types + +# intra-package imports ... +from Template import Template +from Utilities import mergeNestedDictionaries +import CodeGenerator as CodeGen + +try: + from WebKit.HTTPServlet import HTTPServlet +except: + class HTTPServlet: + pass + +################################################## +## GLOBALS AND CONSTANTS ## + +True = (1==1) +False = (0==1) + +################################################## +## CLASSES ## + +class TemplateServlet(Template, HTTPServlet): + """An abstract base class for Cheetah servlets that can be used with + Webware""" + + def __init__(self, template='', *searchList, **kw): + """ """ + if not kw.has_key('settings'): + kw['settings']={} + kw['settings']['delayedStart'] = True + Template.__init__(self, template, *searchList, **kw) + self.initializeTemplate() + self.startServer() + + def initializeTemplate(self): + """a hook that can be used by subclasses to do things after the + Template has been initialized, but before it has been started + (i.e. before the codeGeneration process starts) """ + pass + + + + + diff --git a/src/SettingsManager.py b/src/SettingsManager.py new file mode 100644 index 0000000..811fc21 --- /dev/null +++ b/src/SettingsManager.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# $Id: SettingsManager.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $ +"""Provides a mixin class for managing settings dictionaries + +Meta-Data +========== +Author: Tavis Rudd <tavis@calrudd.com> +Version: $Revision: 1.1 $ +Start Date: 2001/05/30 +Last Revision Date: $Date: 2001/06/13 03:50:40 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + + +################################################## +## DEPENDENCIES ## + +from copy import deepcopy, copy + +#intra-package imports ... +from Utilities import mergeNestedDictionaries + +################################################## +## CONSTANTS & GLOBALS ## + +True = (1==1) +False = (1==0) + + +################################################## +## CLASSES ## + +class Error(Exception): + pass + +class NoDefault: + pass + +class SettingsManager: + """A mixin class that provides facilities for managing settings dictionaries.""" + + def initializeSettings(self): + """This dummy method should be reimplemented by subclasses.""" + self._settings = {} + + def updateSettings(self, newSettings, merge=True): + """Update the settings with a selective merge or a complete overwrite.""" + if merge: + self._settings = mergeNestedDictionaries(self._settings, newSettings) + else: + self._settings = newSettings + + def setting(self, name, default=NoDefault): + """Get a setting from self._settings, with or without a default value.""" + if default is NoDefault: + return self._settings[name] + else: + return self._settings.get(name, default) + + def setSetting(self, name, value): + """Set a setting in self._settings.""" + self._settings[name] = value + + def settings(self): + """Return a reference to the settings dictionary""" + return self._settings + + def copySettings(self): + """Returns a shallow copy of the settings dictionary""" + return copy(self._settings) + + def deepcopySettings(self): + """Returns a deep copy of the settings dictionary""" + return deepcopy(self._settings) + diff --git a/src/Template.py b/src/Template.py new file mode 100644 index 0000000..683400e --- /dev/null +++ b/src/Template.py @@ -0,0 +1,681 @@ +#!/usr/bin/env python +# $Id: Template.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ +"""Provides the core Template class for Cheetah +See the docstring in __init__.py and the User's Guide for more information + +Meta-Data +================================================================================ +Author: Tavis Rudd <tavis@calrudd.com>, + with code/advice from Ian Bicking, Mike Orr, Chuck Esterbrook and others + It was inspired by Chuck's RNV module <echuck@mindspring.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + + +################################################## +## DEPENDENCIES ## + +import os # used to get environ vars, etc. +import sys # used in the error handling code +import new # used to bind the compiled template code +import types # used in the mergeNewTemplateData method +import time # used in the cache refresh code +from time import time as currentTime # used in the cache refresh code + +# intra-package imports ... +from SettingsManager import SettingsManager +from NameMapper import valueForName, determineNameType +import NameMapper +import CodeGenerator as CodeGen +import ErrorHandlers +from Delimeters import delimeters as delims +from Utilities import \ + removeDuplicateValues, \ + mergeNestedDictionaries, \ + insertLineNums + +################################################## +## CONSTANTS & GLOBALS ## + +True = (1==1) +False = (0==1) + +EVAL_TAG_TYPE = 0 +EXEC_TAG_TYPE = 1 + + +# text descriptions of what each stage in TemplateServer._codeGenerator() does +stageDescriptions = { +1:"""the raw template is filtered using +the pre-processors specified in the TemplateServer settings.""", +2:"""the core tags ($, #set, #if, #for, +<%...%>, and <%=...%>, etc.) are converted to the internal tag format.""", +3:"""the template is filtered for the 2nd +time using the post-processors specified in the TemplateServer settings.""", +4:"""the tags that have been translated to +the internal format are converted into chunks of python code.""", +5:"""the chunks of python code from stage 4 +are wrapped up in a code string of a function definition.""", +6:"""the generated code string is filtered +using the filters defined in the TemplateServer settings.""", +7:"""the generated code string is executed +to produce a function that will be bound as a method of the TemplateServer.""", +} + + +################################################## +## CLASSES ## + +class Error(Exception): + pass + +class NoDefault: + pass + +class Template(SettingsManager): + """The core template engine: parses, compiles, and serves templates.""" + + def __init__(self, templateDef, *searchList, **kw): + """setup the namespace search list, process settings, then call + self._start() to parse/compile the template and prepare the + self.__str__() and self.respond() methods for serving the template. + + If the environment var CHEETAH_DEBUG is set to True the internal + debug setting will also be set to True.""" + + self._searchList = list(searchList) + [self,] + if kw.has_key('searchList'): + self._searchList += kw['searchList'] + + self.initializeSettings() + if kw.has_key('settings'): + self.updateSettings(kw['settings']) + + if os.environ.get('CHEETAH_DEBUG'): + self._settings['debug'] = True + + if kw.has_key('plugins'): + self._settings['plugins'] += kw['plugins'] + for plugin in self._settings['plugins']: + self.registerServerPlugin(plugin) + + self._rawTemplate = str( templateDef ) + + if not self._settings['delayedStart']: + self.startServer() + + + def initializeSettings(self): + """create the default settings """ + + def includeDirectiveLoop(server, template): + """process the include Directives recursively and plus all the other + directives/filters that affect them or are affected by them""" + settings = server._settings + extDelimeters = settings['extDelimeters'] + + regexs = extDelimeters['includeDirective'] + regexs += extDelimeters['macroDirective'] + regexs += extDelimeters['blockDirectiveStart'] + + try: + while filter(None, [regex.search(template) for regex in regexs]): + for includeDirectiveRE in extDelimeters['includeDirective']: + + ## must alternate between the #parse regexes or + # whitespace gobbling in nested #parse blocks + # won't be handled properly + + for processor in \ + settings['codeGenerator']['includeDirectiveLoop']: + + template = processor[1](server, template) + + template = \ + CodeGen.preProcessIncludeDirectives(server, + template, + includeDirectiveRE) + ## do one more final pass + for processor in settings['codeGenerator']['includeDirectiveLoop']: + template = processor[1](server, template) + + except: + errMsg = '\n' + errMsg += "\nThe includeDirectiveLoop was processing " + \ + processor[0] + \ + " when the error occurred.\n" + errMsg += "This was the state of the template when the error " +\ + "occurred:\n\n" + errMsg += insertLineNums( template ) + '\n' + server._errorMsgStack.append( errMsg ) + raise + + return template + + self._settings = { + 'delayedStart': False, + + 'plugins':[], + 'defaultVarValue':None, + # default val for names, if ==None then varNotFound_handler is called + 'varNotFound_handler': CodeGen.varNotFound_echo, + # only called if defaultVarValue==None and the $var can't be found + + 'debug': False, + 'keepCodeGeneratorResults': False, + + 'blockMarkerStart':['<!-- START BLOCK: ',' -->'], + 'blockMarkerEnd':['<!-- END BLOCK: ',' -->'], + 'includeBlockMarkers': False, + + + 'extDelimeters':{'includeDirective': [delims['includeDirective_gobbleWS'], + delims['includeDirective'], + ], + 'dataDirective': [delims['dataDirective_gobbleWS'], + delims['dataDirective'], + ], + 'macroDirective': [delims['macroDirective'], + ], + 'blockDirectiveStart': [delims['blockDirectiveStart_gobbleWS'], + delims['blockDirectiveStart'], + ], + 'lazyMacroCalls': [delims['lazyMacroCalls'],], + 'callMacro': [delims['callMacro'],], + 'callMacroArgs': delims['callMacroArgs'], + 'rawDirective': [delims['rawDirective'],], + 'comments': [delims['multiLineComment'], + delims['singleLineComment']], + 'slurp': [delims['slurpDirective_gobbleWS'], + delims['slurpDirective'], + ], + }, + + + 'responseErrorHandler': ErrorHandlers.ResponseErrorHandler, + + 'codeGenerator':{ + 'displayLogicblockEndings':['end if','end for'], + 'internalDelims': delims['xml'], + 'tagTokenSeparator': '__@__', + 'indentationStep': ' '*4, # 4 spaces - used in the generated code + 'initialIndentLevel': 2, + + ## must loop over these processores to handle + # nested #include, #macro and #block Directives + 'includeDirectiveLoop':[('rawDirectives', + CodeGen.preProcessRawDirectives), + ('comments', + CodeGen.preProcessComments), + ('setDirectives', + CodeGen.preProcessSetDirectives), + ('dataDirectives', + CodeGen.preProcessDataDirectives), + ('blockDirectives', + CodeGen.preProcessBlockDirectives), + ('macroDirectives', + CodeGen.preProcessMacroDirectives), + ('lazyMacroCalls', + CodeGen.preProcessLazyMacroCalls), + ('explicitMacroCalls', + CodeGen.preProcessExplicitMacroCalls), + ('comments', + CodeGen.preProcessComments), + ('rawDirectives', + CodeGen.preProcessRawDirectives), + ('setDirectives', + CodeGen.preProcessSetDirectives), + ], + + 'preProcessors': [('slurpDirectives', + CodeGen.preProcessSlurpDirective), + ('includeDirectiveLoop', + includeDirectiveLoop), # see above + ('displayLogic$Escaping', + CodeGen.preProcessDisplayLogic), + ], + + 'postProcessors': [('rawDirectives', + CodeGen.postProcessRawDirectives), + ('rawIncludeDirectives', + CodeGen.postProcessRawIncludeDirectives), + ], + + 'coreTags':{'displayLogic':{'type': EXEC_TAG_TYPE, + 'processor': CodeGen.displayLogicTagProcessor, + 'delims': [delims['displayLogic_gobbleWS'], + delims['displayLogic'], + ], + }, + 'placeholders':{'type': EVAL_TAG_TYPE, + 'processor': CodeGen.placeholderTagProcessor, + 'delims': [delims['${,}'],delims['$']], + ##the braced version must go first + }, + 'setDirective':{'type': EXEC_TAG_TYPE, + 'processor': CodeGen.setDirectiveTagProcessor, + 'delims': [delims['setDirective'],], + }, + 'cacheStartTag':{'type': EVAL_TAG_TYPE, + 'processor': CodeGen.cacheDirectiveStartTagProcessor, + 'delims': [delims['cacheDirectiveStartTag'],], + }, + 'cacheEndTag':{'type': EVAL_TAG_TYPE, + 'processor': CodeGen.cacheDirectiveEndTagProcessor, + 'delims': [delims['cacheDirectiveEndTag'],], + }, + + }, + + 'generatedCodeFilters':[('removeEmptyStrings', + CodeGen.removeEmptyStrings), + ('addPerResponseCode', + CodeGen.addPerResponseCode), + ], + + 'masterErrorHandler':ErrorHandlers.CodeGeneratorErrorHandler, + + 'stages':{1:{'title':'pre-processing', + 'description':stageDescriptions[1], + 'errorHandler':ErrorHandlers.Stage1ErrorHandler, + }, + 2:{'title':'translate-to-internal-tags', + 'description':stageDescriptions[2], + 'errorHandler':ErrorHandlers.Stage2ErrorHandler, + }, + 3:{'title':'post-processing', + 'description':stageDescriptions[3], + 'errorHandler':ErrorHandlers.Stage3ErrorHandler, + }, + 4:{'title':'convert-tags-to-code', + 'description':stageDescriptions[4], + 'errorHandler':ErrorHandlers.Stage4ErrorHandler, + }, + 5:{'title':'wrap-code-in-function-definition', + 'description':stageDescriptions[5], + 'errorHandler':ErrorHandlers.Stage5ErrorHandler, + }, + 6:{'title':'filter-generated-code', + 'description':stageDescriptions[6], + 'errorHandler':ErrorHandlers.Stage6ErrorHandler, + }, + 7:{'title':'execute-generated-code', + 'description':stageDescriptions[7], + 'errorHandler':ErrorHandlers.Stage7ErrorHandler, + }, + }, + }, + } + + def addToSearchList(self, object, restart=True): + self._searchList.append(object) + if restart: + self.startServer() + + def startServer(self): + """Process and parse the template, then compile it into a function definition + that is bound to self.__str__() and self.respond()""" + + self._errorMsgStack = [] + generatedFunction = self._codeGenerator( self._rawTemplate ) + self.__str__ = self._bindFunctionAsMethod( generatedFunction ) + self.respond = self._bindFunctionAsMethod( generatedFunction ) + + if not self._settings['keepCodeGeneratorResults']: + self._codeGeneratorResults = {} + + + def _codeGenerator(self, template): + + """parse the template definition, generate a python code string from it, + then execute the code string to create a python function which can be + bound as a method of the Template. Returns a reference to the function. + + stage 1 - the raw template is filtered using the pre-processors + specified in the TemplateServer settings + + stage 2 - convert the coreTags to internal tags. Core tags are: $var, + ${var}, #if .../#, #for .../#, #set, and the PSP tags <%...%>, + <%=...%>. Each internal tag will contain a token prefix to identify what + type of tag it was originally. These tokens are used in stage 4 to + determine how the tag should be processed. + + stage 3 - the template is filtered for the 2nd time using the + post-processors specified in the TemplateServer settings + + stage 4 - the tags that have been translated to the internal format are + converted into chunks of python code + + stage 5 - the chunks of python code from stage 4 are wrapped up in a + code string of a function definition + + stage 6 - the generated code string is filtered using the filters + defined in the TemplateServer settings + + stage 7 - the generated code string is executed to produce a python + function, that will become a method of the TemplateServer + + These stages are contain in a try: ... except: ... block that will + provide helpful information for debugging if an error is caught.""" + + settings = self._settings + generatorSettings = settings['codeGenerator'] + stageSettings = generatorSettings['stages'] + debug = settings['debug'] + results = self._codeGeneratorResults = {} + state = self._codeGeneratorState = {} + self._localVarsList = [] # used to track vars from #set and #for + + try: + ## stage 1 - preProcessing of the template string ## + stage = 1 + if debug: results['stage1'] = [] + for preProcessor in generatorSettings['preProcessors']: + template = preProcessor[1](self, template) + if debug: results['stage1'].append((preProcessor[0], template)) + + + ## stage 2 - translate the coreTag delimeters to internalDelims ## + # with tokens that will be recognized by the self._tagTokenProcessor + stage = 2 + if debug: results['stage2'] = [] + template = template.replace("'''",r"\'\'\'") # ''' must be escaped + for token, tagSettings in generatorSettings['coreTags'].items(): + for delimStruct in tagSettings['delims']: + # this loop allows multiple delims to be used for each token + template = CodeGen.swapDelims( + template, delimStruct, + generatorSettings['internalDelims']['start'] + token \ + + generatorSettings['tagTokenSeparator'], + generatorSettings['internalDelims']['end'], + ) + if debug: results['stage2'].append( (token, template) ) + + + ## stage 3 - postProcessing after the coreTag delim translation ## + stage = 3 + if debug: results['stage3'] = [] + for postProcessor in generatorSettings['postProcessors']: + template = postProcessor[1](self, template) + if debug: results['stage3'].append( (postProcessor[0], template) ) + + + ## stage 4 - generate the python code for each of the tokenized tags ## + # a) separate internal tags from text in the template to create + # textVsTagsList + # b) send textVsTagsList through self._tagTokenProcessor to generate + # the code pieces + # c) merge the code pieces into a single string + stage = 4 + if debug: results['stage4'] = [] + + # a) + subStage = 'a' + textVsTagsList = CodeGen.separateTagsFromText( + template, generatorSettings['internalDelims']['placeholderRE']) + if debug: + results['stage4'].append(('textVsTagsList', textVsTagsList)) + # b) + subStage = 'b' + codePiecesFromTextVsTagsList = CodeGen.processTextVsTagsList( + textVsTagsList, + self._tagTokenProcessor) + # c) + subStage = 'c' + codeFromTextVsTagsList = "".join(codePiecesFromTextVsTagsList) + if debug: + results['stage4'].append(('codeFromTextVsTagsList', + codeFromTextVsTagsList)) + + ## stage 5 - wrap the code up in a function definition ## + stage = 5 + if debug: results['stage5'] = [] + indent = generatorSettings['indentationStep'] + generatedCode = \ + "def generatedFunction(self, trans=None, iAmNested=False):\n" \ + + indent * 1 + "try:\n" \ + + indent * 2 + "#setupCodeInsertMarker\n" \ + + indent * 2 + "outputList = []\n" \ + + indent * 2 + "outputList += ['''" + codeFromTextVsTagsList + \ + "''',]\n" \ + + indent * 2 + "output = ''.join(outputList)\n" \ + + indent * 2 + "if trans and not iAmNested:\n" \ + + indent * 3 + "trans.response().write(output)\n" \ + + indent * 2 + "return output\n" \ + + indent * 1 + "except:\n" \ + + indent * 2 + "print self._settings['responseErrorHandler']()\n" \ + + indent * 2 + "raise\n" \ + + if debug: results['stage5'].append( ('generatedCode', generatedCode) ) + + + ## stage 6 - final filtering of the generatedCode ## + stage = 6 + if debug: results['stage6'] = [] + for filter in generatorSettings['generatedCodeFilters']: + generatedCode = filter[1](self, generatedCode) + if debug: results['stage6'].append( (filter[0], generatedCode) ) + + ## stage 7 - create "generatedFunction" in this namespace ## + stage = 7 + if debug: results['stage7'] = [] + exec generatedCode + if debug: + results['stage7'].append(('generatedFunction', generatedFunction)) + + ## + self._generatedCode = generatedCode + + return generatedFunction + + except: + ## call masterErrorHandler, which in turn calls the ErrorHandler ## + # for the stage in which the error occurred + print generatorSettings['masterErrorHandler']() + raise + + + def mergeNewTemplateData(self, newDataDict): + """merge the newDataDict into self.__dict__. This is a recursive merge + that handles nested dictionaries in the same way as + Template.updateServerSettings()""" + + for key, val in newDataDict.items(): + if type(val) == types.DictType and hasattr(self,key) \ + and type(getattr(self,key)) == types.DictType: + + setattr(self,key, mergeNestedDictionaries(getattr(self,key), val)) + else: + setattr(self,key,val) + + def registerServerPlugin(self, plugin): + """register a plugin that extends the functionality of the Template""" + plugin.bindToTemplateServer(self) + + def _bindFunctionAsMethod(self, function): + """used to dynamically bind a plain function as a method of the + Template instance""" + return new.instancemethod(function, self, self.__class__) + + def _tagTokenProcessor(self, tag, wrapOutput=True): + """an abstract tag processor that will identify the tag type from its + tagToken prefix and call the appropriate processor for that type of + tag""" + settings = self._settings + + tagToken, tag = tag.split(settings['codeGenerator']['tagTokenSeparator']) + + for token, tagSettings in settings['codeGenerator']['coreTags'].items(): + if tagToken == token: + processedTag = tagSettings['processor'](self, tag) + + if not wrapOutput: + return processedTag + elif tagSettings['type'] == EVAL_TAG_TYPE: + return "''', " + processedTag + ", '''" + elif tagSettings['type'] == EXEC_TAG_TYPE: + return "''',]\n" + processedTag + "outputList += ['''" + + + ## methods for dealing with the embedded NameMapper object references ## + + def mapName(self, name, default=None, executeCallables=False): + """Returns a mapping for the placeholder name to its actual value + + This function is similar, but not identical, to Webware's valueForName! + """ + + for namespace in self._searchList: + binding = valueForName(namespace, name, '<!NotFound!>') + if binding != '<!NotFound!>': + break + + if binding == '<!NotFound!>': + if default!=None: + binding = defaultVarValue + elif self._settings['defaultVarValue']!=None: + binding = self._settings['defaultVarValue'] + else: + raise NameMapper.NotFound(name) + + if executeCallables and callable(binding): + binding = binding() + return binding + + + def _setTimedRefresh(self, placeholderName, cacheRefreshInterval): + nextUpdateTime = currentTime() + cacheRefreshInterval * 60 + self._timedRefreshList.append( + [placeholderName, nextUpdateTime, cacheRefreshInterval]) + self._checkForCacheRefreshes = True + + def _timedRefresh(self, currTime): + """refresh all the cached NameMapper vars that are scheduled for a + refresh at this time, and reschedule them for their next update. + + the entries in the recache list are in the format [name, interval, + nextRecacheTime] """ + + def updateList(item, currTime=currTime): + if item[1] < currTime: + item[1] = currTime + (item[2]*60) # reschedule for next update + return True + else: + return False + + for name in filter(updateList, self._timedRefreshList): + if self._settings['debug']: + print repr(self), 'refreshing {' + name[0] + '} at time:', \ + currentTime() + print + + ## send it back through the tag processor to recache the value + CodeGen.placeholderTagProcessor( + self, tag=name[0], cacheType=CodeGen.TIMED_REFRESH_CACHE, + cacheRefreshInterval=float(name[1])) + + ## the placeholderTagProcessor will have added a new entry to the + # list which we don't need so ... + self._timedRefreshList.pop() + + + def defineTemplateBlock(self, blockName, blockContents): + """ """ + if not hasattr(self, '_blocks'): + self._blocks = {} + + self._blocks[blockName]= blockContents + + def killTemplateBlock(self, *blockNames): + """ """ + if not hasattr(self, '_blocks'): + return False + for blockName in blockNames: + self._blocks[blockName]= '' + + def loadMacro(self, macroName, macro): + """Load a macro into the macros dictionary, using the specified macroName""" + if not hasattr(self, '_macros'): + self._macros = {} + + self._macros[macroName] = macro + + + def loadMacros(self, *macros): + """Create macros from any number of functions and/or bound methods. For + each macro, the function/method name is used as the macro name. """ + + for macro in macros: + self.loadMacro(macro.__name__, macro) + + def loadMacrosFromModule(self, module): + """Load all the macros from a module into the macros dictionary""" + + if hasattr(module,'_exclusionList'): + exclusionList = module._exclusionList + else: + exclusionList = () + + macrosList = [] + for obj in module.__dict__.values(): + if callable(obj) and obj not in exclusionList: + macrosList.append( (obj.__name__, obj) ) + + for macro in macrosList: + self.loadMacro(macro[0], macro[1]) + + + def extendTemplate(self, extension): + """ + @@needs documenting + #redefine and #data directives MUST NOT be nested!! + """ + import re + + if not hasattr(self, '_blocks'): + self._blocks = {} + + redefineDirectiveRE = re.compile( + r'(?<!#)#redefine[\t ]+' + + r'(?P<blockName>[A-Za-z_][A-Za-z_0-9]*?)' + + r'(?:/#|\r\n|\n|\Z)',re.DOTALL) + + while redefineDirectiveRE.search(extension): + startTagMatch = redefineDirectiveRE.search(extension) + blockName = startTagMatch.group('blockName') + endTagRE = re.compile(r'#end redefine[\t ]+' + blockName + r'[\t ]*(?:/#|\r\n|\n|\Z)', + re.DOTALL | re.MULTILINE) + endTagMatch = endTagRE.search(extension) + blockContents = extension[startTagMatch.end() : endTagMatch.start()] + self.defineTemplateBlock(blockName, blockContents) + extension = extension[0:startTagMatch.start()] + \ + extension[endTagMatch.end():] + + ## process the #data and #macro definition directives + # after removing comments + extension = CodeGen.preProcessComments(self, extension) + CodeGen.preProcessDataDirectives(self, extension) + CodeGen.preProcessMacroDirectives(self, extension) + + + ## utility functions ## + def getFileContents(self, fileName): + fp = open(fileName,'r') + output = fp.read() + fp.close() + return output + + def runAsMainProgram(self): + """An abstract method that can be reimplemented to enable the Template + to function as a standalone command-line program for static page + generation and testing/debugging. + + The debugging facilities are provided by a plugin to Template.""" + + print self diff --git a/src/Templates/SkeletonPage.py b/src/Templates/SkeletonPage.py new file mode 100644 index 0000000..9c06cdd --- /dev/null +++ b/src/Templates/SkeletonPage.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# $Id: SkeletonPage.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $ +"""A skeleton page template for use with the Cheetah package + +Meta-Data +========== +Author: Tavis Rudd <tavis@calrudd.com>, +Version: $Revision: 1.1 $ +Start Date: 2001/04/05 +Last Revision Date: $Date: 2001/06/13 03:50:40 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## + +import types, os, sys + + +from Cheetah.Servlet import TemplateServlet +import Cheetah.Macros.HTML as HTMLMacros +from Cheetah.Macros.HTML import spacer, currentYr, formHTMLTag, formatMetaTags + +################################################## +## GLOBALS AND CONSTANTS ## + +True = (1==1) +False = (0==1) + +################################################## +## TEMPLATE ELEMENTS ## + +template = """$docType +<HTML> +#################### +#block headerComment +<!-- This document was autogenerated by Cheetah. Don't edit it directly! + +Copyright #currentYr() - $siteCopyrightName - All Rights Reserved. +Feel free to copy any javascript or html you like on this site, +provided you remove all links and/or references to $siteDomainName +However, please do not copy any content or images without permission. + +$siteCredits + +--> + +#end block headerComment +##################### + +################# +#block headTag /# +<HEAD> +<TITLE>$title</TITLE> +$metaTags +$stylesheetTags +$javascriptTags +</HEAD> +#end block headTag /# +################# + + +################# +#block bodyTag /# +$bodyTag +#end block bodyTag /# +################# + +#block bodyContents /# +This skeleton page has no flesh. Its body needs to be implemented. +#end block bodyContents /# + +</BODY> +</HTML> +""" + +################################################## +## TEMPLATE SERVLET ## + +class SkeletonPage(TemplateServlet): + """A Skeleton HTML page template""" + + def __init__(self, template=template, *searchList, **kw): + """ """ + self.loadMacrosFromModule(HTMLMacros) + self.loadMacro('imgTag', self.imgTag ) + TemplateServlet.__init__(self, template, *searchList, **kw) + + + + ## Default values for the names embedded in the template ## + + docType = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" ' + \ + '"http://www.w3.org/TR/html4/loose.dtd">' + + title = '' + #_metaTags = {'HTTP_EQUIV':{'test':1234}, 'NAME':{'test':1234,'test2':1234} } + #_stylesheets = {'.cssClassName':'stylesheetCode'} + #_stylesheetLibs = {'libName':'libSrcPath'} + #_javascriptLibs = {'libName':'libSrcPath'} + bodyTagAttribs = {} + + siteDomainName = 'www.example.com' + siteCredits = 'Designed & Implemented by Tavis Rudd' + siteCopyrightName = "Tavis Rudd" + + + def metaTags(self): + """ """ + if not hasattr(self,'_metaTags'): + return '' + else: + formatMetaTags(self._metaTags) + + def stylesheetTags(self): + stylesheetTagsTxt = '' + if hasattr(self,'_stylesheetLibs'): + for title, src in self._stylesheetLibs.items(): + stylesheetTagsTxt += '<LINK REL="stylesheet" TYPE="text/css" HREF="' + str(src) + '">\n' + + if hasattr(self,'_stylesheets') and hasattr(self,'_stylesheetsOrder'): + stylesheetTagsTxt += '<STYLE TYPE="text/css"><!--\n' + for identifier in self._stylesheetsOrder: + if not self._stylesheets.has_key(identifier): + warning = '# the identifier ' + identifier + \ + 'was in self._stylesheetsOrder, but not in self._stylesheets' + print warning + stylesheetTagsTxt += warning + continue + + attribsDict = self._stylesheets[identifier] + cssCode = '' + attribCode = '' + for k, v in attribsDict.items(): + attribCode += str(k) + ': ' + str(v) + '; ' + attribCode = attribCode[:-2] # get rid of the last semicolon + + cssCode = '\n' + identifier + ' {' + attribCode + '}' + stylesheetTagsTxt += cssCode + stylesheetTagsTxt += '\n//--></STYLE>\n' + + return stylesheetTagsTxt.strip() + + def javascriptTags(self): + if not hasattr(self,'_javascriptTags'): + return '' + #else... + javascriptTagsTxt = [] + if self._javascriptTags.has_key('libs'): + for title, details in self._javascriptTags['libs'].items(): + if type(details) not in (types.ListType, types.TupleType): + details = ['',details] + + javascriptTagsTxt += ['<SCRIPT LANGUAGE="JavaScript', str(details[0]), + '" SRC="', str(details[1]), '">\n'] + return ''.join(javascriptTagsTxt) + + def bodyTag(self): + if not hasattr(self,'bodyTagAttribs'): + self.bodyTagAttribs = {} + + bodyTagTxt = formHTMLTag('BODY', self.bodyTagAttribs) + return bodyTagTxt + + + def imgTag(self, src, alt='', width=None, height=None, border=0): + """dynamically generate an image tag + + The dimensions are calculated using PIL or ImageMagick if they are available.""" + try: + src = self.serverSidePath(src) + print src + except: + pass + + try: + if not width or not height: + try: # see if the dimensions can be calc'd with PIL + import Image + im = Image.open(src) + calcWidth, calcHeight = im.size + del im + except: + try: # try imageMagick instead + calcWidth, calcHeight = os.popen('identify -format "%w,%h" ' + src).read().split(',') + except: + pass + + if not width: width = calcWidth + if not height: height = calcHeight + + return ''.join(['<IMG SRC="', src, '" WIDTH=', str(width), ' HEIGHT=', str(height), + ' ALT="', alt, '" BORDER=', str(border), '>']) + except: + return ''.join(['<IMG SRC="', src, '" ALT="', alt, '" BORDER=', str(border),'>']) + + +################################################## +## if run from the command line ## + +if __name__ == '__main__': + SkeletonPage().runAsMainProgram() + diff --git a/src/Templates/__init__.py b/src/Templates/__init__.py new file mode 100644 index 0000000..4265cc3 --- /dev/null +++ b/src/Templates/__init__.py @@ -0,0 +1 @@ +#!/usr/bin/env python diff --git a/src/Tests.py b/src/Tests.py new file mode 100644 index 0000000..286912f --- /dev/null +++ b/src/Tests.py @@ -0,0 +1,553 @@ +#!/usr/bin/env python +# $Id: Tests.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ +"""Unit-testing framework for the Cheetah package + +TODO +================================================================================ +- Check NameMapper independently + + +Meta-Data +================================================================================ +Author: Tavis Rudd <tavis@calrudd.com>, +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + + +################################################## +## DEPENDENCIES ## + +import sys +import types +import re +from copy import deepcopy +import os.path + + +try: + import unittest +except: + import unittest_local_copy as unittest + +# intra-package imports ... +import NameMapper as NameMapper +from Template import Template +from Delimeters import delimeters + +################################################## +## CONSTANTS & GLOBALS ## + +True = (1==1) +False = (0==1) + + +################################################## +## TEST CLASSES ## + +class AbstractTestCase(unittest.TestCase): + def nameSpace(self): + return self._nameSpace + + def __init__(self, title, template, expectedOutput, nameSpace): + self.testTitle = title + self.template = template + self.expectedOutput = expectedOutput + self._nameSpace = nameSpace + unittest.TestCase.__init__(self, "runTest") + +class VerifyTemplateOutput(AbstractTestCase): + def runTest(self): + servlet = Template(self.template, self.nameSpace()) + output = servlet.respond() + + assert output == self.expectedOutput, \ + ('Template output mismatch\n\tTest Title: ' + self.testTitle + + '\n\tInput Template = %(template)s\n\tExpected Output = ' + + '%(expected)s---\n\tActual Output = %(actual)s---\n' ) \ + % {'template':self.template, 'expected':self.expectedOutput, + 'actual':output} + + +################################################## +## TEST DATA FOR USE IN THE TEMPLATES ## + +class DummyClass: + 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 dummyFunc(arg="Scooby"): + return arg + +defaultTestNameSpace = { + 'numOne': 1, + 'numTwo': 2, + 'c':"blarg", + 'numFive':5, + 'emptyString':'', + 'numZero':0, + 'func':dummyFunc, + 'meth':DummyClass().meth1, + 'obj':DummyClass(), + 'dict':{'one':'item1', + 'two':'item2', + 'nestedDict':{1:'nestedItem1', + 'two':'nestedItem2' + }, + 'nestedFunc':dummyFunc, + }, + 'dict2': {'one':'item1', 'two':'item2'}, + 'blockToBeParsed':"""$numOne $numTwo""", + } + + +################################################## +## TEST CASES ## + + +# combo tests +# negative test cases for expected exceptions +# black-box vs clear-box testing +# do some tests that run the Template for long enough to check that the refresh code works + +## [testTitle, template, expected output] + +posixCases = [ + ['single $var','$c','blarg'], + ['simple $var permutations', + """ +$ $500 $*. $. \$var +''' +$emptyString $numZero +$numOne$numTwo +$numOne and $numTwo $c. $dict.one $dict.nestedDict.1 $dict.nestedFunc +$func $func. $func(). $func(4). $func('x'). $func("x"). $func("x"*2). $func(arg="x"). $func(arg='x'). +$meth $meth. $meth(). $meth(5). $meth('y'). $meth("y"). $meth("y"*2). $meth(arg="y"). $meth(arg='y'). +$obj $obj. +$obj.meth $obj.meth. $obj.meth(). $obj.meth(6). +$obj.meth('z'). $obj.meth("z"). $obj.meth("z"*2). $obj.meth(arg="z"). $obj.meth(arg='z').""", + + """ +$ $500 $*. $. $var +''' + 0 +12 +1 and 2 blarg. item1 nestedItem1 Scooby +Scooby Scooby. Scooby. 4. x. x. xx. x. x. +doo doo. doo. 5. y. y. yy. y. y. +object object. +arff arff. arff. 6. +z. z. zz. z. z.""" + ], + + ] + + +standardVarRegex = delimeters['$']['placeholderRE'] +def convertVars(titlePrefix, replacementString, caseData): + caseData = deepcopy(caseData) + caseData[0] = titlePrefix + caseData[0] + caseData[1] = standardVarRegex.sub(replacementString, caseData[1]) + return caseData + +for i in range(len(posixCases)): + posixCases.append(convertVars("Braced cached ${vars} : ", r"${\1}", posixCases[i] )) + posixCases.append(convertVars("Dynamic $*vars : ", r"$*\1", posixCases[i] )) + posixCases.append(convertVars("Braced Dynamic ${*vars} : ", r"${*\1}", posixCases[i] )) + posixCases.append(convertVars("Dynamic Refresh $*15*vars : ", r"$*15*\1", posixCases[i] )) + posixCases.append(convertVars("Braced Dynamic Refresh ${*15*vars} : ", r"${*15*\1}", posixCases[i] )) + + + +## I have disabled nested calls like $var($anotherVar) for the time-being +#nestedVarTests = [ +# ['$var($var)', +# """$func($numOne) $meth($numOne) $obj.meth($numOne)""", +# '1 1 1'], +# ['$var($var)', +# """${func($numOne)} ${meth($numOne)} ${obj.meth($numOne)}""", +# '1 1 1'], +# ] +#posixCases += nestedVarTests + +commentTests = [ + ['simple ## comment - with whitespace - should gobble', + " ## \n", + "",], + + ['simple ## comment - no whitespace', + "##", + "",], + + ['simple ## comment - after other text', + "\nblarg ## foo", + "\nblarg ",], + + ['simple ## comment - with #if directive', + "##if 0\nblarg\n##end if\n", + "blarg\n",], + + ['simple #* comment *# - no whitespace', + "#* \naoeuaoeu\naoeuaoeu\n *#", + "",], + + ['simple #* comment *# - with whitespace', + "\n#* \naoeuaoeu\naoeuaoeu\n *# ", + "\n ",], + ] +posixCases += commentTests + +forLoopTests = [ + ['simple #for loop', + """#for $i in range(5)\n$i\n#end for\n""", + """0\n1\n2\n3\n4\n""",], + ['simple #for loop with no whitespace at the end', + """#for $i in range(5)\n$i\n#end for""", + """0\n1\n2\n3\n4\n""",], + ['simple #for loop with no whitespace', + """#for $i in range(5)\n$i#end for""", + """01234""",], + ['simple #for loop with explicit closures', + """#for $i in range(5)/#$i#end for/#""", + """01234""",], + + ['simple #for loop using another $var', + """#for $i in range($numFive)\n$i\n#end for\n""", + """0\n1\n2\n3\n4\n""",], + + ['simple #for loop using $dict2', + """#for $key, $val in $dict2.items\n$key - $val\n#end for\n""", + """one - item1\ntwo - item2\n""",], + ['simple #for loop using $dict2, with $ on $key,$val', + """#for key, val in $dict2.items\n$key - $val\n#end for\n""", + """one - item1\ntwo - item2\n""",], + + ['simple #for loop using $dict2 and another $var ($c)', + "#for $key, $val in $dict2.items\n$key - $val - $c\n#end for\n", + "one - item1 - blarg\ntwo - item2 - blarg\n",], + ['simple #for loop using $dict2 and another $*var ($*c)', + "#for $key, $val in $dict2.items\n$key - $val - $*c\n#end for\n", + "one - item1 - blarg\ntwo - item2 - blarg\n",], + ['simple #for loop using $dict2 and another $*15*var ($*15*c)', + "#for $key, $val in $dict2.items\n$key - $val - $*15*c\n#end for\n", + "one - item1 - blarg\ntwo - item2 - blarg\n",], + + ['simple #for loop using $dict2 and a method of the local var', + "#for $key, $val in $dict2.items\n$key - $val.upper\n#end for\n", + "one - ITEM1\ntwo - ITEM2\n",], + ] +posixCases += forLoopTests + + +ifBlockTests = [ + ['simple #if block', + "#if 1\n$c\n#end if\n", + "blarg\n",], + + ['simple #if block with no trailing whitespace', + "#if 1\n$c\n#end if", + "blarg\n",], + + ['simple #if block with explicit closures', + "#if 1/#$c#end if/#", + "blarg",], + + ['simple #if block using $numOne', + "#if $numOne\n$c\n#end if\n", + "blarg\n",], + + ['simple #if block using a $numZero', + "#if $numZero\n$c\n#end if\n", + "",], + + ['simple #if block using a $emptyString', + "#if $emptyString\n$c\n#end if\n", + "",], + + ['simple #if... #else ... block using a $emptyString', + "#if $emptyString\n$c\n#else\n$c - $c#end if\n", + "blarg - blarg",], + + ['simple #if... #elif ... #else ... block using a $emptyString', + "#if $emptyString\n$c\n#elif $numOne\n$numOne\n#else\n$c - $c#end if\n", + "1\n",], + + ['simple "#if not" test', + "#if not $emptyString\n$c\n#elif $numOne\n$numOne\n#else\n$c - $c#end if\n", + "blarg\n",], + + ## These currently fail @@work on this + #['simple #if block using a $*emptyString', + # "#if $*emptyString\n$c\n#end if\n", + # "",], + + #['simple #if block using a ${emptyString}', + # "#if ${emptyString}\n$c\n#end if\n", + # "",], + + ] +posixCases += ifBlockTests + +blockTests = [ + ['simple #block - with no whitespace', + "#block testBlock\nthis is a\ntest block\n#end block testBlock\n", + "this is a\ntest block\n",], + + ['simple #block - with whitespace - should gobble', + " #block testBlock\nthis is a\ntest block\n #end block testBlock\n", + "this is a\ntest block\n",], + + ['simple #block - with explicit closures', + "#block testBlock/#this is a\ntest block#end block testBlock/#\n", + "this is a\ntest block\n",], + + ['#block - with explicit closures and surrounding whitespace', + " #block testBlock/#this is a\ntest block#end block testBlock/# \n", + " this is a\ntest block \n",], + + ['#block - long block test', + '#block longBlock\n' + ' aoeu aoeu aoeu aoeuoae ao uaoeu aoeu aoeu\n'*2000 + + '#end block longBlock', + ' aoeu aoeu aoeu aoeuoae ao uaoeu aoeu aoeu\n'*2000,], + + + ] +posixCases += blockTests + + +macroTests = [ + ['simple #macro - with no whitespace', + "#macro testMacro()\nthis is a\ntest block\n#end macro", + "",], + ['simple #macro + call - with no whitespace', + "#macro testMacro()\nthis is a\ntest block\n#end macro\n#testMacro()", + "this is a\ntest block",], + + ['simple #macro - with whitespace - should gobble', + " #macro testMacro()\nthis is a\ntest block\n#end macro ", + "",], + + ['simple #macro + call - with whitespace - should gobble', + " #macro testMacro()\nthis is a\ntest block\n#end macro \n#testMacro()", + "this is a\ntest block",], + + ['simple #macro + call - with an arg', + "#macro testMacro(a=1234)\nthis is a\ntest block $a\n#end macro\n#testMacro()", + "this is a\ntest block 1234",], + + ['simple #macro + call - with an arg, using arg in call', + "#macro testMacro(a=1234)\nthis is a\ntest block $a\n#end macro\n#testMacro(9876)", + "this is a\ntest block 9876",], + + ['simple #macro + call - with two args, using one', + """#macro testMacro(a=1234, b='blarg')\nthis is a\ntest block $a $b\n#end macro\n#testMacro(9876)""", + "this is a\ntest block 9876 blarg",], + + ['simple #macro + call - with two args, using both', + """#macro testMacro(a=1234, b='blarg')\nthis is a\ntest block $a $b\n#end macro\n#testMacro(5,$numOne)""", + "this is a\ntest block 5 1",], + + ] +posixCases += macroTests + + +setTests = [ + ['simple #set', + "#set $testVar = 'blarg'", + "",], + + ['simple #set - with no whitespace', + "#set $testVar='blarg'", + "",], + + ['simple #set + use of var', + "#set $testVar = 'blarg'\n$testVar", + "blarg",], + + ['#set with a dictionary', + """#set $testDict = {"one":"one1","two":"two2","three":"three3"} +$testDict.one +$testDict.two""", + """one1 +two2""",], + + + ['#set with string, then used in #if block', + """#set $test='a string' +#if $test/#blarg#end if""", + "blarg",], + + ] + +posixCases += setTests + + +rawTests = [ + ['simple #raw - with no whitespace', + "#raw", + "",], + + ['#raw followed by $vars', + "#raw\n$varName1 $varName2", + "$varName1 $varName2",], + + ['#raw followed by $vars, and preceeded by real $vars', + "$numOne\n#raw\n$varName1 $varName2", + "1\n$varName1 $varName2",], + + ['#raw and #end raw surrounded by real $vars', + "$numOne\n#raw\n$varName1 $varName2\n#end raw\n$numTwo", + "1\n$varName1 $varName2\n2",], + + ] +posixCases += rawTests + +includeTests = [ + ['simple #include of $emptyString - with no whitespace', + "#include $emptyString", + "",], + + ['simple #include of $blockToBeParsed - with no whitespace', + "#include $blockToBeParsed", + "1 2",], + + ['simple #include of $blockToBeParsed - with whitespace', + "\n#include $blockToBeParsed\n", + "\n1 2",], + + ['simple #include of file - with no whitespace', + "#include 'parseTest.txt'", + "1 2",], + + ['simple #include of file - with whitespace', + "#include 'parseTest.txt'", + "1 2",], + + ] +posixCases += includeTests + + +includeRawTests = [ + ['simple #include raw of $emptyString - with no whitespace', + "#include raw $emptyString", + "",], + + ['simple #include raw of $blockToBeParsed - with no whitespace', + "#include raw $blockToBeParsed", + "$numOne $numTwo",], + + ['simple #include raw of file - with no whitespace', + "#include raw 'parseTest.txt'", + "$numOne $numTwo",], + + ['simple #include raw of file - with whitespace', + "#include raw 'parseTest.txt'", + "$numOne $numTwo",], + + ] +posixCases += includeRawTests + +callMacroTests = [ + + ['simple #macro + explicit #callMacro call', + """#macro testMacro(a=1234, b='blarg',c='argC') +this is a +test block $a $b $c +#end macro +#callMacro testMacro(a=9876) +#arg b +joe +#end arg + +#arg c +bloggs +#end arg + +#end callMacro +""", + "this is a\ntest block 9876 joe bloggs",], + + ] +posixCases += callMacroTests + + +#extendTests = [ +# ['simple #extend - with no whitespace', +# "#extend Cheetah.Templates.SkeletonPage", +# "",], +# ] +#posixCases += extendTests +# @@ at the moment the #entend directive is only caught by Servlet.extendTemplate() + + + +windowsCases = deepcopy(posixCases) +for case in windowsCases: + case[1] = case[1].replace("\n","\r\n") + case[2] = case[2].replace("\n","\r\n") + +# the dataDirectiveTests must be added after the windows line ending conversion +# as \r\n appears to be an invalid line ending for python code chunks that are +# exec'd on Posix systems + +dataDirectiveTests = [ + ['simple #data block + use of the vars that are assigned', + """#data +testVar = 1234 +testVar2 = 'aoeu' +#end data +$testVar +$testVar2 +""", + "1234\naoeu\n",], + ] +posixCases += dataDirectiveTests + +suiteData = [ + ['posixCases', posixCases], + ['windowsCases', windowsCases], + ] + + +def buildTestSuite(suiteTitle, testCasesData): + suite = unittest.TestSuite() + for testData in testCasesData: + suite.addTest( VerifyTemplateOutput( + suiteTitle + ": " + testData[0], testData[1], testData[2], defaultTestNameSpace) ) + return suite + +def allSuites(): + suitesList = [] + for testSuite in suiteData: + suitesList.append( buildTestSuite(testSuite[0], testSuite[1]) ) + + return unittest.TestSuite(suitesList) + +def runTests(): + runner = unittest.TextTestRunner(stream=sys.stdout) + unittest.main(defaultTest='allSuites', testRunner=runner) + +################################################## +## if run from the command line ## +if __name__ == '__main__': + if not os.path.exists('parseTest.txt'): + fp = open('parseTest.txt','w') + fp.write("$numOne $numTwo") + fp.flush() + fp.close + runTests() + diff --git a/src/Utilities.py b/src/Utilities.py new file mode 100644 index 0000000..f739abf --- /dev/null +++ b/src/Utilities.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# $Id: Utilities.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ +"""Utility classes and functions used in the Cheetah package + + +Meta-Data +================================================================================ +Author: Tavis Rudd <tavis@calrudd.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + + +################################################## +## DEPENDENCIES ## + +import re +import types + +################################################## +## CONSTANTS & GLOBALS ## + +True = (1==1) +False = (0==1) + +################################################## +## FUNCTIONS ## + +def insertLineNums(string): + lineNum = [0,] + def lineNums(match, lineNum=lineNum): + lineNum[0] +=1 + return "%(match)s%(line)-3d|"% {'match':match.group(), 'line':lineNum[0]} + + if string.find('\n') != -1: + return re.sub(r'^|\n', lineNums, string) + else: + return "1 |" + string + +def getLines(string, lineNums): + lines = string.split('\n') + return lines[lineNums] + +def lineNumFromPos(string, pos): + return len(string[0:pos].split('\n')) + + +def removeDuplicateValues(list): + """remove all duplicate values in a list""" + listCopy = [] + while len(list) > 0: + if not list[0] in listCopy: + listCopy.append(list[0]) + del list[0] + + return listCopy + +def mergeNestedDictionaries(dict1, dict2): + """Recursively merge the values of dict2 into dict1. + + This little function is very handy for selectively overriding settings in a + settings dictionary that has a nested structure. """ + + newDict = dict1.copy() + for key,val in dict2.items(): + if newDict.has_key(key) and type(val) == types.DictType and type(newDict[key]) == types.DictType: + newDict[key] = mergeNestedDictionaries(newDict[key], val) + else: + newDict[key] = val + return newDict + + + +################################################## +## CLASSES ## + +#none yet + +################################################## +## if run from the command line ## + +if __name__ == '__main__': + pass + diff --git a/src/Validators.py b/src/Validators.py new file mode 100644 index 0000000..51fc625 --- /dev/null +++ b/src/Validators.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# $Id: Validators.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ +"""Code validation tools for the Cheetah package + + +Meta-Data +================================================================================ +Author: Tavis Rudd <tavis@calrudd.com> +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + +################################################## +## DEPENDENCIES ## + +import re, types + + +################################################## +## CONSTANTS & GLOBALS ## + +True = (1==1) +False = (0==1) + +################################################## +## FUNCTIONS ## + +def validateDisplayLogicCode(templateObj, displayLogic): + """check for any unsafe code in displayLogic - NOT IMPLEMENTED YET - this will + implement a form of 'Safe Delegation' as this term is used in Zope and Spectra""" + pass + +def validateArgStringInPlaceholderTag(templateObj, argString): + """check for any unsafe code in argStrings - NOT IMPLEMENTED YET - this will + implement a form of 'Safe Delegation' as this term is used in Zope and Spectra""" + pass + +def validateIncludeDirective(templateObj, includeDirective): + """check for any unsafe code in includeDirective - NOT IMPLEMENTED YET - this + will implement a form of 'Safe Delegation' as this term is used in Zope and + Spectra""" + pass + +def validateMacroDirective(templateObj, macroDirective): + """check for any unsafe code in macroDirective - NOT IMPLEMENTED YET - this will + implement a form of 'Safe Delegation' as this term is used in Zope and Spectra""" + + pass + +def validateSetDirective(templateObj, setDirective): + """check for any unsafe code in setDirective - NOT IMPLEMENTED YET - this will + implement a form of 'Safe Delegation' as this term is used in Zope and Spectra""" + pass + diff --git a/src/Version.py b/src/Version.py new file mode 100644 index 0000000..54e34e5 --- /dev/null +++ b/src/Version.py @@ -0,0 +1 @@ +version = '0.9.6' diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..2d19864 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# $Id: __init__.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $ + +"""Cheetah is a Python-powered template engine and code-generator. +It similar to the Jakarta project's Velocity. + +Homepage +================================================================================ +http://www.CheetahTemplate.org + +Mailing list +================================================================================ +cheetahtemplate-discuss@lists.sourceforge.net +Subscribe at +http://lists.sourceforge.net/lists/listinfo/cheetahtemplate-discuss + +Documentation +================================================================================ + +For a high-level introduction to Cheetah please refer to the User's Guide +in the docs/ directory of the Cheetah distribution. + +Installation +================================================================================ +Cheetah can be run a directory in the from the system-wide Python path or +from a directory in a user's Python path. + +To install Cheetah for a single user: + - copy the 'src' sub-directory to a directory called 'Cheetah' that is in the + user's PYTHON_PATH + +To install Cheetah for system-wide use: + - on Posix systems (AIX, Solaris, Linux, IRIX, etc.) become the 'root' user + and run: python ./setup.py install + + - On non-posix systems, such as Windows NT, login as an administrator and + type this at the command-line: python setup.py install + +On Posix systems, the system-wide installation will also install the Cheetah's +command-line compiler program, TScompile, to a system-wide executable path such as +/usr/local/bin. + + +Meta-Data +================================================================================ +Authors: The Cheetah Development Team (Tavis Rudd, Mike Orr, Chuck Esterbrook + Ian Bicking, Tom Schwaller) +License: This software is released for unlimited distribution under the + terms of the Python license. +Version: $Revision: 1.1 $ +Start Date: 2001/03/30 +Last Revision Date: $Date: 2001/06/13 03:50:39 $ +""" +__author__ = "Tavis Rudd <tavis@calrudd.com>" +__version__ = "$Revision: 1.1 $"[11:-2] + diff --git a/src/unittest_local_copy.py b/src/unittest_local_copy.py new file mode 100644 index 0000000..4cd84d4 --- /dev/null +++ b/src/unittest_local_copy.py @@ -0,0 +1,711 @@ +#!/usr/bin/env python +''' +Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's +Smalltalk testing framework. + +This module contains the core framework classes that form the basis of +specific test cases and suites (TestCase, TestSuite etc.), and also a +text-based utility class for running the tests and reporting the results +(TextTestRunner). + +Simple usage: + + import unittest + + class IntegerArithmenticTestCase(unittest.TestCase): + def testAdd(self): ## test method names begin 'test*' + self.assertEquals((1 + 2), 3) + self.assertEquals(0 + 1, 1) + def testMultiply(self); + self.assertEquals((0 * 10), 0) + self.assertEquals((5 * 8), 40) + + if __name__ == '__main__': + unittest.main() + +Further information is available in the bundled documentation, and from + + http://pyunit.sourceforge.net/ + +Copyright (c) 1999, 2000, 2001 Steve Purcell +This module is free software, and you may redistribute it and/or modify +it under the same terms as Python itself, so long as this copyright message +and disclaimer are retained in their original form. + +IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, +AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +''' + +__author__ = "Steve Purcell" +__email__ = "stephen_purcell at yahoo dot com" +__version__ = "$Revision: 1.1 $"[11:-2] + +import time +import sys +import traceback +import string +import os +import types + +############################################################################## +# Test framework core +############################################################################## + +class TestResult: + """Holder for test result information. + + Test results are automatically managed by the TestCase and TestSuite + classes, and do not need to be explicitly manipulated by writers of tests. + + Each instance holds the total number of tests run, and collections of + failures and errors that occurred among those test runs. The collections + contain tuples of (testcase, exceptioninfo), where exceptioninfo is a + tuple of values as returned by sys.exc_info(). + """ + def __init__(self): + self.failures = [] + self.errors = [] + self.testsRun = 0 + self.shouldStop = 0 + + def startTest(self, test): + "Called when the given test is about to be run" + self.testsRun = self.testsRun + 1 + + def stopTest(self, test): + "Called when the given test has been run" + pass + + def addError(self, test, err): + "Called when an error has occurred" + self.errors.append((test, err)) + + def addFailure(self, test, err): + "Called when a failure has occurred" + self.failures.append((test, err)) + + def addSuccess(self, test): + "Called when a test has completed successfully" + pass + + def wasSuccessful(self): + "Tells whether or not this result was a success" + return len(self.failures) == len(self.errors) == 0 + + def stop(self): + "Indicates that the tests should be aborted" + self.shouldStop = 1 + + def __repr__(self): + return "<%s run=%i errors=%i failures=%i>" % \ + (self.__class__, self.testsRun, len(self.errors), + len(self.failures)) + + +class TestCase: + """A class whose instances are single test cases. + + By default, the test code itself should be placed in a method named + 'runTest'. + + If the fixture may be used for many test cases, create as + many test methods as are needed. When instantiating such a TestCase + subclass, specify in the constructor arguments the name of the test method + that the instance is to execute. + + Test authors should subclass TestCase for their own tests. Construction + and deconstruction of the test's environment ('fixture') can be + implemented by overriding the 'setUp' and 'tearDown' methods respectively. + + If it is necessary to override the __init__ method, the base class + __init__ method must always be called. It is important that subclasses + should not change the signature of their __init__ method, since instances + of the classes are instantiated automatically by parts of the framework + in order to be run. + """ + + # This attribute determines which exception will be raised when + # the instance's assertion methods fail; test methods raising this + # exception will be deemed to have 'failed' rather than 'errored' + + failureException = AssertionError + + def __init__(self, methodName='runTest'): + """Create an instance of the class that will use the named test + method when executed. Raises a ValueError if the instance does + not have a method with the specified name. + """ + try: + self.__testMethodName = methodName + testMethod = getattr(self, methodName) + self.__testMethodDoc = testMethod.__doc__ + except AttributeError: + raise ValueError, "no such test method in %s: %s" % \ + (self.__class__, methodName) + + def setUp(self): + "Hook method for setting up the test fixture before exercising it." + pass + + def tearDown(self): + "Hook method for deconstructing the test fixture after testing it." + pass + + def countTestCases(self): + return 1 + + def defaultTestResult(self): + return TestResult() + + def shortDescription(self): + """Returns a one-line description of the test, or None if no + description has been provided. + + The default implementation of this method returns the first line of + the specified test method's docstring. + """ + doc = self.__testMethodDoc + return doc and string.strip(string.split(doc, "\n")[0]) or None + + def id(self): + return "%s.%s" % (self.__class__, self.__testMethodName) + + def __str__(self): + return "%s (%s)" % (self.__testMethodName, self.__class__) + + def __repr__(self): + return "<%s testMethod=%s>" % \ + (self.__class__, self.__testMethodName) + + def run(self, result=None): + return self(result) + + def __call__(self, result=None): + if result is None: result = self.defaultTestResult() + result.startTest(self) + testMethod = getattr(self, self.__testMethodName) + try: + try: + self.setUp() + except: + result.addError(self,self.__exc_info()) + return + + ok = 0 + try: + testMethod() + ok = 1 + except self.failureException, e: + result.addFailure(self,self.__exc_info()) + except: + result.addError(self,self.__exc_info()) + + try: + self.tearDown() + except: + result.addError(self,self.__exc_info()) + ok = 0 + if ok: result.addSuccess(self) + finally: + result.stopTest(self) + + def debug(self): + """Run the test without collecting errors in a TestResult""" + self.setUp() + getattr(self, self.__testMethodName)() + self.tearDown() + + def __exc_info(self): + """Return a version of sys.exc_info() with the traceback frame + minimised; usually the top level of the traceback frame is not + needed. + """ + exctype, excvalue, tb = sys.exc_info() + if sys.platform[:4] == 'java': ## tracebacks look different in Jython + return (exctype, excvalue, tb) + newtb = tb.tb_next + if newtb is None: + return (exctype, excvalue, tb) + return (exctype, excvalue, newtb) + + def fail(self, msg=None): + """Fail immediately, with the given message.""" + raise self.failureException, msg + + def failIf(self, expr, msg=None): + "Fail the test if the expression is true." + if expr: raise self.failureException, msg + + def failUnless(self, expr, msg=None): + """Fail the test unless the expression is true.""" + if not expr: raise self.failureException, msg + + def failUnlessRaises(self, excClass, callableObj, *args, **kwargs): + """Fail unless an exception of class excClass is thrown + by callableObj when invoked with arguments args and keyword + arguments kwargs. If a different type of exception is + thrown, it will not be caught, and the test case will be + deemed to have suffered an error, exactly as for an + unexpected exception. + """ + try: + apply(callableObj, args, kwargs) + except excClass: + return + else: + if hasattr(excClass,'__name__'): excName = excClass.__name__ + else: excName = str(excClass) + raise self.failureException, excName + + def failUnlessEqual(self, first, second, msg=None): + """Fail if the two objects are unequal as determined by the '!=' + operator. + """ + if first != second: + raise self.failureException, (msg or '%s != %s' % (first, second)) + + def failIfEqual(self, first, second, msg=None): + """Fail if the two objects are equal as determined by the '==' + operator. + """ + if first == second: + raise self.failureException, (msg or '%s == %s' % (first, second)) + + assertEqual = assertEquals = failUnlessEqual + + assertNotEqual = assertNotEquals = failIfEqual + + assertRaises = failUnlessRaises + + assert_ = failUnless + + + +class TestSuite: + """A test suite is a composite test consisting of a number of TestCases. + + For use, create an instance of TestSuite, then add test case instances. + When all tests have been added, the suite can be passed to a test + runner, such as TextTestRunner. It will run the individual test cases + in the order in which they were added, aggregating the results. When + subclassing, do not forget to call the base class constructor. + """ + def __init__(self, tests=()): + self._tests = [] + self.addTests(tests) + + def __repr__(self): + return "<%s tests=%s>" % (self.__class__, self._tests) + + __str__ = __repr__ + + def countTestCases(self): + cases = 0 + for test in self._tests: + cases = cases + test.countTestCases() + return cases + + def addTest(self, test): + self._tests.append(test) + + def addTests(self, tests): + for test in tests: + self.addTest(test) + + def run(self, result): + return self(result) + + def __call__(self, result): + for test in self._tests: + if result.shouldStop: + break + test(result) + return result + + def debug(self): + """Run the tests without collecting errors in a TestResult""" + for test in self._tests: test.debug() + + +class FunctionTestCase(TestCase): + """A test case that wraps a test function. + + This is useful for slipping pre-existing test functions into the + PyUnit framework. Optionally, set-up and tidy-up functions can be + supplied. As with TestCase, the tidy-up ('tearDown') function will + always be called if the set-up ('setUp') function ran successfully. + """ + + def __init__(self, testFunc, setUp=None, tearDown=None, + description=None): + TestCase.__init__(self) + self.__setUpFunc = setUp + self.__tearDownFunc = tearDown + self.__testFunc = testFunc + self.__description = description + + def setUp(self): + if self.__setUpFunc is not None: + self.__setUpFunc() + + def tearDown(self): + if self.__tearDownFunc is not None: + self.__tearDownFunc() + + def runTest(self): + self.__testFunc() + + def id(self): + return self.__testFunc.__name__ + + def __str__(self): + return "%s (%s)" % (self.__class__, self.__testFunc.__name__) + + def __repr__(self): + return "<%s testFunc=%s>" % (self.__class__, self.__testFunc) + + def shortDescription(self): + if self.__description is not None: return self.__description + doc = self.__testFunc.__doc__ + return doc and string.strip(string.split(doc, "\n")[0]) or None + + + +############################################################################## +# Locating and loading tests +############################################################################## + +class TestLoader: + """This class is responsible for loading tests according to various + criteria and returning them wrapped in a Test + """ + testMethodPrefix = 'test' + sortTestMethodsUsing = cmp + suiteClass = TestSuite + + def loadTestsFromTestCase(self, testCaseClass): + """Return a suite of all tests cases contained in testCaseClass""" + return self.suiteClass(map(testCaseClass, + self.getTestCaseNames(testCaseClass))) + + def loadTestsFromModule(self, module): + """Return a suite of all tests cases contained in the given module""" + tests = [] + for name in dir(module): + obj = getattr(module, name) + if type(obj) == types.ClassType and issubclass(obj, TestCase): + tests.append(self.loadTestsFromTestCase(obj)) + return self.suiteClass(tests) + + def loadTestsFromName(self, name, module=None): + """Return a suite of all tests cases given a string specifier. + + The name may resolve either to a module, a test case class, a + test method within a test case class, or a callable object which + returns a TestCase or TestSuite instance. + + The method optionally resolves the names relative to a given module. + """ + parts = string.split(name, '.') + if module is None: + if not parts: + raise ValueError, "incomplete test name: %s" % name + else: + parts_copy = parts[:] + while parts_copy: + try: + module = __import__(string.join(parts_copy,'.')) + break + except ImportError: + del parts_copy[-1] + if not parts_copy: raise + parts = parts[1:] + obj = module + for part in parts: + obj = getattr(obj, part) + + if type(obj) == types.ModuleType: + return self.loadTestsFromModule(obj) + elif type(obj) == types.ClassType and issubclass(obj, TestCase): + return self.loadTestsFromTestCase(obj) + elif type(obj) == types.UnboundMethodType: + return obj.im_class(obj.__name__) + elif callable(obj): + test = obj() + if not isinstance(test, TestCase) and \ + not isinstance(test, TestSuite): + raise ValueError, \ + "calling %s returned %s, not a test" % obj,test + return test + else: + raise ValueError, "don't know how to make test from: %s" % obj + + def loadTestsFromNames(self, names, module=None): + """Return a suite of all tests cases found using the given sequence + of string specifiers. See 'loadTestsFromName()'. + """ + suites = [] + for name in names: + suites.append(self.loadTestsFromName(name, module)) + return self.suiteClass(suites) + + def getTestCaseNames(self, testCaseClass): + """Return a sorted sequence of method names found within testCaseClass + """ + testFnNames = filter(lambda n,p=self.testMethodPrefix: n[:len(p)] == p, + dir(testCaseClass)) + for baseclass in testCaseClass.__bases__: + for testFnName in self.getTestCaseNames(baseclass): + if testFnName not in testFnNames: # handle overridden methods + testFnNames.append(testFnName) + if self.sortTestMethodsUsing: + testFnNames.sort(self.sortTestMethodsUsing) + return testFnNames + + + +defaultTestLoader = TestLoader() + + +############################################################################## +# Patches for old functions: these functions should be considered obsolete +############################################################################## + +def _makeLoader(prefix, sortUsing, suiteClass=None): + loader = TestLoader() + loader.sortTestMethodsUsing = sortUsing + loader.testMethodPrefix = prefix + if suiteClass: loader.suiteClass = suiteClass + return loader + +def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp): + return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass) + +def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite): + return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass) + +def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite): + return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module) + + +############################################################################## +# Text UI +############################################################################## + +class _WritelnDecorator: + """Used to decorate file-like objects with a handy 'writeln' method""" + def __init__(self,stream): + self.stream = stream + + def __getattr__(self, attr): + return getattr(self.stream,attr) + + def writeln(self, *args): + if args: apply(self.write, args) + self.write('\n') # text-mode streams translate to \r\n if needed + + +class _TextTestResult(TestResult): + """A test result class that can print formatted text results to a stream. + + Used by TextTestRunner. + """ + separator1 = '=' * 70 + separator2 = '-' * 70 + + def __init__(self, stream, descriptions, verbosity): + TestResult.__init__(self) + self.stream = stream + self.showAll = verbosity > 1 + self.dots = verbosity == 1 + self.descriptions = descriptions + + def getDescription(self, test): + if self.descriptions: + return test.shortDescription() or str(test) + else: + return str(test) + + def startTest(self, test): + TestResult.startTest(self, test) + if self.showAll: + self.stream.write(self.getDescription(test)) + self.stream.write(" ... ") + + def addSuccess(self, test): + TestResult.addSuccess(self, test) + if self.showAll: + self.stream.writeln("ok") + elif self.dots: + self.stream.write('.') + + def addError(self, test, err): + TestResult.addError(self, test, err) + if self.showAll: + self.stream.writeln("ERROR") + elif self.dots: + self.stream.write('E') + if err[0] is KeyboardInterrupt: + self.shouldStop = 1 + + def addFailure(self, test, err): + TestResult.addFailure(self, test, err) + if self.showAll: + self.stream.writeln("FAIL") + elif self.dots: + self.stream.write('F') + + def printErrors(self): + if self.dots or self.showAll: + self.stream.writeln() + self.printErrorList('ERROR', self.errors) + self.printErrorList('FAIL', self.failures) + + def printErrorList(self, flavour, errors): + for test, err in errors: + self.stream.writeln(self.separator1) + self.stream.writeln("%s: %s" % (flavour,self.getDescription(test))) + self.stream.writeln(self.separator2) + for line in apply(traceback.format_exception, err): + for l in string.split(line,"\n")[:-1]: + self.stream.writeln("%s" % l) + + +class TextTestRunner: + """A test runner class that displays results in textual form. + + It prints out the names of tests as they are run, errors as they + occur, and a summary of the results at the end of the test run. + """ + def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1): + self.stream = _WritelnDecorator(stream) + self.descriptions = descriptions + self.verbosity = verbosity + + def _makeResult(self): + return _TextTestResult(self.stream, self.descriptions, self.verbosity) + + def run(self, test): + "Run the given test case or test suite." + result = self._makeResult() + startTime = time.time() + test(result) + stopTime = time.time() + timeTaken = float(stopTime - startTime) + result.printErrors() + self.stream.writeln(result.separator2) + run = result.testsRun + self.stream.writeln("Ran %d test%s in %.3fs" % + (run, run == 1 and "" or "s", timeTaken)) + self.stream.writeln() + if not result.wasSuccessful(): + self.stream.write("FAILED (") + failed, errored = map(len, (result.failures, result.errors)) + if failed: + self.stream.write("failures=%d" % failed) + if errored: + if failed: self.stream.write(", ") + self.stream.write("errors=%d" % errored) + self.stream.writeln(")") + else: + self.stream.writeln("OK") + return result + + + +############################################################################## +# Facilities for running tests from the command line +############################################################################## + +class TestProgram: + """A command-line program that runs a set of tests; this is primarily + for making test modules conveniently executable. + """ + USAGE = """\ +Usage: %(progName)s [options] [test] [...] + +Options: + -h, --help Show this message + -v, --verbose Verbose output + -q, --quiet Minimal output + +Examples: + %(progName)s - run default set of tests + %(progName)s MyTestSuite - run suite 'MyTestSuite' + %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething + %(progName)s MyTestCase - run all 'test*' test methods + in MyTestCase +""" + def __init__(self, module='__main__', defaultTest=None, + argv=None, testRunner=None, testLoader=defaultTestLoader): + if type(module) == type(''): + self.module = __import__(module) + for part in string.split(module,'.')[1:]: + self.module = getattr(self.module, part) + else: + self.module = module + if argv is None: + argv = sys.argv + self.verbosity = 1 + self.defaultTest = defaultTest + self.testRunner = testRunner + self.testLoader = testLoader + self.progName = os.path.basename(argv[0]) + self.parseArgs(argv) + self.runTests() + + def usageExit(self, msg=None): + if msg: print msg + print self.USAGE % self.__dict__ + sys.exit(2) + + def parseArgs(self, argv): + import getopt + try: + options, args = getopt.getopt(argv[1:], 'hHvq', + ['help','verbose','quiet']) + for opt, value in options: + if opt in ('-h','-H','--help'): + self.usageExit() + if opt in ('-q','--quiet'): + self.verbosity = 0 + if opt in ('-v','--verbose'): + self.verbosity = 2 + if len(args) == 0 and self.defaultTest is None: + self.test = self.testLoader.loadTestsFromModule(self.module) + return + if len(args) > 0: + self.testNames = args + else: + self.testNames = (self.defaultTest,) + self.createTests() + except getopt.error, msg: + self.usageExit(msg) + + def createTests(self): + self.test = self.testLoader.loadTestsFromNames(self.testNames, + self.module) + + def runTests(self): + if self.testRunner is None: + self.testRunner = TextTestRunner(verbosity=self.verbosity) + result = self.testRunner.run(self.test) + sys.exit(not result.wasSuccessful()) + +main = TestProgram + + +############################################################################## +# Executing this module from the command line +############################################################################## + +if __name__ == "__main__": + main(module=None) |