# HG changeset patch # User Bobby Impollonia # Date 1290021519 28800 # Node ID 395af41fe34e950e26ef1a98293b43671535db46 # Parent 92088733aadec76dd702b1d1204c4b42a3bc2d8b Add benchmark using Mako (http://www.makotemplates.org/) diff -r 92088733aade -r 395af41fe34e README.txt --- a/README.txt Wed Nov 17 19:00:32 2010 +0100 +++ b/README.txt Wed Nov 17 11:18:39 2010 -0800 @@ -15,13 +15,13 @@ more useful than others. If you're interested in overall system performance, the best guide is this: - $ python unladen-bmarks/perf.py -r -b default /control/python /test/python + $ python perf.py -r -b default /control/python /test/python That will run the benchmarks we consider the most important headline indicators of performance. There's an additional collection of whole-app benchmarks that are important, but take longer to run: - $ python unladen-bmarks/perf.py -r -b apps /control/python /test/python + $ python perf.py -r -b apps /control/python /test/python Available Benchmarks @@ -34,14 +34,18 @@ - call_method_slots - method calls on classes that use __slots__. - call_method_unknown - method calls where the receiver cannot be predicted. - django - use the Django template system to build a 150x150-cell HTML table. +- fastpickle - use the cPickle module to pickle a variety of datasets. +- fastunnpickle - use the cPickle module to unnpickle a variety of datasets. - float - artificial, floating point-heavy benchmark originally used by Factor. - html5lib - parse the HTML 5 spec using html5lib. - html5lib_warmup - like html5lib, but gives the JIT a chance to warm up by doing the iterations in the same process. +- mako - use the Mako template system to build a 150x150-cell HTML table. - nbody - the N-body Shootout benchmark. Microbenchmark for floating point operations. - nqueens - small solver for the N-Queens problem. -- pickle - use the cPickle module to pickle a variety of datasets. +- pickle - use the cPickle and pure-Python pickle modules to pickle and unpickle + a variety of datasets. - pickle_dict - microbenchmark; use the cPickle module to pickle a lot of dicts. - pickle_list - microbenchmark; use the cPickle module to pickle a lot of lists. - pybench - run the standard Python PyBench benchmark suite. This is considered @@ -89,6 +93,7 @@ - Django: r9654 from http://code.djangoproject.com/svn/django/trunk - htm5lib: rev 16b9dacec7 from https://html5lib.googlecode.com/hg/ - lockfile: release 0.3 +- Mako: rev 9ca34926d830 from http://hg.makotemplates.org/mako - Mercurial: release 1.2.1 - Psyco: release 1.6 - PyBench: release 2.6.4 diff -r 92088733aade -r 395af41fe34e lib/mako/.hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/.hgignore Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,7 @@ +syntax:regexp +^build/ +^doc/build/output +.pyc$ +.orig$ +Mako.egg-info +easy-install.pth diff -r 92088733aade -r 395af41fe34e lib/mako/CHANGES --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/CHANGES Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,590 @@ +0.3.6 +- Documentation is on Sphinx. + [ticket:126] + +- Beaker is now part of "extras" in + setup.py instead of "install_requires". + This to produce a lighter weight install + for those who don't use the caching + as well as to conform to Pyramid + deployment practices. [ticket:154] + +- The Beaker import (or attempt thereof) + is delayed until actually needed; + this to remove the performance penalty + from startup, particularly for + "single execution" environments + such as shell scripts. [ticket:153] + +- Patch to lexer to not generate an empty + '' write in the case of backslash-ended + lines. [ticket:155] + +- Fixed missing **extra collection in + setup.py which prevented setup.py + from running 2to3 on install. + [ticket:148] + +- New flag on Template, TemplateLookup - + strict_undefined=True, will cause + variables not found in the context to + raise a NameError immediately, instead of + defaulting to the UNDEFINED value. + +- The range of Python identifiers that + are considered "undefined", meaning they + are pulled from the context, has been + trimmed back to not include variables + declared inside of expressions (i.e. from + list comprehensions), as well as + in the argument list of lambdas. This + to better support the strict_undefined + feature. The change should be + fully backwards-compatible but involved + a little bit of tinkering in the AST code, + which hadn't really been touched for + a couple of years, just FYI. + +0.3.5 +- The <%namespace> tag allows expressions + for the `file` argument, i.e. with ${}. + The `context` variable, if needed, + must be referenced explicitly. + [ticket:141] + +- ${} expressions embedded in tags, + such as <%foo:bar x="${...}">, now + allow multiline Python expressions. + +- Fixed previously non-covered regular + expression, such that using a ${} expression + inside of a tag element that doesn't allow + them raises a CompileException instead of + silently failing. + +- Added a try/except around "import markupsafe". + This to support GAE which can't run markupsafe. + [ticket:151] No idea whatsoever if the + install_requires in setup.py also breaks GAE, + couldn't get an answer on this. + +0.3.4 +- Now using MarkupSafe for HTML escaping, + i.e. in place of cgi.escape(). Faster + C-based implementation and also escapes + single quotes for additional security. + Supports the __html__ attribute for + the given expression as well. + + When using "disable_unicode" mode, + a pure Python HTML escaper function + is used which also quotes single quotes. + + Note that Pylons by default doesn't + use Mako's filter - check your + environment.py file. + +- Fixed call to "unicode.strip" in + exceptions.text_error_template which + is not Py3k compatible. [ticket:137] + +0.3.3 +- Added conditional to RichTraceback + such that if no traceback is passed + and sys.exc_info() has been reset, + the formatter just returns blank + for the "traceback" portion. + [ticket:135] + +- Fixed sometimes incorrect usage of + exc.__class__.__name__ + in html/text error templates when using + Python 2.4 [ticket:131] + +- Fixed broken @property decorator on + template.last_modified + +- Fixed error formatting when a stacktrace + line contains no line number, as in when + inside an eval/exec-generated function. + [ticket:132] + +- When a .py is being created, the tempfile + where the source is stored temporarily is + now made in the same directory as that of + the .py file. This ensures that the two + files share the same filesystem, thus + avoiding cross-filesystem synchronization + issues. Thanks to Charles Cazabon. + +0.3.2 +- Calling a def from the top, via + template.get_def(...).render() now checks the + argument signature the same way as it did in + 0.2.5, so that TypeError is not raised. + reopen of [ticket:116] + + +0.3.1 +- Fixed incorrect dir name in setup.py + [ticket:129] + +0.3 +- Python 2.3 support is dropped. [ticket:123] + +- Python 3 support is added ! See README.py3k + for installation and testing notes. + [ticket:119] + +- Unit tests now run with nose. [ticket:127] + +- Source code escaping has been simplified. + In particular, module source files are now + generated with the Python "magic encoding + comment", and source code is passed through + mostly unescaped, except for that code which + is regenerated from parsed Python source. + This fixes usage of unicode in + <%namespace:defname> tags. [ticket:99] + +- RichTraceback(), html_error_template().render(), + text_error_template().render() now accept "error" + and "traceback" as optional arguments, and + these are now actually used. [ticket:122] + +- The exception output generated when + format_exceptions=True will now be as a Python + unicode if it occurred during render_unicode(), + or an encoded string if during render(). + +- A percent sign can be emitted as the first + non-whitespace character on a line by escaping + it as in "%%". [ticket:112] + +- Template accepts empty control structure, i.e. + % if: %endif, etc. [ticket:94] + +- The <%page args> tag can now be used in a base + inheriting template - the full set of render() + arguments are passed down through the inherits + chain. Undeclared arguments go into **pageargs + as usual. [ticket:116] + +- defs declared within a <%namespace> section, an + uncommon feature, have been improved. The defs + no longer get doubly-rendered in the body() scope, + and now allow local variable assignment without + breakage. [ticket:109] + +- Windows paths are handled correctly if a Template + is passed only an absolute filename (i.e. with c: + drive etc.) and no URI - the URI is converted + to a forward-slash path and module_directory + is treated as a windows path. [ticket:128] + +- TemplateLookup raises TopLevelLookupException for + a given path that is a directory, not a filename, + instead of passing through to the template to + generate IOError. [ticket:73] + +0.2.6 + +- Fix mako function decorators to preserve the + original function's name in all cases. Patch + from Scott Torborg. + +- Support the <%namespacename:defname> syntax in + the babel extractor. [ticket:118] + +- Further fixes to unicode handling of .py files with the + html_error_template. [ticket:88] + +0.2.5 +- Added a "decorator" kw argument to <%def>, + allows custom decoration functions to wrap + rendering callables. Mainly intended for + custom caching algorithms, not sure what + other uses there may be (but there may be). + Examples are in the "filtering" docs. + +- When Mako creates subdirectories in which + to store templates, it uses the more + permissive mode of 0775 instead of 0750, + helping out with certain multi-process + scenarios. Note that the mode is always + subject to the restrictions of the existing + umask. [ticket:101] + +- Fixed namespace.__getattr__() to raise + AttributeError on attribute not found + instead of RuntimeError. [ticket:104] + +- Added last_modified accessor to Template, + returns the time.time() when the module + was created. [ticket:97] + +- Fixed lexing support for whitespace + around '=' sign in defs. [ticket:102] + +- Removed errant "lower()" in the lexer which + was causing tags to compile with + case-insensitive names, thus messing up + custom <%call> names. [ticket:108] + +- added "mako.__version__" attribute to + the base module. [ticket:110] + +0.2.4 +- Fixed compatibility with Jython 2.5b1. + +0.2.3 +- the <%namespacename:defname> syntax described at + http://techspot.zzzeek.org/?p=28 has now + been added as a built in syntax, and is recommended + as a more modern syntax versus <%call expr="expression">. + The %call tag itself will always remain, + with <%namespacename:defname> presenting a more HTML-like + alternative to calling defs, both plain and + nested. Many examples of the new syntax are in the + "Calling a def with embedded content" section + of the docs. + +- added support for Jython 2.5. + +- cache module now uses Beaker's CacheManager + object directly, so that all cache types are included. + memcached is available as both "ext:memcached" and + "memcached", the latter for backwards compatibility. + +- added "cache" accessor to Template, Namespace. + e.g. ${local.cache.get('somekey')} or + template.cache.invalidate_body() + +- added "cache_enabled=True" flag to Template, + TemplateLookup. Setting this to False causes cache + operations to "pass through" and execute every time; + this flag should be integrated in Pylons with its own + cache_enabled configuration setting. + +- the Cache object now supports invalidate_def(name), + invalidate_body(), invalidate_closure(name), + invalidate(key), which will remove the given key + from the cache, if it exists. The cache arguments + (i.e. storage type) are derived from whatever has + been already persisted for that template. + [ticket:92] + +- For cache changes to work fully, Beaker 1.1 is required. + 1.0.1 and up will work as well with the exception of + cache expiry. Note that Beaker 1.1 is **required** + for applications which use dynamically generated keys, + since previous versions will permanently store state in memory + for each individual key, thus consuming all available + memory for an arbitrarily large number of distinct + keys. + +- fixed bug whereby an <%included> template with + <%page> args named the same as a __builtin__ would not + honor the default value specified in <%page> [ticket:93] + +- fixed the html_error_template not handling tracebacks from + normal .py files with a magic encoding comment [ticket:88] + +- RichTraceback() now accepts an optional traceback object + to be used in place of sys.exc_info()[2]. html_error_template() + and text_error_template() accept an optional + render()-time argument "traceback" which is passed to the + RichTraceback object. + +- added ModuleTemplate class, which allows the construction + of a Template given a Python module generated by a previous + Template. This allows Python modules alone to be used + as templates with no compilation step. Source code + and template source are optional but allow error reporting + to work correctly. + +- fixed Python 2.3 compat. in mako.pyparser [ticket:90] + +- fix Babel 0.9.3 compatibility; stripping comment tags is now + optional (and enabled by default). + + +0.2.2 +- cached blocks now use the current context when rendering +an expired section, instead of the original context +passed in [ticket:87] +- fixed a critical issue regarding caching, whereby +a cached block would raise an error when called within a +cache-refresh operation that was initiated after the +initiating template had completed rendering. + +0.2.1 +- fixed bug where 'output_encoding' parameter would prevent +render_unicode() from returning a unicode object. +- bumped magic number, which forces template recompile for +this version (fixes incompatible compile symbols from 0.1 +series). +- added a few docs for cache options, specifically those that +help with memcached. + +0.2.0 +- Speed improvements (as though we needed them, but people + contributed and there you go): + + - added "bytestring passthru" mode, via + `disable_unicode=True` argument passed to Template or + TemplateLookup. All unicode-awareness and filtering is + turned off, and template modules are generated with + the appropriate magic encoding comment. In this mode, + template expressions can only receive raw bytestrings + or Unicode objects which represent straight ASCII, and + render_unicode() may not be used if multibyte + characters are present. When enabled, speed + improvement around 10-20%. [ticket:77] (courtesy + anonymous guest) + + - inlined the "write" function of Context into a local + template variable. This affords a 12-30% speedup in + template render time. (idea courtesy same anonymous + guest) [ticket:76] + +- New Features, API changes: + + - added "attr" accessor to namespaces. Returns + attributes configured as module level attributes, i.e. + within <%! %> sections. [ticket:62] i.e.: + + # somefile.html + <%! + foo = 27 + %> + + # some other template + <%namespace name="myns" file="somefile.html"/> + ${myns.attr.foo} + + The slight backwards incompatibility here is, you + can't have namespace defs named "attr" since the + "attr" descriptor will occlude it. + + - cache_key argument can now render arguments passed + directly to the %page or %def, i.e. <%def + name="foo(x)" cached="True" cache_key="${x}"/> + [ticket:78] + + - some functions on Context are now private: + _push_buffer(), _pop_buffer(), + caller_stack._push_frame(), caller_stack._pop_frame(). + + - added a runner script "mako-render" which renders + standard input as a template to stdout [ticket:81] + [ticket:56] + +- Bugfixes: + - can now use most names from __builtins__ as variable + names without explicit declaration (i.e. 'id', + 'exception', 'range', etc.) [ticket:83] [ticket:84] + + - can also use builtin names as local variable names + (i.e. dict, locals) (came from fix for [ticket:84]) + + - fixed bug in python generation when variable names are + used with identifiers like "else", "finally", etc. + inside them [ticket:68] + + - fixed codegen bug which occured when using <%page> + level caching, combined with an expression-based + cache_key, combined with the usage of <%namespace + import="*"/> - fixed lexer exceptions not cleaning up + temporary files, which could lead to a maximum number + of file descriptors used in the process [ticket:69] + + - fixed issue with inline format_exceptions that was + producing blank exception pages when an inheriting + template is present [ticket:71] + + - format_exceptions will apply the encoding options of + html_error_template() to the buffered output + + - rewrote the "whitespace adjuster" function to work + with more elaborate combinations of quotes and + comments [ticket:75] + +0.1.10 +- fixed propagation of 'caller' such that nested %def calls + within a <%call> tag's argument list propigates 'caller' + to the %call function itself (propigates to the inner + calls too, this is a slight side effect which previously + existed anyway) +- fixed bug where local.get_namespace() could put an + incorrect "self" in the current context +- fixed another namespace bug where the namespace functions + did not have access to the correct context containing + their 'self' and 'parent' + +0.1.9 +- filters.Decode filter can also accept a non-basestring +object and will call str() + unicode() on it [ticket:47] +- comments can be placed at the end of control lines, +i.e. if foo: # a comment, [ticket:53], thanks to +Paul Colomiets +- fixed expressions and page tag arguments and with embedded +newlines in CRLF templates, follow up to [ticket:16], thanks +Eric Woroshow +- added an IOError catch for source file not found in RichTraceback +exception reporter [ticket:51] + +0.1.8 +- variable names declared in render methods by internal +codegen prefixed by "__M_" to prevent name collisions +with user code +- added a Babel (http://babel.edgewall.org/) extractor entry +point, allowing extraction of gettext messages directly from +mako templates via Babel [ticket:45] +- fix to turbogears plugin to work with dot-separated names +(i.e. load_template('foo.bar')). also takes file extension +as a keyword argument (default is 'mak'). +- more tg fix: fixed [ticket:35], allowing string-based +templates with tgplugin even if non-compatible args were sent + +0.1.7 +- one small fix to the unit tests to support python 2.3 +- a slight hack to how cache.py detects Beaker's memcached, +works around unexplained import behavior observed on some +python 2.3 installations + +0.1.6 +- caching is now supplied directly by Beaker, which has + all of MyghtyUtils merged into it now. The latest Beaker + (0.7.1) also fixes a bug related to how Mako was using the + cache API. +- fix to module_directory path generation when the path is "./" + [ticket:34] +- TGPlugin passes options to string-based templates [ticket:35] +- added an explicit stack frame step to template runtime, which + allows much simpler and hopefully bug-free tracking of 'caller', + fixes #28 +- if plain Python defs are used with <%call>, a decorator + @runtime.supports_callable exists to ensure that the "caller" + stack is properly handled for the def. +- fix to RichTraceback and exception reporting to get template + source code as a unicode object #37 +- html_error_template includes options "full=True", "css=True" + which control generation of HTML tags, CSS [ticket:39] +- added the 'encoding_errors' parameter to Template/TemplateLookup + for specifying the error handler associated with encoding to + 'output_encoding' [ticket:40] +- the Template returned by html_error_template now defaults to + output_encoding=sys.getdefaultencoding(), + encoding_errors='htmlentityreplace' [ticket:37] +- control lines, i.e. % lines, support backslashes to continue long + lines (#32) +- fixed codegen bug when defining <%def> within <%call> within <%call> +- leading utf-8 BOM in template files is honored according to pep-0263 + +0.1.5 +- AST expression generation - added in just about everything + expression-wise from the AST module [ticket:26] +- AST parsing, properly detects imports of the form "import foo.bar" + [ticket:27] +- fix to lexing of <%docs> tag nested in other tags +- fix to context-arguments inside of <%include> tag which broke +during 0.1.4 [ticket:29] +- added "n" filter, disables *all* filters normally applied to an expression +via <%page> or default_filters (but not those within the filter) +- added buffer_filters argument, defines filters applied to the return value +of buffered/cached/filtered %defs, after all filters defined with the %def +itself have been applied. allows the creation of default expression filters +that let the output of return-valued %defs "opt out" of that filtering +via passing special attributes or objects. + +0.1.4 +- got defs-within-defs to be cacheable +- fixes to code parsing/whitespace adjusting where plain python comments + may contain quote characters [ticket:23] +- fix to variable scoping for identifiers only referenced within + functions +- added a path normalization step to lookup so URIs like + "/foo/bar/../etc/../foo" pre-process the ".." tokens before checking + the filesystem +- fixed/improved "caller" semantics so that undefined caller is + "UNDEFINED", propigates __nonzero__ method so it evaulates to False if + not present, True otherwise. this way you can say % if caller:\n + ${caller.body()}\n% endif +- <%include> has an "args" attribute that can pass arguments to the + called template (keyword arguments only, must be declared in that + page's <%page> tag.) +- <%include> plus arguments is also programmatically available via + self.include_file(, **kwargs) +- further escaping added for multibyte expressions in %def, %call + attributes [ticket:24] + + +0.1.3 +- ***Small Syntax Change*** - the single line comment character is now +*two* hash signs, i.e. "## this is a comment". This avoids a common +collection with CSS selectors. +- the magic "coding" comment (i.e. # coding:utf-8) will still work with +either one "#" sign or two for now; two is preferred going forward, i.e. +## coding:. +- new multiline comment form: "<%doc> a comment " +- UNDEFINED evaluates to False +- improvement to scoping of "caller" variable when using <%call> tag +- added lexer error for unclosed control-line (%) line +- added "preprocessor" argument to Template, TemplateLookup - is a single + callable or list of callables which will be applied to the template text + before lexing. given the text as an argument, returns the new text. +- added mako.ext.preprocessors package, contains one preprocessor so far: + 'convert_comments', which will convert single # comments to the new ## + format + +0.1.2 +- fix to parsing of code/expression blocks to insure that non-ascii + characters, combined with a template that indicates a non-standard + encoding, are expanded into backslash-escaped glyphs before being AST + parsed [ticket:11] +- all template lexing converts the template to unicode first, to + immediately catch any encoding issues and ensure internal unicode + representation. +- added module_filename argument to Template to allow specification of a + specific module file +- added modulename_callable to TemplateLookup to allow a function to + determine module filenames (takes filename, uri arguments). used for + [ticket:14] +- added optional input_encoding flag to Template, to allow sending a + unicode() object with no magic encoding comment +- "expression_filter" argument in <%page> applies only to expressions +- added "default_filters" argument to Template, TemplateLookup. applies only + to expressions, gets prepended to "expression_filter" arg from <%page>. + defaults to ["unicode"], so that all expressions get stringified into u'' + by default (this is what Mako already does). By setting to [], expressions + are passed through raw. +- added "imports" argument to Template, TemplateLookup. so you can predefine + a list of import statements at the top of the template. can be used in + conjunction with default_filters. +- support for CRLF templates...whoops ! welcome to all the windows users. + [ticket:16] +- small fix to local variable propigation for locals that are conditionally + declared +- got "top level" def calls to work, i.e. template.get_def("somedef").render() + +0.1.1 +- buffet plugin supports string-based templates, allows ToscaWidgets to work + [ticket:8] +- AST parsing fixes: fixed TryExcept identifier parsing +- removed textmate tmbundle from contrib and into separate SVN location; + windows users cant handle those files, setuptools not very good at + "pruning" certain directories +- fix so that "cache_timeout" parameter is propigated +- fix to expression filters so that string conversion (actually unicode) + properly occurs before filtering +- better error message when a lookup is attempted with a template that has no + lookup +- implemented "module" attribute for namespace +- fix to code generation to correctly track multiple defs with the same name +- "directories" can be passed to TemplateLookup as a scalar in which case it + gets converted to a list [ticket:9] + +0.1.0 + +Initial release. diff -r 92088733aade -r 395af41fe34e lib/mako/LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/LICENSE Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,20 @@ +This is the MIT license: http://www.opensource.org/licenses/mit-license.php + +Copyright (C) 2006, 2007, 2008, 2009, 2010 Michael Bayer and contributors. +Mako is a trademark of Michael Bayer. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff -r 92088733aade -r 395af41fe34e lib/mako/MANIFEST.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/MANIFEST.in Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,12 @@ +# any kind of "*" pulls in __init__.pyc files, +# so all extensions are explicit. + +recursive-include doc *.html *.css *.txt *.js *.jpg *.png *.py Makefile *.rst *.mako *.sty autohandler +recursive-include examples *.py *.xml *.mako *.myt *.kid *.tmpl +recursive-include test *.py *.dat *.html *.mako + +include README* LICENSE distribute_setup.py ez_setup.py CHANGES* + +# when we go to sphinx +#prune doc/build/output + diff -r 92088733aade -r 395af41fe34e lib/mako/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/README Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,25 @@ +Mako is licensed under an MIT-style license (see LICENSE). +Other incorporated projects may be licensed under different licenses. +All licenses allow for non-commercial and commercial use. + +To install: + + python setup.py install + +SVN checkouts also inlcude setup.cfg file allowing setuptools to create +an svn-tagged build. + +Documentation is available in HTML format in the ./doc/ directory. + +Unit tests run via nose, and are available via setup.py: + + python setup.py test + +Or direct nose usage: + + nosetests -v + +For Python 3 information, see README.py3k. + +good luck ! + diff -r 92088733aade -r 395af41fe34e lib/mako/README.py3k --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/README.py3k Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,56 @@ +================= +PYTHON 3 SUPPORT +================= + +Python 3 support in Mako is provided by the Python 2to3 script. + +Installing Distribute +--------------------- + +Distribute should be installed with the Python3 installation. The +distribute bootloader is included. + +Running as a user with permission to modify the Python distribution, +install Distribute: + + python3 distribute_setup.py + +Installing Mako in Python 3 +--------------------------------- + +Once Distribute is installed, Mako can be installed directly. +The 2to3 process will kick in which takes several minutes: + + python3 setup.py install + +Converting Tests, Examples, Source to Python 3 +---------------------------------------------- + +To convert all files in the source distribution, run +the 2to3 script: + + 2to3 -w mako test + +If using 3.1's 2to3 tool, the --no-diffs flag might help +with unicode issues: + + 2to3-3.1 -w --no-diffs mako test + +The above will rewrite all files in-place in Python 3 format. + +Running Tests +------------- + +To run the unit tests, ensure Distribute is installed as above, +and also that at least the ./mako/ and ./test/ directories have been converted +to Python 3 using the source tool above. A Python 3 version of Nose +can be acquired from Bitbucket using Mercurial: + + hg clone http://bitbucket.org/jpellerin/nose3/ + cd nose3 + python3 setup.py install + +The tests can then be run using the "nosetests3" script installed +by the above (python3 setup.py test doesn't seem to be working with +nose3). + diff -r 92088733aade -r 395af41fe34e lib/mako/distribute_setup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/distribute_setup.py Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,485 @@ +#!python +"""Bootstrap distribute installation + +If you want to use setuptools in your package's setup.py, just include this +file in the same directory with it, and add this to the top of your setup.py:: + + from distribute_setup import use_setuptools + use_setuptools() + +If you want to require a specific version of setuptools, set a download +mirror, or use an alternate download directory, you can do so by supplying +the appropriate options to ``use_setuptools()``. + +This file can also be run as a script to install or upgrade setuptools. +""" +import os +import sys +import time +import fnmatch +import tempfile +import tarfile +from distutils import log + +try: + from site import USER_SITE +except ImportError: + USER_SITE = None + +try: + import subprocess + + def _python_cmd(*args): + args = (sys.executable,) + args + return subprocess.call(args) == 0 + +except ImportError: + # will be used for python 2.3 + def _python_cmd(*args): + args = (sys.executable,) + args + # quoting arguments if windows + if sys.platform == 'win32': + def quote(arg): + if ' ' in arg: + return '"%s"' % arg + return arg + args = [quote(arg) for arg in args] + return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 + +DEFAULT_VERSION = "0.6.13" +DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" +SETUPTOOLS_FAKED_VERSION = "0.6c11" + +SETUPTOOLS_PKG_INFO = """\ +Metadata-Version: 1.0 +Name: setuptools +Version: %s +Summary: xxxx +Home-page: xxx +Author: xxx +Author-email: xxx +License: xxx +Description: xxx +""" % SETUPTOOLS_FAKED_VERSION + + +def _install(tarball): + # extracting the tarball + tmpdir = tempfile.mkdtemp() + log.warn('Extracting in %s', tmpdir) + old_wd = os.getcwd() + try: + os.chdir(tmpdir) + tar = tarfile.open(tarball) + _extractall(tar) + tar.close() + + # going in the directory + subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) + os.chdir(subdir) + log.warn('Now working in %s', subdir) + + # installing + log.warn('Installing Distribute') + if not _python_cmd('setup.py', 'install'): + log.warn('Something went wrong during the installation.') + log.warn('See the error message above.') + finally: + os.chdir(old_wd) + + +def _build_egg(egg, tarball, to_dir): + # extracting the tarball + tmpdir = tempfile.mkdtemp() + log.warn('Extracting in %s', tmpdir) + old_wd = os.getcwd() + try: + os.chdir(tmpdir) + tar = tarfile.open(tarball) + _extractall(tar) + tar.close() + + # going in the directory + subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) + os.chdir(subdir) + log.warn('Now working in %s', subdir) + + # building an egg + log.warn('Building a Distribute egg in %s', to_dir) + _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) + + finally: + os.chdir(old_wd) + # returning the result + log.warn(egg) + if not os.path.exists(egg): + raise IOError('Could not build the egg.') + + +def _do_download(version, download_base, to_dir, download_delay): + egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' + % (version, sys.version_info[0], sys.version_info[1])) + if not os.path.exists(egg): + tarball = download_setuptools(version, download_base, + to_dir, download_delay) + _build_egg(egg, tarball, to_dir) + sys.path.insert(0, egg) + import setuptools + setuptools.bootstrap_install_from = egg + + +def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, + to_dir=os.curdir, download_delay=15, no_fake=True): + # making sure we use the absolute path + to_dir = os.path.abspath(to_dir) + was_imported = 'pkg_resources' in sys.modules or \ + 'setuptools' in sys.modules + try: + try: + import pkg_resources + if not hasattr(pkg_resources, '_distribute'): + if not no_fake: + _fake_setuptools() + raise ImportError + except ImportError: + return _do_download(version, download_base, to_dir, download_delay) + try: + pkg_resources.require("distribute>="+version) + return + except pkg_resources.VersionConflict: + e = sys.exc_info()[1] + if was_imported: + sys.stderr.write( + "The required version of distribute (>=%s) is not available,\n" + "and can't be installed while this script is running. Please\n" + "install a more recent version first, using\n" + "'easy_install -U distribute'." + "\n\n(Currently using %r)\n" % (version, e.args[0])) + sys.exit(2) + else: + del pkg_resources, sys.modules['pkg_resources'] # reload ok + return _do_download(version, download_base, to_dir, + download_delay) + except pkg_resources.DistributionNotFound: + return _do_download(version, download_base, to_dir, + download_delay) + finally: + if not no_fake: + _create_fake_setuptools_pkg_info(to_dir) + +def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, + to_dir=os.curdir, delay=15): + """Download distribute from a specified location and return its filename + + `version` should be a valid distribute version number that is available + as an egg for download under the `download_base` URL (which should end + with a '/'). `to_dir` is the directory where the egg will be downloaded. + `delay` is the number of seconds to pause before an actual download + attempt. + """ + # making sure we use the absolute path + to_dir = os.path.abspath(to_dir) + try: + from urllib.request import urlopen + except ImportError: + from urllib2 import urlopen + tgz_name = "distribute-%s.tar.gz" % version + url = download_base + tgz_name + saveto = os.path.join(to_dir, tgz_name) + src = dst = None + if not os.path.exists(saveto): # Avoid repeated downloads + try: + log.warn("Downloading %s", url) + src = urlopen(url) + # Read/write all in one block, so we don't create a corrupt file + # if the download is interrupted. + data = src.read() + dst = open(saveto, "wb") + dst.write(data) + finally: + if src: + src.close() + if dst: + dst.close() + return os.path.realpath(saveto) + +def _no_sandbox(function): + def __no_sandbox(*args, **kw): + try: + from setuptools.sandbox import DirectorySandbox + if not hasattr(DirectorySandbox, '_old'): + def violation(*args): + pass + DirectorySandbox._old = DirectorySandbox._violation + DirectorySandbox._violation = violation + patched = True + else: + patched = False + except ImportError: + patched = False + + try: + return function(*args, **kw) + finally: + if patched: + DirectorySandbox._violation = DirectorySandbox._old + del DirectorySandbox._old + + return __no_sandbox + +def _patch_file(path, content): + """Will backup the file then patch it""" + existing_content = open(path).read() + if existing_content == content: + # already patched + log.warn('Already patched.') + return False + log.warn('Patching...') + _rename_path(path) + f = open(path, 'w') + try: + f.write(content) + finally: + f.close() + return True + +_patch_file = _no_sandbox(_patch_file) + +def _same_content(path, content): + return open(path).read() == content + +def _rename_path(path): + new_name = path + '.OLD.%s' % time.time() + log.warn('Renaming %s into %s', path, new_name) + os.rename(path, new_name) + return new_name + +def _remove_flat_installation(placeholder): + if not os.path.isdir(placeholder): + log.warn('Unkown installation at %s', placeholder) + return False + found = False + for file in os.listdir(placeholder): + if fnmatch.fnmatch(file, 'setuptools*.egg-info'): + found = True + break + if not found: + log.warn('Could not locate setuptools*.egg-info') + return + + log.warn('Removing elements out of the way...') + pkg_info = os.path.join(placeholder, file) + if os.path.isdir(pkg_info): + patched = _patch_egg_dir(pkg_info) + else: + patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) + + if not patched: + log.warn('%s already patched.', pkg_info) + return False + # now let's move the files out of the way + for element in ('setuptools', 'pkg_resources.py', 'site.py'): + element = os.path.join(placeholder, element) + if os.path.exists(element): + _rename_path(element) + else: + log.warn('Could not find the %s element of the ' + 'Setuptools distribution', element) + return True + +_remove_flat_installation = _no_sandbox(_remove_flat_installation) + +def _after_install(dist): + log.warn('After install bootstrap.') + placeholder = dist.get_command_obj('install').install_purelib + _create_fake_setuptools_pkg_info(placeholder) + +def _create_fake_setuptools_pkg_info(placeholder): + if not placeholder or not os.path.exists(placeholder): + log.warn('Could not find the install location') + return + pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) + setuptools_file = 'setuptools-%s-py%s.egg-info' % \ + (SETUPTOOLS_FAKED_VERSION, pyver) + pkg_info = os.path.join(placeholder, setuptools_file) + if os.path.exists(pkg_info): + log.warn('%s already exists', pkg_info) + return + + log.warn('Creating %s', pkg_info) + f = open(pkg_info, 'w') + try: + f.write(SETUPTOOLS_PKG_INFO) + finally: + f.close() + + pth_file = os.path.join(placeholder, 'setuptools.pth') + log.warn('Creating %s', pth_file) + f = open(pth_file, 'w') + try: + f.write(os.path.join(os.curdir, setuptools_file)) + finally: + f.close() + +_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) + +def _patch_egg_dir(path): + # let's check if it's already patched + pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') + if os.path.exists(pkg_info): + if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): + log.warn('%s already patched.', pkg_info) + return False + _rename_path(path) + os.mkdir(path) + os.mkdir(os.path.join(path, 'EGG-INFO')) + pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') + f = open(pkg_info, 'w') + try: + f.write(SETUPTOOLS_PKG_INFO) + finally: + f.close() + return True + +_patch_egg_dir = _no_sandbox(_patch_egg_dir) + +def _before_install(): + log.warn('Before install bootstrap.') + _fake_setuptools() + + +def _under_prefix(location): + if 'install' not in sys.argv: + return True + args = sys.argv[sys.argv.index('install')+1:] + for index, arg in enumerate(args): + for option in ('--root', '--prefix'): + if arg.startswith('%s=' % option): + top_dir = arg.split('root=')[-1] + return location.startswith(top_dir) + elif arg == option: + if len(args) > index: + top_dir = args[index+1] + return location.startswith(top_dir) + if arg == '--user' and USER_SITE is not None: + return location.startswith(USER_SITE) + return True + + +def _fake_setuptools(): + log.warn('Scanning installed packages') + try: + import pkg_resources + except ImportError: + # we're cool + log.warn('Setuptools or Distribute does not seem to be installed.') + return + ws = pkg_resources.working_set + try: + setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', + replacement=False)) + except TypeError: + # old distribute API + setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) + + if setuptools_dist is None: + log.warn('No setuptools distribution found') + return + # detecting if it was already faked + setuptools_location = setuptools_dist.location + log.warn('Setuptools installation detected at %s', setuptools_location) + + # if --root or --preix was provided, and if + # setuptools is not located in them, we don't patch it + if not _under_prefix(setuptools_location): + log.warn('Not patching, --root or --prefix is installing Distribute' + ' in another location') + return + + # let's see if its an egg + if not setuptools_location.endswith('.egg'): + log.warn('Non-egg installation') + res = _remove_flat_installation(setuptools_location) + if not res: + return + else: + log.warn('Egg installation') + pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') + if (os.path.exists(pkg_info) and + _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): + log.warn('Already patched.') + return + log.warn('Patching...') + # let's create a fake egg replacing setuptools one + res = _patch_egg_dir(setuptools_location) + if not res: + return + log.warn('Patched done.') + _relaunch() + + +def _relaunch(): + log.warn('Relaunching...') + # we have to relaunch the process + # pip marker to avoid a relaunch bug + if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: + sys.argv[0] = 'setup.py' + args = [sys.executable] + sys.argv + sys.exit(subprocess.call(args)) + + +def _extractall(self, path=".", members=None): + """Extract all members from the archive to the current working + directory and set owner, modification time and permissions on + directories afterwards. `path' specifies a different directory + to extract to. `members' is optional and must be a subset of the + list returned by getmembers(). + """ + import copy + import operator + from tarfile import ExtractError + directories = [] + + if members is None: + members = self + + for tarinfo in members: + if tarinfo.isdir(): + # Extract directories with a safe mode. + directories.append(tarinfo) + tarinfo = copy.copy(tarinfo) + tarinfo.mode = 448 # decimal for oct 0700 + self.extract(tarinfo, path) + + # Reverse sort directories. + if sys.version_info < (2, 4): + def sorter(dir1, dir2): + return cmp(dir1.name, dir2.name) + directories.sort(sorter) + directories.reverse() + else: + directories.sort(key=operator.attrgetter('name'), reverse=True) + + # Set correct owner, mtime and filemode on directories. + for tarinfo in directories: + dirpath = os.path.join(path, tarinfo.name) + try: + self.chown(tarinfo, dirpath) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError: + e = sys.exc_info()[1] + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + +def main(argv, version=DEFAULT_VERSION): + """Install or upgrade setuptools and EasyInstall""" + tarball = download_setuptools() + _install(tarball) + + +if __name__ == '__main__': + main(sys.argv[1:]) diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/Makefile Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,143 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = output + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest dist-html site-mako + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dist-html same as html, but places files in /doc" + @echo " site-mako build Mako files for usage on the Mako site" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html -A mako_layout=html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dist-html: + $(SPHINXBUILD) -b html -A mako_layout=html $(ALLSPHINXOPTS) .. + @echo + @echo "Build finished. The HTML pages are in ../." + +site-mako: + $(SPHINXBUILD) -b html -A mako_layout=site $(ALLSPHINXOPTS) $(BUILDDIR)/site + @echo + @echo "Build finished. The Mako pages are in $(BUILDDIR)/site." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SQLAlchemy.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SQLAlchemy.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/SQLAlchemy" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SQLAlchemy" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + cp texinputs/* $(BUILDDIR)/latex/ + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) . + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/builder/builders.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/builder/builders.py Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,69 @@ +from sphinx.application import TemplateBridge +from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.highlighting import PygmentsBridge +from pygments import highlight +from pygments.lexer import RegexLexer, bygroups, using +from pygments.token import * +from pygments.filter import Filter, apply_filters +from pygments.lexers import PythonLexer, PythonConsoleLexer +from pygments.formatters import HtmlFormatter, LatexFormatter +import re +from mako.lookup import TemplateLookup +from mako.template import Template +from mako.ext.pygmentplugin import MakoLexer + +class MakoBridge(TemplateBridge): + def init(self, builder, *args, **kw): + self.layout = builder.config.html_context.get('mako_layout', 'html') + + self.lookup = TemplateLookup(directories=builder.config.templates_path, + format_exceptions=True, + imports=[ + "from builder import util" + ] + ) + + def render(self, template, context): + template = template.replace(".html", ".mako") + context['prevtopic'] = context.pop('prev', None) + context['nexttopic'] = context.pop('next', None) + context['mako_layout'] = self.layout == 'html' and 'static_base.mako' or 'site_base.mako' + # sphinx 1.0b2 doesn't seem to be providing _ for some reason... + context.setdefault('_', lambda x:x) + return self.lookup.get_template(template).render_unicode(**context) + + + def render_string(self, template, context): + context['prevtopic'] = context.pop('prev', None) + context['nexttopic'] = context.pop('next', None) + context['mako_layout'] = self.layout == 'html' and 'static_base.mako' or 'site_base.mako' + # sphinx 1.0b2 doesn't seem to be providing _ for some reason... + context.setdefault('_', lambda x:x) + return Template(template, lookup=self.lookup, + format_exceptions=True, + imports=[ + "from builder import util" + ] + ).render_unicode(**context) + +class StripDocTestFilter(Filter): + def filter(self, lexer, stream): + for ttype, value in stream: + if ttype is Token.Comment and re.match(r'#\s*doctest:', value): + continue + yield ttype, value + + +def autodoc_skip_member(app, what, name, obj, skip, options): + if what == 'class' and skip and name == '__init__': + return False + else: + return skip + +def setup(app): +# app.connect('autodoc-skip-member', autodoc_skip_member) + # Mako is already in Pygments, adding the local + # lexer here so that the latest syntax is available + app.add_lexer('mako', MakoLexer()) + + \ No newline at end of file diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/builder/util.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/builder/util.py Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,12 @@ +import re + +def striptags(text): + return re.compile(r'<[^>]*>').sub('', text) + +def go(m): + # .html with no anchor if present, otherwise "#" for top of page + return m.group(1) or '#' + +def strip_toplevel_anchors(text): + return re.compile(r'(\.html)?#[-\w]+-toplevel').sub(go, text) + diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/caching.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/caching.rst Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,127 @@ +.. _caching_toplevel: + +======== +Caching +======== + +Any template or component can be cached using the ``cache`` +argument to the ``<%page>`` or ``<%def>`` directives: + +.. sourcecode:: mako + + <%page cached="True"/> + + template text + +The above template, after being executed the first time, will +store its content within a cache that by default is scoped +within memory. Subsequent calls to the template's :meth:`~.Template.render` +method will return content directly from the cache. When the +:class:`.Template` object itself falls out of scope, its corresponding +cache is garbage collected along with the template. + +Caching requires that the ``beaker`` package be installed on the +system. + +The caching flag and all its options can be used with the +``<%def>`` tag. + +.. sourcecode:: mako + + <%def name="mycomp" cached="True" cache_timeout="30" cache_type="memory"> + other text + + +Cache arguments +================ + +The various cache arguments are cascaded from their default +values, to the arguments specified programmatically to the +:class:`.Template` or its originating :class:`.TemplateLookup`, then to those +defined in the ``<%page>`` tag of an individual template, and +finally to an individual ``<%def>`` tag within the template. This +means you can define, for example, a cache type of ``dbm`` on your +:class:`.TemplateLookup`, a cache timeout of 60 seconds in a particular +template's ``<%page>`` tag, and within one of that template's +``<%def>`` tags ``cache=True``, and that one particular def will +then cache its data using a ``dbm`` cache and a data timeout of 60 +seconds. + +The options available are: + +* ``cached="False|True"`` - turn caching on +* ``cache_timeout`` - number of seconds in which to invalidate the + cached data. after this timeout, the content is re-generated + on the next call. +* ``cache_type`` - type of caching. ``memory``, ``file``, ``dbm``, or + ``memcached``. +* ``cache_url`` - (only used for ``memcached`` but required) a single + IP address or a semi-colon separated list of IP address of + memcache servers to use. +* ``cache_dir`` - In the case of the ``file`` and ``dbm`` cache types, + this is the filesystem directory with which to store data + files. If this option is not present, the value of + ``module_directory`` is used (i.e. the directory where compiled + template modules are stored). If neither option is available + an exception is thrown. + + In the case of the ``memcached`` type, this attribute is required + and it's used to store the lock files. +* ``cache_key`` - the "key" used to uniquely identify this content + in the cache. the total namespace of keys within the cache is + local to the current template, and the default value of "key" + is the name of the def which is storing its data. It is an + evaluable tag, so you can put a Python expression to calculate + the value of the key on the fly. For example, heres a page + that caches any page which inherits from it, based on the + filename of the calling template: + +.. sourcecode:: mako + + <%page cached="True" cache_key="${self.filename}"/> + + ${next.body()} + + ## rest of template + +Accessing the Cache +=================== + +The :class:`.Template`, as well as any template-derived namespace, has +an accessor called ``cache`` which returns the ``Cache`` object +for that template. This object is a facade on top of the Beaker +internal cache object, and provides some very rudimental +capabilities, such as the ability to get and put arbitrary +values: + +.. sourcecode:: mako + + <% + local.cache.put("somekey", type="memory", "somevalue") + %> + +Above, the cache associated with the ``local`` namespace is +accessed and a key is placed within a memory cache. + +More commonly the ``cache`` object is used to invalidate cached +sections programmatically: + +.. sourcecode:: python + + template = lookup.get_template('/sometemplate.html') + + # invalidate the "body" of the template + template.cache.invalidate_body() + + # invalidate an individual def + template.cache.invalidate_def('somedef') + + # invalidate an arbitrary key + template.cache.invalidate('somekey') + +API Reference +============== + +.. autoclass:: mako.cache.Cache + :members: + :show-inheritance: \ No newline at end of file diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/conf.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/conf.py Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,280 @@ +# -*- coding: utf-8 -*- +# +# Mako documentation build configuration file +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('../..')) +sys.path.insert(0, os.path.abspath('.')) + +import mako + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +#extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', +# 'sphinx.ext.doctest', 'builder.builders'] + +extensions = ['sphinx.ext.autodoc', + 'sphinx.ext.doctest', 'builder.builders'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['templates'] + +nitpicky = True + +# The suffix of source filenames. +source_suffix = '.rst' + +template_bridge = "builder.builders.MakoBridge" + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Mako' +copyright = u'the Mako authors and contributors' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = mako.__version__ +# The full version, including alpha/beta/rc tags. +release = mako.__version__ + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The style sheet to use for HTML and HTML Help pages. A file of that name +# must exist either in Sphinx' static/ path, or in one of the custom paths +# given in html_static_path. +html_style = 'default.css' + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = "%s %s Documentation" % (project, release) + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +html_last_updated_fmt = '%m/%d/%Y %H:%M:%S' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +html_domain_indices = False + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, the reST sources are included in the HTML build as _sources/. +#html_copy_source = True + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Makodoc' + +#autoclass_content = 'both' + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'mako_%s.tex' % release.replace('.', '_'), ur'Mako Documentation', + ur'Mike Bayer', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +# sets TOC depth to 2. +latex_preamble = '\setcounter{tocdepth}{3}' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + +#latex_elements = { +# 'papersize': 'letterpaper', +# 'pointsize': '10pt', +#} + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'mako', u'Mako Documentation', + [u'Mako authors'], 1) +] + + +# -- Options for Epub output --------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'Mako' +epub_author = u'Mako authors' +epub_publisher = u'Mako authors' +epub_copyright = u'Mako authors' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +#epub_exclude_files = [] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/defs.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/defs.rst Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,436 @@ +.. _defs_toplevel: + +==== +Defs +==== + +``<%def>`` is the single tag used to demarcate any block of text +and/or code. It exists within generated Python as a callable +function: + +.. sourcecode:: mako + + <%def name="hello()"> + hello world + + +They are normally called as expressions: + +.. sourcecode:: mako + + the def: ${hello()} + +If the ``<%def>`` is not nested inside of another ``<%def>``, +its known as a **top level def** and can be accessed anywhere in +the template, including above where it was defined. + +All defs, top level or not, have access to the current +contextual namespace in exactly the same way their containing +template does. Suppose the template below is executed with the +variables ``username`` and ``accountdata`` inside the context: + +.. sourcecode:: mako + + Hello there ${username}, how are ya. Lets see what your account says: + + ${account()} + + <%def name="account()"> + Account for ${username}:
+ + % for row in accountdata: + Value: ${row}
+ % endfor + + +The ``username`` and ``accountdata`` variables are present +within the main template body as well as the body of the +``account()`` def. + +Since defs are just Python functions, you can define and pass +arguments to them as well: + +.. sourcecode:: mako + + ${account(accountname='john')} + + <%def name="account(accountname, type='regular')"> + account name: ${accountname}, type ${type} + + +When you declare an argument signature for your def, they are +required to follow normal Python conventions (i.e., all +arguments are required except keyword arguments with a default +value). This is in contrast to using context-level variables, +which evaluate to ``UNDEFINED`` if you reference a name that +does not exist. + +Calling defs from Other Files +============================== + +Top level ``<%defs>`` are **exported** by your template's +module, and can be called from the outside; including from other +templates, as well as normal Python code. Calling a ``<%def>`` +from another template is something like using an ``<%include>`` +- except you are calling a specific function within the +template, not the whole template. + +The remote ``<%def>`` call is also a little bit like calling +functions from other modules in Python. There is an "import" +step to pull the names from another template into your own +template; then the function or functions are available. + +To import another template, use the ``<%namespace>`` tag: + +.. sourcecode:: mako + + <%namespace name="mystuff" file="mystuff.html"/> + +The above tag adds a local variable "mystuff" to the current +scope. + +Then, just call the defs off of ``mystuff``: + +.. sourcecode:: mako + + ${mystuff.somedef(x=5,y=7)} + +The ``<%namespace>`` tag also supports some of the other +semantics of Python's ``import`` statement, including pulling +names into the local variable space, or using ``*`` to represent +all names, using the ``import`` attribute: + +.. sourcecode:: mako + + <%namespace file="mystuff.html" import="foo, bar"/> + +This is just a quick intro to the concept of a **namespace**, +which is a central Mako concept that has its own chapter in +these docs. For more detail and examples, see +:ref:`namespaces_toplevel`. + +Calling defs programmatically +============================== + +You can call def's programmatically from any :class:`.Template` object +using the :meth:`~.Template.get_def()` method, which returns a :class:`.DefTemplate` +object. This is a :class:`.Template` subclass which the parent +:class:`.Template` creates, and is usable like any other template: + +.. sourcecode:: python + + from mako.template import Template + + template = Template(""" + <%def name="hi(name)"> + hi ${name}! + + + <%def name="bye(name)"> + bye ${name}! + + """) + + print template.get_def("hi").render(name="ed") + print template.get_def("bye").render(name="ed") + + +Defs within Defs +================ + +The def model follows regular Python rules for closures. +Declaring ``<%def>`` inside another ``<%def>`` declares it +within the parent's **enclosing scope**: + +.. sourcecode:: mako + + <%def name="mydef()"> + <%def name="subdef()"> + a sub def + + + im the def, and the subcomponent is ${subdef()} + + +Just like Python, names that exist outside the inner ``<%def>`` +exist inside it as well: + +.. sourcecode:: mako + + <% + x = 12 + %> + <%def name="outer()"> + <% + y = 15 + %> + <%def name="inner()"> + inner, x is ${x}, y is ${y} + + + outer, x is ${x}, y is ${y} + + +Assigning to a name inside of a def declares that name as local +to the scope of that def (again, like Python itself). This means +the following code will raise an error: + +.. sourcecode:: mako + + <% + x = 10 + %> + <%def name="somedef()"> + ## error ! + somedef, x is ${x} + <% + x = 27 + %> + + +...because the assignment to ``x`` declares x as local to the +scope of ``somedef``, rendering the "outer" version unreachable +in the expression that tries to render it. + +.. _defs_with_content: + +Calling a def with embedded content and/or other defs +===================================================== + +A flip-side to def within def is a def call with content. This +is where you call a def, and at the same time declare a block of +content (or multiple blocks) that can be used by the def being +called. The main point of such a call is to create custom, +nestable tags, just like any other template language's +custom-tag creation system - where the external tag controls the +execution of the nested tags and can communicate state to them. +Only with Mako, you don't have to use any external Python +modules, you can define arbitrarily nestable tags right in your +templates. + +To achieve this, the target def is invoked using the form +``<%namepacename:defname>`` instead of the normal ``${}`` +syntax. This syntax, introduced in Mako 0.2.3, is functionally +equivalent another tag known as ``call``, which takes the form +``<%call expr='namespacename.defname(args)'>``. While ``%call`` +is available in all versions of Mako, the newer style is +probably more familiar looking. The ``namespace`` portion of the +call is the name of the **namespace** in which the def is +defined - in the most simple cases, this can be ``local`` or +``self`` to reference the current template's namespace (the +difference between ``local`` and ``self`` is one of inheritance +- see :ref:`namespaces_builtin` for details). + +When the target def is invoked, a variable ``caller`` is placed +in its context which contains another namespace containing the +body and other defs defined by the caller. The body itself is +referenced by the method ``body()``. Below, we build a ``%def`` +that operates upon ``caller.body()`` to invoke the body of the +custom tag: + +.. sourcecode:: mako + + <%def name="buildtable()"> + + +
+ ${caller.body()} +
+ + + <%self:buildtable> + I am the table body. + + +This produces the output (whitespace formatted): + +.. sourcecode:: html + + + +
+ I am the table body. +
+ +Using the older ``%call`` syntax looks like: + +.. sourcecode:: mako + + <%def name="buildtable()"> + + +
+ ${caller.body()} +
+ + + <%call expr="buildtable()"> + I am the table body. + + +The ``body()`` can be executed multiple times or not at all. +This means you can use def-call-with-content to build iterators, +conditionals, etc: + +.. sourcecode:: mako + + <%def name="lister(count)"> + % for x in range(count): + ${caller.body()} + % endfor + + + <%self:lister count="${3}"> + hi + + +Produces: + +.. sourcecode:: html + + hi + hi + hi + +Notice above we pass ``3`` as a Python expression, so that it +remains as an integer. + +A custom "conditional" tag: + +.. sourcecode:: mako + + <%def name="conditional(expression)"> + % if expression: + ${caller.body()} + % endif + + + <%self:conditional expression="${4==4}"> + im the result + + +Produces: + +.. sourcecode:: html + + im the result + +But that's not all. The ``body()`` function also can handle +arguments, which will augment the local namespace of the body +callable. The caller must define the arguments which it expects +to receive from its target def using the ``args`` attribute, +which is a comma-separated list of argument names. Below, our +``<%def>`` calls the ``body()`` of its caller, passing in an +element of data from its argument: + +.. sourcecode:: mako + + <%def name="layoutdata(somedata)"> + + % for item in somedata: + + % for col in item: + + % endfor + + % endfor +
${caller.body(col=col)}
+ + + <%self:layoutdata somedata="${[[1,2,3],[4,5,6],[7,8,9]]}" args="col">\ + Body data: ${col}\ + + +Produces: + +.. sourcecode:: html + + + + + + + + + + + + + + + + + +
Body data: 1Body data: 2Body data: 3
Body data: 4Body data: 5Body data: 6
Body data: 7Body data: 8Body data: 9
+ +You don't have to stick to calling just the ``body()`` function. +The caller can define any number of callables, allowing the +``<%call>`` tag to produce whole layouts: + +.. sourcecode:: mako + + <%def name="layout()"> + ## a layout def +
+
+ ${caller.header()} +
+ +
+ ${caller.body()} +
+
+ + + ## calls the layout def + <%self:layout> + <%def name="header()"> + I am the header + + <%def name="sidebar()"> +
    +
  • sidebar 1
  • +
  • sidebar 2
  • +
+ + + this is the body + + +The above layout would produce: + +.. sourcecode:: html + +
+
+ I am the header +
+ + + +
+ this is the body +
+
+ +The number of things you can do with ``<%call>`` and/or the +``<%namespacename:defname>`` calling syntax is enormous. You can +create form widget libraries, such as an enclosing ``
`` +tag and nested HTML input elements, or portable wrapping schemes +using ``
`` or other elements. You can create tags that +interpret rows of data, such as from a database, providing the +individual columns of each row to a ``body()`` callable which +lays out the row any way it wants. Basically anything you'd do +with a "custom tag" or tag library in some other system, Mako +provides via ``<%def>`` tags and plain Python callables which are +invoked via ``<%namespacename:defname>`` or ``<%call>``. + + + diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/filtering.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/filtering.rst Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,340 @@ +.. _filtering_toplevel: + +======================= +Filtering and Buffering +======================= + +Expression Filtering +===================== + +As described in the chapter :ref:`syntax_toplevel`, the "``|``" operator can be +applied to a "``${}``" expression to apply escape filters to the +output: + +.. sourcecode:: mako + + ${"this is some text" | u} + +The above expression applies URL escaping to the expression, and +produces ``this+is+some+text``. + +The built-in escape flags are: + +* ``u`` : URL escaping, provided by + ``urllib.quote_plus(string.encode('utf-8'))`` +* ``h`` : HTML escaping, provided by + ``markupsafe.escape(string)`` (new as of 0.3.4 - prior + versions use ``cgi.escape(string, True)``) +* ``x`` : XML escaping +* ``trim`` : whitespace trimming, provided by ``string.strip()`` +* ``entity`` : produces HTML entity references for applicable + strings, derived from ``htmlentitydefs`` +* ``unicode`` (``str`` on Python 3): produces a Python unicode + string (this function is applied by default). +* ``decode.`` : decode input into a Python + unicode with the specified encoding +* ``n`` : disable all default filtering; only filters specified + in the local expression tag will be applied. + +To apply more than one filter, separate them by a comma: + +.. sourcecode:: mako + + ${" some value " | h,trim} + +The above produces ``<tag>some value</tag>``, with +no leading or trailing whitespace. The HTML escaping function is +applied first, the "trim" function second. + +Naturally, you can make your own filters too. A filter is just a +Python function that accepts a single string argument, and +returns the filtered result. The expressions after the ``|`` +operator draw upon the local namespace of the template in which +they appear, meaning you can define escaping functions locally: + +.. sourcecode:: mako + + <%! + def myescape(text): + return "" + text + "" + %> + + Heres some tagged text: ${"text" | myescape} + +Or from any Python module: + +.. sourcecode:: mako + + <%! + import myfilters + %> + + Heres some tagged text: ${"text" | myfilters.tagfilter} + +A page can apply a default set of filters to all expression tags +using the ``expression_filter`` argument to the ``%page`` tag: + +.. sourcecode:: mako + + <%page expression_filter="h"/> + + Escaped text: ${"some html"} + +Result: + +.. sourcecode:: html + + Escaped text: <html>some html</html> + +.. _filtering_default_filters: + +The default_filters Argument +---------------------------- + +In addition to the ``expression_filter`` argument, the +``default_filters`` argument to both ``Template`` and +``TemplateLookup`` can specify filtering for all expression tags +at the programmatic level. This array-based argument, when given +its default argument of ``None``, will be internally set to +``["unicode"]`` (or ``["str"]`` on Python 3), except when +``disable_unicode=True`` is set in which case it defaults to +``["str"]``: + +.. sourcecode:: python + + t = TemplateLookup(directories=['/tmp'], default_filters=['unicode']) + +To replace the usual ``unicode``/``str`` function with a +specific encoding, the ``decode`` filter can be substituted: + +.. sourcecode:: python + + t = TemplateLookup(directories=['/tmp'], default_filters=['decode.utf8']) + +To disable ``default_filters`` entirely, set it to an empty +list: + +.. sourcecode:: python + + t = TemplateLookup(directories=['/tmp'], default_filters=[]) + +Any string name can be added to ``default_filters`` where it +will be added to all expressions as a filter. The filters are +applied from left to right, meaning the leftmost filter is +applied first. + +.. sourcecode:: python + + t = Template(templatetext, default_filters=['unicode', 'myfilter']) + +To ease the usage of ``default_filters`` with custom filters, +you can also add imports (or other code) to all templates using +the ``imports`` argument: + +.. sourcecode:: python + + t = TemplateLookup(directories=['/tmp'], + default_filters=['unicode', 'myfilter'], + imports=['from mypackage import myfilter']) + +The above will generate templates something like this: + +.. sourcecode:: python + + # .... + from mypackage import myfilter + + def render_body(context): + context.write(myfilter(unicode("some text"))) + +Turning off Filtering with the "n" filter +------------------------------------------ + +In all cases the special ``n`` filter, used locally within an +expression, will **disable** all filters declared in the +``<%page>`` tag as well ``default_filters``. Such as: + +.. sourcecode:: mako + + ${'myexpression' | n} + +Will render ``myexpression`` with no filtering of any kind, and + +.. sourcecode:: mako + + ${'myexpression' | n, trim} + +will render ``myexpression`` using the ``trim`` filter only. + +Filtering Defs +================= + +The ``%def`` tag has a filter argument which will apply the +given list of filter functions to the output of the ``%def``: + +.. sourcecode:: mako + + <%def name="foo()" filter="h, trim"> + this is bold + + +When the filter attribute is applied to a def as above, the def +is automatically **buffered** as well. This is described next. + +Buffering +========== + +One of Mako's central design goals is speed. To this end, all of +the textual content within a template and its various callables +is by default piped directly to the single buffer that is stored +within the ``Context`` object. While this normally is easy to +miss, it has certain side effects. The main one is that when you +call a def using the normal expression syntax, i.e. +``${somedef()}``, it may appear that the return value of the +function is the content it produced, which is then delivered to +your template just like any other expression substitution, +except that normally, this is not the case; the return value of +``${somedef()}`` is simply the empty string ``''``. By the time +you receive this empty string, the output of ``somedef()`` has +been sent to the underlying buffer. + +You may not want this effect, if for example you are doing +something like this: + +.. sourcecode:: mako + + ${" results " + somedef() + " more results "} + +If the ``somedef()`` function produced the content "``somedef's +results``", the above template would produce this output: + +.. sourcecode:: html + + somedef's results results more results + +This is because ``somedef()`` fully executes before the +expression returns the results of its concatenation; the +concatenation in turn receives just the empty string as its +middle expression. + +Mako provides two ways to work around this. One is by applying +buffering to the ``%def`` itself: + +.. sourcecode:: mako + + <%def name="somedef()" buffered="True"> + somedef's results + + +The above definition will generate code similar to this: + +.. sourcecode:: python + + def somedef(): + context.push_buffer() + try: + context.write("somedef's results") + finally: + buf = context.pop_buffer() + return buf.getvalue() + +So that the content of ``somedef()`` is sent to a second buffer, +which is then popped off the stack and its value returned. The +speed hit inherent in buffering the output of a def is also +apparent. + +Note that the ``filter`` argument on %def also causes the def to +be buffered. This is so that the final content of the %def can +be delivered to the escaping function in one batch, which +reduces method calls and also produces more deterministic +behavior for the filtering function itself, which can possibly +be useful for a filtering function that wishes to apply a +transformation to the text as a whole. + +The other way to buffer the output of a def or any Mako callable +is by using the built-in ``capture`` function. This function +performs an operation similar to the above buffering operation +except it is specified by the caller. + +.. sourcecode:: mako + + ${" results " + capture(somedef) + " more results "} + +Note that the first argument to the ``capture`` function is +**the function itself**, not the result of calling it. This is +because the ``capture`` function takes over the job of actually +calling the target function, after setting up a buffered +environment. To send arguments to the function, just send them +to ``capture`` instead: + +.. sourcecode:: mako + + ${capture(somedef, 17, 'hi', use_paging=True)} + +The above call is equivalent to the unbuffered call: + +.. sourcecode:: mako + + ${somedef(17, 'hi', use_paging=True)} + +Decorating +=========== + +This is a feature that's new as of version 0.2.5. Somewhat like +a filter for a %def but more flexible, the ``decorator`` +argument to ``%def`` allows the creation of a function that will +work in a similar manner to a Python decorator. The function can +control whether or not the function executes. The original +intent of this function is to allow the creation of custom cache +logic, but there may be other uses as well. + +``decorator`` is intended to be used with a regular Python +function, such as one defined in a library module. Here we'll +illustrate the python function defined in the template for +simplicities' sake: + +.. sourcecode:: mako + + <%! + def bar(fn): + def decorate(context, *args, **kw): + context.write("BAR") + fn(*args, **kw) + context.write("BAR") + return '' + return decorate + %> + + <%def name="foo()" decorator="bar"> + this is foo + + + ${foo()} + +The above template will return, with more whitespace than this, +``"BAR this is foo BAR"``. The function is the render callable +itself (or possibly a wrapper around it), and by default will +write to the context. To capture its output, use the ``capture`` +callable in the ``mako.runtime`` module (available in templates +as just ``runtime``): + +.. sourcecode:: mako + + <%! + def bar(fn): + def decorate(context, *args, **kw): + return "BAR" + runtime.capture(context, fn, *args, **kw) + "BAR" + return decorate + %> + + <%def name="foo()" decorator="bar"> + this is foo + + + ${foo()} + +The decorator can be used with top-level defs as well as nested +defs. Note that when calling a top-level def from the +``Template`` api, i.e. ``template.get_def('somedef').render()``, +the decorator has to write the output to the ``context``, i.e. +as in the first example. The return value gets discarded. diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/index.rst Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,21 @@ +Table of Contents +================= + +.. toctree:: + :maxdepth: 2 + + usage + syntax + defs + runtime + namespaces + inheritance + filtering + unicode + caching + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`search` diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/inheritance.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/inheritance.rst Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,321 @@ +.. _inheritance_toplevel: + +=========== +Inheritance +=========== + +Using template inheritance, two or more templates can organize +themselves into an **inheritance chain**, where content and +functions from all involved templates can be intermixed. The +general paradigm of template inheritance is this: if a template +``A`` inherits from template ``B``, then template ``A`` agrees +to send the executional control to template ``B`` at runtime +(``A`` is called the **inheriting** template). Template ``B``, +the **inherited** template, then makes decisions as to what +resources from ``A`` shall be executed. + +In practice, it looks like this. Heres a hypothetical inheriting +template, ``index.html``: + +.. sourcecode:: mako + + ## index.html + <%inherit file="base.html"/> + + <%def name="header()"> + this is some header content + + + this is the body content. + +And ``base.html``, the inherited template: + +.. sourcecode:: mako + + ## base.html + + +
+ ${self.header()} +
+ + ${self.body()} + + + + + + <%def name="footer()"> + this is the footer + + +Here is a breakdown of the execution: + +* When ``index.html`` is rendered, control immediately passes to + ``base.html``. +* ``base.html`` then renders the top part of an HTML document, + then calls the method ``header()`` off of a built in namespace + called ``self`` (this namespace was first introduced in the + Namespaces chapter in + :ref:`namespace_self`). Since + ``index.html`` is the topmost template and also defines a def + called ``header()``, its this ``header()`` def that gets + executed. +* Control comes back to ``base.html``. Some more HTML is + rendered. +* ``base.html`` executes ``self.body()``. The ``body()`` + function on all template-based namespaces refers to the main + body of the template, therefore the main body of + ``index.html`` is rendered. +* Control comes back to ``base.html``. More HTML is rendered, + then the ``self.footer()`` expression is invoked. +* The ``footer`` def is only defined in ``base.html``, so being + the topmost definition of ``footer``, its the one that + executes. If ``index.html`` also specified ``footer``, then + its version would **override** that of the base. +* ``base.html`` finishes up rendering its HTML and the template + is complete, producing: + +.. sourcecode:: html + + + +
+ this is some header content +
+ + this is the body content. + + + + + +...and that is template inheritance in a nutshell. The main idea +is that the methods that you call upon ``self`` always +correspond to the topmost definition of that method. Very much +the way ``self`` works in a Python class, even though Mako is +not actually using Python class inheritance to implement this +functionality. (Mako doesn't take the "inheritance" metaphor too +seriously; while useful to setup some commonly recognized +semantics, a textual template is not very much like an +object-oriented class construct in practice). + +Using the "next" namespace to produce content wrapping +======================================================= + +Sometimes you have an inheritance chain that spans more than two +templates. Or maybe you don't, but youd like to build your +system such that extra inherited templates can be inserted in +the middle of a chain where they would be smoothly integrated. +If each template wants to define its layout just within its main +body, you can't just call ``self.body()`` to get at the +inheriting template's body, since that is only the topmost body. +To get at the body of the *next* template, you call upon the +namespace ``next``, which is the namespace of the template +**immediately following** the current template. + +Lets change the line in ``base.html`` which calls upon +``self.body()`` to instead call upon ``next.body()``: + +.. sourcecode:: mako + + ## base.html + + +
+ ${self.header()} +
+ + ${next.body()} + + + + + + <%def name="footer()"> + this is the footer + + +Lets also add an intermediate template called ``layout.html``, +which inherits from ``base.html``: + +.. sourcecode:: mako + + ## layout.html + <%inherit file="base.html"/> +
    + ${self.toolbar()} +
+
+ ${next.body()} +
+ + <%def name="toolbar()"> +
  • selection 1
  • +
  • selection 2
  • +
  • selection 3
  • + + +And finally change ``index.html`` to inherit from +``layout.html`` instead: + +.. sourcecode:: mako + + ## index.html + <%inherit file="layout.html"/> + + ## .. rest of template + +In this setup, each call to ``next.body()`` will render the body +of the next template in the inheritance chain (which can be +written as ``base.html -> layout.html -> index.html``). Control +is still first passed to the bottommost template ``base.html``, +and ``self`` still references the topmost definition of any +particular def. + +The output we get would be: + +.. sourcecode:: html + + + +
    + this is some header content +
    + +
      +
    • selection 1
    • +
    • selection 2
    • +
    • selection 3
    • +
    + +
    + this is the body content. +
    + + + + + +So above, we have the ````, ```` and +``header``/``footer`` layout of ``base.html``, we have the +``
      `` and ``mainlayout`` section of ``layout.html``, and the +main body of ``index.html`` as well as its overridden ``header`` +def. The ``layout.html`` template is inserted into the middle of +the chain without ``base.html`` having to change anything. +Without the ``next`` namespace, only the main body of +``index.html`` could be used; there would be no way to call +``layout.html``'s body content. + +Using the "parent" namespace to augment defs +============================================= + +Lets now look at the other inheritance-specific namespace, the +opposite of ``next`` called ``parent``. ``parent`` is the +namespace of the template **immediately preceding** the current +template. What is most useful about this namespace is the +methods within it which can be accessed within overridden +versions of those methods. This is not as hard as it sounds and +is very much like using the ``super`` keyword in Python. Lets +modify ``index.html`` to augment the list of selections provided +by the ``toolbar`` function in ``layout.html``: + +.. sourcecode:: mako + + ## index.html + <%inherit file="layout.html"/> + + <%def name="header()"> + this is some header content + + + <%def name="toolbar()"> + ## call the parent's toolbar first + ${parent.toolbar()} +
    • selection 4
    • +
    • selection 5
    • + + + this is the body content. + +Above, we implemented a ``toolbar()`` function, which is meant +to override the definition of ``toolbar`` within the inherited +template ``layout.html``. However, since we want the content +from that of ``layout.html`` as well, we call it via the +``parent`` namespace whenever we want it's content, in this case +before we add our own selections. So the output for the whole +thing is now: + +.. sourcecode:: html + + + +
      + this is some header content +
      + +
        +
      • selection 1
      • +
      • selection 2
      • +
      • selection 3
      • +
      • selection 4
      • +
      • selection 5
      • +
      + +
      + this is the body content. +
      + + + + + +and you're now a template inheritance ninja ! + +Inheritable Attributes +====================== + +The ``attr`` accessor of the :class:`.Namespace` object allows access +to module level variables declared in a template. By accessing +``self.attr``, you can access regular attributes from the +inheritance chain as declared in ``<%! %>`` sections. Such as: + +.. sourcecode:: mako + + <%! + class_ = "grey" + %> + +
      + ${self.body()} +
      + +If a an inheriting template overrides ``class_`` to be +``white``, as in: + +.. sourcecode:: mako + + <%! + class_ = "white" + %> + <%inherit file="parent.html"/> + + This is the body + +You'll get output like: + +.. sourcecode:: html + +
      + This is the body +
      diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/namespaces.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/namespaces.rst Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,341 @@ +.. _namespaces_toplevel: + +========== +Namespaces +========== + +Namespaces are used to organize groups of components into +categories, and also to "import" components from other files. + +If the file ``components.html`` defines these two components: + +.. sourcecode:: mako + + ## components.html + <%def name="comp1()"> + this is comp1 + + + <%def name="comp2(x)"> + this is comp2, x is ${x} + + +You can make another file, for example ``index.html``, that +pulls those two components into a namespace called ``comp``: + +.. sourcecode:: mako + + ## index.html + <%namespace name="comp" file="components.html"/> + + Heres comp1: ${comp.comp1()} + Heres comp2: ${comp.comp2(x=5)} + +The ``comp`` variable above is an instance of +:class:`.Namespace`, a **proxy object** which delivers +method calls to the underlying template callable using the +current context. + +``<%namespace>`` also provides an ``import`` attribute which can +be used to pull the names into the local namespace, removing the +need to call it via the ".". When ``import`` is used, the +``name`` attribute is optional. + +.. sourcecode:: mako + + <%namespace file="components.html" import="comp1, comp2"/> + + Heres comp1: ${comp1()} + Heres comp2: ${comp2(x=5)} + +``import`` also supports the "*" operator: + +.. sourcecode:: mako + + <%namespace file="components.html" import="*"/> + + Heres comp1: ${comp1()} + Heres comp2: ${comp2(x=5)} + +The names imported by the ``import`` attribute take precedence +over any names that exist within the current context. + +.. note:: in current versions of Mako, usage of ``import='*'`` is + known to decrease performance of the template. This will be + fixed in a future release. + +The ``file`` argument allows expressions - if looking for +context variables, the ``context`` must be named explicitly: + +.. sourcecode:: mako + + <%namespace name="dyn" file="${context['namespace_name']}"/> + +Ways to Call Namespaces +======================== + +There are essentially four ways to call a function from a +namespace. + +The "expression" format, as described previously. Namespaces are +just Python objects with functions on them, and can be used in +expressions like any other function: + +.. sourcecode:: mako + + ${mynamespace.somefunction('some arg1', 'some arg2', arg3='some arg3', arg4='some arg4')} + +Synonymous with the "expression" format is the "custom tag" +format, when a "closed" tag is used. This format, introduced in +Mako 0.2.3, allows the usage of a "custom" Mako tag, with the +function arguments passed in using named attributes: + +.. sourcecode:: mako + + <%mynamespace:somefunction arg1="some arg1" arg2="some arg2" arg3="some arg3" arg4="some arg4"/> + +When using tags, the values of the arguments are taken as +literal strings by default. To embed Python expressions as +arguments, use the embedded expression format: + +.. sourcecode:: mako + + <%mynamespace:somefunction arg1="${someobject.format()}" arg2="${somedef(5, 12)}"/> + +The "custom tag" format is intended mainly for namespace +functions which recognize body content, which in Mako is known +as a "def with embedded content": + +.. sourcecode:: mako + + <%mynamespace:somefunction arg1="some argument" args="x, y"> + Some record: ${x}, ${y} + + +The "classic" way to call defs with embedded content is the ``<%call>`` tag: + +.. sourcecode:: mako + + <%call expr="mynamespace.somefunction(arg1='some argument')" args="x, y"> + Some record: ${x}, ${y} + + +For information on how to construct defs that embed content from +the caller, see :ref:`defs_with_content`. + +.. _namespaces_python_modules: + +Namespaces from Regular Python Modules +======================================== + +Namespaces can also import regular Python functions from +modules. These callables need to take at least one argument, +``context``, an instance of :class:`.Context`. A module file +``some/module.py`` might contain the callable: + +.. sourcecode:: python + + def my_tag(context): + context.write("hello world") + return '' + +A template can use this module via: + +.. sourcecode:: mako + + <%namespace name="hw" module="some.module"/> + + ${hw.my_tag()} + +Note that the ``context`` argument is not needed in the call; +the :class:`.Namespace` tag creates a locally-scoped callable which +takes care of it. The ``return ''`` is so that the def does not +dump a ``None`` into the output stream - the return value of any +def is rendered after the def completes, in addition to whatever +was passed to :meth:`.Context.write` within its body. + +If your def is to be called in an "embedded content" context, +that is as described in :ref:`defs_with_content`, you should use +the :func:`.supports_caller` decorator, which will ensure that Mako +will ensure the correct "caller" variable is available when your +def is called, supporting embedded content: + +.. sourcecode:: python + + from mako.runtime import supports_caller + + @supports_caller + def my_tag(context): + context.write("
      ") + context['caller'].body() + context.write("
      ") + return '' + +Capturing of output is available as well, using the +outside-of-templates version of the :func:`.capture` function, +which accepts the "context" as its first argument: + +.. sourcecode:: python + + from mako.runtime import supports_caller, capture + + @supports_caller + def my_tag(context): + return "
      %s
      " % \ + capture(context, context['caller'].body, x="foo", y="bar") + +Declaring defs in namespaces +============================= + +The ``<%namespace>`` tag supports the definition of ``<%defs>`` +directly inside the tag. These defs become part of the namespace +like any other function, and will override the definitions +pulled in from a remote template or module: + +.. sourcecode:: mako + + ## define a namespace + <%namespace name="stuff"> + <%def name="comp1()"> + comp1 + + + + ## then call it + ${stuff.comp1()} + +.. _namespaces_body: + +The "body()" method +===================== + +Every namespace that is generated from a template contains a +method called ``body()``. This method corresponds to the main +body of the template, and plays its most important roles when +using inheritance relationships as well as +def-calls-with-content. + +Since the ``body()`` method is available from a namespace just +like all the other defs defined in a template, what happens if +you send arguments to it ? By default, the ``body()`` method +accepts no positional arguments, and for usefulness in +inheritance scenarios will by default dump all keyword arguments +into a dictionary called ``pageargs``. But if you actually want +to get at the keyword arguments, Mako recommends you define your +own argument signature explicitly. You do this via using the +``<%page>`` tag: + +.. sourcecode:: mako + + <%page args="x, y, someval=8, scope='foo', **kwargs"/> + +A template which defines the above signature requires that the +variables ``x`` and ``y`` are defined, defines default values +for ``someval`` and ``scope``, and sets up ``**kwargs`` to +receive all other keyword arguments. If ``**kwargs`` or similar +is not present, the argument ``**pageargs`` gets tacked on by +Mako. When the template is called as a top-level template (i.e. +via :meth:`~.Template.render`) or via the ``<%include>`` tag, the +values for these arguments will be pulled from the ``Context``. +In all other cases, i.e. via calling the ``body()`` method, the +arguments are taken as ordinary arguments from the method call. +So above, the body might be called as: + +.. sourcecode:: mako + + ${self.body(5, y=10, someval=15, delta=7)} + +The :class:`.Context` object also supplies a :attr:`~.Context.kwargs` accessor, for +cases when youd like to pass along whatever is in the context to +a ``body()`` callable: + +.. sourcecode:: mako + + ${next.body(**context.kwargs)} + +The usefulness of calls like the above become more apparent when +one works with inheriting templates. For more information on +this, as well as the meanings of the names ``self`` and +``next``, see :ref:`inheritance_toplevel`. + +.. _namespaces_builtin: + +Built-in Namespaces +==================== + +The namespace is so great that Mako gives your template one (or +two) for free. The names of these namespaces are ``local`` and +``self``. Other built-in namespaces include ``parent`` and +``next``, which are optional and are described in +:ref:`inheritance_toplevel`. + +.. _namespace_local: + +local +----- + +The ``local`` namespace is basically the namespace for the +currently executing template. This means that all of the top +level defs defined in your template, as well as your template's +``body()`` function, are also available off of the ``local`` +namespace. + +The ``local`` namespace is also where properties like ``uri``, +``filename``, and ``module`` and the ``get_namespace`` method +can be particularly useful. + +.. _namespace_self: + +self +----- + +The ``self`` namespace, in the case of a template that does not +use inheritance, is synonomous with ``local``. If inheritance is +used, then ``self`` references the topmost template in the +inheritance chain, where it is most useful for providing the +ultimate form of various "method" calls which may have been +overridden at various points in an inheritance chain. See +:ref:`inheritance_toplevel`. + +Inheritable Namespaces +======================== + +The ``<%namespace>`` tag includes an optional attribute +``inheritable="True"``, which will cause the namespace to be +attached to the ``self`` namespace. Since ``self`` is globally +available throughout an inheritance chain (described in the next +section), all the templates in an inheritance chain can get at +the namespace imported in a super-template via ``self``. + +.. sourcecode:: mako + + ## base.html + <%namespace name="foo" file="foo.html" inheritable="True"/> + + ${next.body()} + + ## somefile.html + <%inherit file="base.html"/> + + ${self.foo.bar()} + +This allows a super-template to load a whole bunch of namespaces +that its inheriting templates can get to, without them having to +explicitly load those namespaces themselves. + +The ``import="*"`` part of the ``<%namespace>`` tag doesn't yet +interact with the ``inheritable`` flag, so currently you have to +use the explicit namespace name off of ``self``, followed by the +desired function name. But more on this in a future release. + +API Reference +============== + +.. autoclass:: mako.runtime.Namespace + :show-inheritance: + :members: + +.. autofunction:: mako.runtime.supports_caller + +.. autofunction:: mako.runtime.capture + diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/runtime.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/runtime.rst Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,238 @@ +.. _runtime_toplevel: + +============================= +The Mako Runtime Environment +============================= + +This section describes a little bit about the objects and +built-in functions that are available in templates. + +Context +======= + +The :class:`.Context` is the central object that is created when +a template is first executed, and is responsible for handling +all communication with the outside world. This includes two +major components, one of which is the output buffer, which is a +file-like object such as Python's ``StringIO`` or similar, and +the other a dictionary of variables that can be freely +referenced within a template; this dictionary is a combination +of the arguments sent to the :meth:`~.Template.render` function and +some built-in variables provided by Mako's runtime environment. + +The Buffer +---------- + +The buffer is stored within the :class:`.Context`, and writing +to it is achieved by calling :meth:`.Context.write`. You usually +don't need to care about this as all text within a template, as +well as all expressions provided by ``${}``, automatically send +everything to this method. The cases you might want to be aware +of its existence are if you are dealing with various +filtering/buffering scenarios, which are described in +:ref:`filtering_toplevel`, or if you want to programmatically +send content to the output stream, such as within a ``<% %>`` +block. + +.. sourcecode:: mako + + <% + context.write("some programmatic text") + %> + +The actual buffer may or may not be the original buffer sent to +the :class:`.Context` object, as various filtering/caching +scenarios may "push" a new buffer onto the context's underlying +buffer stack. For this reason, just stick with +:meth:`.Context.write` and content will always go to the topmost +buffer. + +Context Variables +------------------ + +When your template is compiled into a Python module, the body +content is enclosed within a Python function called +``render_body``. Other top-level defs defined in the template are +defined within their own function bodies which are named after +the def's name with the prefix ``render_`` (i.e. ``render_mydef``). +One of the first things that happens within these functions is +that all variable names that are referenced within the function +which are not defined in some other way (i.e. such as via +assignment, module level imports, etc.) are pulled from the +:class:`.Context` object's dictionary of variables. This is how you're +able to freely reference variable names in a template which +automatically correspond to what was passed into the current +:class:`.Context`. + +* **What happens if I reference a variable name that is not in + the current context?** - the value you get back is a special + value called ``UNDEFINED``, or if the ``strict_undefined=True`` flag + is used a ``NameError`` is raised. ``UNDEFINED`` is just a simple global + variable with the class ``mako.runtime.Undefined``. The + ``UNDEFINED`` object throws an error when you call ``str()`` on + it, which is what happens if you try to use it in an + expression. +* **UNDEFINED makes it hard for me to find what name is missing** - An alternative + introduced in version 0.3.6 is to specify the option + ``strict_undefined=True`` + to the :class:`.Template` or :class:`.TemplateLookup`. This will cause + any non-present variables to raise an immediate ``NameError`` + which includes the name of the variable in its message + when :meth:`~.Template.render` is called - ``UNDEFINED`` is not used. +* **Why not just return None?** Using ``UNDEFINED``, or + raising a ``NameError`` is more + explicit and allows differentiation between a value of ``None`` + that was explicitly passed to the :class:`.Context` and a value that + wasn't present at all. +* **Why raise an exception when you call str() on it ? Why not + just return a blank string?** - Mako tries to stick to the + Python philosophy of "explicit is better than implicit". In + this case, its decided that the template author should be made + to specifically handle a missing value rather than + experiencing what may be a silent failure. Since ``UNDEFINED`` + is a singleton object just like Python's ``True`` or ``False``, + you can use the ``is`` operator to check for it: + + .. sourcecode:: mako + + % if someval is UNDEFINED: + someval is: no value + % else: + someval is: ${someval} + % endif + +Another facet of the :class:`.Context` is that its dictionary of +variables is **immutable**. Whatever is set when +:meth:`~.Template.render` is called is what stays. Of course, since +its Python, you can hack around this and change values in the +context's internal dictionary, but this will probably will not +work as well as you'd think. The reason for this is that Mako in +many cases creates copies of the :class:`.Context` object, which +get sent to various elements of the template and inheriting +templates used in an execution. So changing the value in your +local :class:`.Context` will not necessarily make that value +available in other parts of the template's execution. Examples +of where Mako creates copies of the :class:`.Context` include +within top-level def calls from the main body of the template +(the context is used to propagate locally assigned variables +into the scope of defs; since in the template's body they appear +as inlined functions, Mako tries to make them act that way), and +within an inheritance chain (each template in an inheritance +chain has a different notion of ``parent`` and ``next``, which +are all stored in unique :class:`.Context` instances). + +* **So what if I want to set values that are global to everyone + within a template request?** - All you have to do is provide a + dictionary to your :class:`.Context` when the template first + runs, and everyone can just get/set variables from that. Lets + say its called ``attributes``. + + Running the template looks like: + + .. sourcecode:: python + + output = template.render(attributes={}) + + Within a template, just reference the dictionary: + + .. sourcecode:: mako + + <% + attributes['foo'] = 'bar' + %> + 'foo' attribute is: ${attributes['foo']} + +* **Why can't "attributes" be a built-in feature of the + Context?** - This is an area where Mako is trying to make as + few decisions about your application as it possibly can. + Perhaps you don't want your templates to use this technique of + assigning and sharing data, or perhaps you have a different + notion of the names and kinds of data structures that should + be passed around. Once again Mako would rather ask the user to + be explicit. + +Context Methods and Accessors +------------------------------ + +Significant members off of :class:`.Context` include: + +* ``context[key]`` / ``context.get(key, default=None)`` - + dictionary-like accessors for the context. Normally, any + variable you use in your template is automatically pulled from + the context if it isnt defined somewhere already. Use the + dictionary accessor and/or ``get`` method when you want a + variable that *is* already defined somewhere else, such as in + the local arguments sent to a %def call. If a key is not + present, like a dictionary it raises ``KeyError``. +* ``keys()`` - all the names defined within this context. +* ``kwargs`` - this returns a **copy** of the context's + dictionary of variables. This is useful when you want to + propagate the variables in the current context to a function + as keyword arguments, i.e.: + +.. sourcecode:: mako + + ${next.body(**context.kwargs)} + +* ``write(text)`` - write some text to the current output + stream. +* ``lookup`` - returns the :class:`.TemplateLookup` instance that is + used for all file-lookups within the current execution (even + though individual :class:`.Template` instances can conceivably have + different instances of a :class:`.TemplateLookup`, only the + :class:`.TemplateLookup` of the originally-called :class:`.Template` gets + used in a particular execution). + +All the built-in names +====================== + +A one-stop shop for all the names Mako defines. Most of these +names are instances of :class:`.Namespace`, which are described +in the next section, :ref:`namespaces_toplevel`. Also, most of +these names other than :class:`.Context` and ``UNDEFINED`` are +also present *within* the :class:`.Context` itself. + +* ``local`` - the namespace of the current template, described + in :ref:`namespaces_builtin`. +* ``self`` - the namespace of the topmost template in an + inheritance chain (if any, otherwise the same as ``local``), + mostly described in :ref:`inheritance_toplevel`. +* ``parent`` - the namespace of the parent template in an + inheritance chain (otherwise undefined); see + :ref:`inheritance_toplevel`. +* ``next`` - the namespace of the next template in an + inheritance chain (otherwise undefined); see + :ref:`inheritance_toplevel`. +* ``caller`` - a "mini" namespace created when using the + ``<%call>`` tag to define a "def call with content"; described + in :ref:`defs_with_content`. +* ``capture`` - a function that calls a given def and captures + its resulting content into a string, which is returned. Usage + is described in :ref:`filtering_toplevel`. +* ``UNDEFINED`` - a global singleton that is applied to all + otherwise uninitialized template variables that were not + located within the :class:`.Context` when rendering began, + unless the :class:`.Template` flag ``strict_undefined`` + is set to ``True``. ``UNDEFINED`` is + an instance of :class:`.Undefined`, and raises an + exception when its ``__str__()`` method is called. +* ``pageargs`` - this is a dictionary which is present in a + template which does not define any \**kwargs section in its + ``<%page>`` tag. All keyword arguments sent to the ``body()`` + function of a template (when used via namespaces) go here by + default unless otherwise defined as a page argument. If this + makes no sense, it shouldn't; read the section + :ref:`namespaces_body`. + +API Reference +============== + +.. autoclass:: mako.runtime.Context + :show-inheritance: + :members: + +.. autoclass:: mako.runtime.Undefined + :show-inheritance: + + + diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/static/docs.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/static/docs.css Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,288 @@ +/* documentation section styles */ + +body, td { + font-family: Tahoma,Geneva,sans-serif; +} + +body { + background-color: #FDFBFC; + margin:38px; + color:#333333; +} + +form { + display:inline; +} + +p { + margin-top:10px; + margin-bottom:10px; +} + + +a { + font-weight:normal; + text-decoration:none; +} +a:link {color:#0000FF;} +a:visited {color:#0000FF;} +a:active {color:#0000FF;} +a:hover {color:#700000;} + + +strong a { + font-weight: bold; +} + +#search { + float:right; +} + +#searchform { + padding:20px; +} + +#pagecontrol { + float:right; +} + +.topnav { + background-color: #eeeeee; + border: solid 1px #ccc; + padding:10px; + margin:10px 0px 10px 0px; +} + +.bottomnav { + background-color: #eeeeee; + border:1px solid #CCCCCC; + float:right; + margin: 1em 0 1em 5px; + padding:10px; +} + +.document { + border: solid 1px #ccc; +} + +.topnav .prevnext { + padding: 5px 0px 0px 0px; + /*font-size: 0.8em*/ +} + +h1, h2, h3, h4, h5 { + font-weight:bold; +} + +h1 { + font-size:1.6em; + font-weight:bold; +} +.document h1, .document h2, .document h3, .document h4, .document h5 { + font-size: 1.2em; +} + +.document img { + display:block; + margin: 0 auto; +} + +.document h1 { + display:none; +} + +.topnav h2 { + margin:26px 4px 0px 5px; + font-size:1.6em; + font-weight:normal; + line-height:1.6em; +} + +.topnav h3 { + font-weight: bold; + font-size: 1.4em; + margin:0px; + display:inline; +} + +.topnav li, +li.toctree-l1, +li.toctree-l1 li +{ + list-style-type:disc; + margin:0px; + padding:1px 8px; +} + + +.topnav li ul, +li.toctree-l1 ul +{ + padding:0px 0px 0px 20px; +} + +.topnav li ul li li, +li.toctree-l1 ul li li +{ + /*font-size:.90em;*/ +} + +.sourcelink { + font-size:.8em; + text-align:right; + padding-top:10px; +} + +.section { + line-height: 1.5em; + padding:8px 10px 20px 10px; + margin:10px 0px 0px; +} + +.section .section { + margin:0px 0px 0px 0px; + padding: 0px; +} + +.section .section .section { + margin:0px 0px 0px 20px; +} + +.section .section .section .section { + margin:0px 0px 0px 20px; +} + +th.field-name { + text-align:right; +} + +div.note, div.warning, p.deprecated { + background-color:#EEFFEF; +} + + +div.admonition, div.topic, p.deprecated { + border:1px solid #CCCCCC; + margin:5px 5px 5px 5px; + padding:5px 5px 5px 35px; + font-size:.9em; +} + +div.warning .admonition-title { + color:#FF0000; +} + +div.admonition .admonition-title { + font-weight:bold; +} + + +.totoc { + +} + +.doc_copyright { + font-size:.85em; + padding:10px 0px 10px 0px; +} + +pre { + background-color: #f0f0f0; + border: solid 1px #ccc; + padding:10px; + margin: 5px 5px 5px 5px; + overflow:auto; + line-height:1.3em; +} + +.versionheader { + margin-top: 0.5em; +} +.versionnum { + font-weight: bold; +} + +.viewcode-back, .viewcode-link { + float:right; +} +.prerelease { + border: solid #c25757 2px; + border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + background-color: #c21a1a; + color: white; + padding: 0.05em 0.2em; +} + +dl.function > dt, +dl.attribute > dt, +dl.classmethod > dt, +dl.method > dt, +dl.class > dt, +dl.exception > dt +{ + background-color:#F0F0F0; + margin:0px -10px; + padding: 0px 10px; +} + + +dt:target, span.highlight { + background-color:#FBE54E; +} + +a.headerlink { + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink { + visibility: visible; +} + +a.headerlink:hover { + background-color: #00f; + color: white; +} + +.clearboth { + clear:both; +} + +tt.descname { + background-color:transparent; + font-size:1.2em; + font-weight:bold; +} + +tt.descclassname { + background-color:transparent; +} + +tt { + background-color:#ECF0F3; + padding:0 1px; +} + +@media print { + #nav { display: none; } + #pagecontrol { display: none; } + .topnav .prevnext { display: none; } + .bottomnav { display: none; } + .totoc { display: none; } + .topnav ul li a { text-decoration: none; color: #000; } +} + +/* syntax highlighting overrides */ +.k, .kn {color:#0908CE;} +.o {color:#BF0005;} +.go {color:#804049;} diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/syntax.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/syntax.rst Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,417 @@ +.. _syntax_toplevel: + +====== +Syntax +====== + +A Mako template is parsed from a text stream containing any kind +of content, XML, HTML, email text, etc. The template can further +contain Mako-specific directives which represent variable and/or +expression substitutions, control structures (i.e. conditionals +and loops), server-side comments, full blocks of Python code, as +well as various tags that offer additional functionality. All of +these constructs compile into real Python code. This means that +you can leverage the full power of Python in almost every aspect +of a Mako template. + +Expression Substitution +======================== + +The simplest expression is just a variable substitution. The +syntax for this is the ``${}`` construct, which is inspired by +Perl, Genshi, JSP EL, and others: + +.. sourcecode:: mako + + this is x: ${x} + +Above, the string representation of ``x`` is applied to the +template's output stream. If you're wondering where ``x`` comes +from, its usually from the ``Context`` supplied to the +template's rendering function. If ``x`` was not supplied to the +template and was not otherwise assigned locally, it evaluates to +a special value ``UNDEFINED``. More on that later. + +The contents within the ``${}`` tag are evaluated by Python +directly, so full expressions are OK: + +.. sourcecode:: mako + + pythagorean theorem: ${pow(x,2) + pow(y,2)} + +The results of the expression are evaluated into a string result +in all cases before being rendered to the output stream, such as +the above example where the expression produces a numeric +result. + +Expression Escaping +=================== + + +Mako includes a number of built-in escaping mechanisms, +including HTML, URI and XML escaping, as well as a "trim" +function. These escapes can be added to an expression +substituion using the ``|`` operator: + +.. sourcecode:: mako + + ${"this is some text" | u} + +The above expression applies URL escaping to the expression, and +produces ``this+is+some+text``. The ``u`` name indicates URL +escaping, whereas ``h`` represents HTML escaping, ``x`` +represents XML escaping, and ``trim`` applies a trim function. + +Read more about built in filtering functions, including how to +make your own filter functions, in :ref:`filtering_toplevel`. + +Control Structures +================== + +A control structure refers to all those things that control the +flow of a program - conditionals (i.e. if/else), loops (like +while and for), as well as things like ``try/except``. In Mako, +control structures are written using the ``%`` marker followed +by a regular Python control expression, and are "closed" by +using another ``%`` marker with the tag "``end``", where +"````" is the keyword of the expression: + +.. sourcecode:: mako + + % if x==5: + this is some output + % endif + +The ``%`` can appear anywhere on the line as long as no text +precedes it; indentation is not signficant. The full range of +Python "colon" expressions are allowed here, including +``if/elif/else``, ``while``, ``for``, and even ``def``, although +Mako has a built-in tag for defs which is more full-featured. + +.. sourcecode:: mako + + % for a in ['one', 'two', 'three', 'four', 'five']: + % if a[0] == 't': + its two or three + % elif a[0] == 'f': + four/five + % else: + one + %endif + % endfor + +The ``%`` sign can also be "escaped", if you actually want to +emit a percent sign as the first non whitespace character on a +line, by escaping it as in ``%%``: + +.. sourcecode:: mako + + %% some text + + %% some more text + +Comments +======== + +Comments come in two varieties. The single line comment uses +``##`` as the first non-space characters on a line: + +.. sourcecode:: mako + + ## this is a comment. + ...text ... + +A multiline version exists using ``<%doc> ...text... ``: + +.. sourcecode:: mako + + <%doc> + these are comments + more comments + + +Newline Filters +================ + +The backslash ("``\``") character, placed at the end of any +line, will consume the newline character before continuing to +the next line: + +.. sourcecode:: mako + + here is a line that goes onto \ + another line. + +The above text evaluates to:: + + here is a line that goes onto another line. + +Python Blocks +============= + +Any arbitrary block of python can be dropped in using the ``<% +%>`` tags: + +.. sourcecode:: mako + + this is a template + <% + x = db.get_resource('foo') + y = [z.element for z in x if x.frobnizzle==5] + %> + % for elem in y: + element: ${elem} + % endfor + +Within ``<% %>``, you're writing a regular block of Python code. +While the code can appear with an arbitrary level of preceding +whitespace, it has to be consistently formatted with itself. +Mako's compiler will adjust the block of Python to be consistent +with the surrounding generated Python code. + +Module-level Blocks +==================== + +A variant on ``<% %>`` is the module-level code block, denoted +by ``<%! %>``. Code within these tags is executed at the module +level of the template, and not within the rendering function of +the template. Therefore, this code does not have access to the +template's context and is only executed when the template is +loaded into memory (which can be only once per application, or +more, depending on the runtime environment). Use the ``<%! %>`` +tags to declare your template's imports, as well as any +pure-Python functions you might want to declare: + +.. sourcecode:: mako + + <%! + import mylib + import re + + def filter(text): + return re.sub(r'^@', '', text) + %> + +Any number of ``<%! %>`` blocks can be declared anywhere in a +template; they will be rendered in the resulting module +in a single contiguous block above all render callables, +in the order in which they appear in the source template. + +Tags +==== + +The rest of what Mako offers takes place in the form of tags. +All tags use the same syntax, which is similar to an XML tag +except that the first character of the tag name is a ``%`` +character. The tag is closed either by a contained slash +character, or an explicit closing tag: + +.. sourcecode:: mako + + <%include file="foo.txt"/> + + <%def name="foo" buffered="True"> + this is a def + + +All tags have a set of attributes which are defined for each +tag. Some of these attributes are required. Also, many +attributes support **evaluation**, meaning you can embed an +expression (using ``${}``) inside the attribute text: + +.. sourcecode:: mako + + <%include file="/foo/bar/${myfile}.txt"/> + +Whether or not an attribute accepts runtime evaluation depends +on the type of tag and how that tag is compiled into the +template. The best way to find out if you can stick an +expression in is to try it ! The lexer will tell you if its not +valid. + +Heres a quick summary of all the tags: + +<%page> +------- + +This tag defines general characteristics of the template, +including caching arguments, and optional lists of arguments +which the template expects when invoked. + +.. sourcecode:: mako + + <%page args="x, y, z='default'"/> + +Or a page tag that defines caching characteristics: + +.. sourcecode:: mako + + <%page cached="True" cache_type="memory"/> + +Currently, only one ``<%page>`` tag gets used per template, the +rest get ignored. While this will be improved in a future +release, for now make sure you have only one ``<%page>`` tag +defined in your template, else you may not get the results you +want. The details of what ``<%page>`` is used for are described +further in :ref:`namespaces_body` as well as :ref:`caching_toplevel`. + +<%include> +----------- + +A tag that is familiar from other template languages, %include +is a regular joe that just accepts a file argument and calls in +the rendered result of that file: + +.. sourcecode:: mako + + <%include file="header.html"/> + + hello world + + <%include file="footer.html"/> + +Include also accepts arguments which are available as ``<%page>`` arguments in the receiving template: + +.. sourcecode:: mako + + <%include file="toolbar.html" args="current_section='members', username='ed'"/> + +<%def> +------ + +The ``%def`` tag defines a Python function which contains a set +of content, that can be called at some other point in the +template. The basic idea is simple: + +.. sourcecode:: mako + + <%def name="myfunc(x)"> + this is myfunc, x is ${x} + + + ${myfunc(7)} + +The %def tag is a lot more powerful than a plain Python def, as +the Mako compiler provides many extra services with %def that +you wouldn't normally have, such as the ability to export defs +as template "methods", automatic propagation of the current +``Context``, buffering/filtering/caching flags, and def calls +with content, which enable packages of defs to be sent as +arguments to other def calls (not as hard as it sounds). Get the +full deal on what %def can do in :ref:`defs_toplevel`. + +<%namespace> +------------- + +%namespace is Mako's equivalent of Python's ``import`` +statement. It allows access to all the rendering functions and +metadata of other template files, plain Python modules, as well +as locally defined "packages" of functions. + +.. sourcecode:: mako + + <%namespace file="functions.html" import="*"/> + +The underlying object generated by %namespace, an instance of +:class:`.mako.runtime.Namespace`, is a central construct used in +templates to reference template-specific information such as the +current URI, inheritance structures, and other things that are +not as hard as they sound right here. Namespaces are described +in :ref:`namespaces_toplevel`. + +<%inherit> +---------- + +Inherit allows templates to arrange themselves in **inheritance +chains**. This is a concept familiar in many other template +languages. + +.. sourcecode:: mako + + <%inherit file="base.html"/> + +When using the %inherit tag, control is passed to the topmost +inherited template first, which then decides how to handle +calling areas of content from its inheriting templates. Mako +offers a lot of flexbility in this area, including dynamic +inheritance, content wrapping, and polymorphic method calls. +Check it out in :ref:`inheritance_toplevel`. + +<%namespacename:defname> +------------------------- + +As of Mako 0.2.3, any user-defined "tag" can be created against +a namespace by using a tag with a name of the form +``<%:>``. The closed and open formats of such a +tag are equivalent to an inline expression and the ``<%call>`` +tag, respectively. + +.. sourcecode:: mako + + <%mynamespace:somedef param="some value"> + this is the body + + +To create custom tags which accept a body, see +:ref:`defs_with_content`. + +<%call> +------- + +The call tag is the "classic" form of a user-defined tag, and is +roughly equiavlent to the ``<%namespacename:defname>`` syntax +described above. This tag is also described in `defs_with_content`. + +<%doc> +------ + +The doc tag handles multiline comments: + +.. sourcecode:: mako + + <%doc> + these are comments + more comments + + +Also the ``##`` symbol as the first non-space characters on a line can be used for single line comments. + +<%text> +------- + +This tag suspends the Mako lexer's normal parsing of Mako +template directives, and returns its entire body contents as +plain text. It is used pretty much to write documentation about +Mako: + +.. sourcecode:: mako + + <%text filter="h"> + heres some fake mako ${syntax} + <%def name="x()">${x} + %CLOSETEXT + +Returning early from a template +=============================== + +Sometimes you want to stop processing a template or ``<%def>`` +method in the middle and just use the text you've accumulated so +far. You can use a ````return```` statement inside a Python +block to do that. + +.. sourcecode:: mako + + % if not len(records): + No records found. + <% return %> + % endif + +Or perhaps: + +.. sourcecode:: mako + + <% + if not len(records): + return + %> + diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/templates/genindex.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/templates/genindex.mako Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,72 @@ +<%inherit file="layout.mako"/> + +<%def name="show_title()">${_('Index')} + +

      ${_('Index')}

      + + % for i, (key, dummy) in enumerate(genindexentries): + ${i != 0 and '| ' or ''}${key} + % endfor + +
      + + % for i, (key, entries) in enumerate(genindexentries): +

      ${key}

      +
      +
      + <% + breakat = genindexcounts[i] // 2 + numcols = 1 + numitems = 0 + %> +% for entryname, (links, subitems) in entries: + +
      + % if links: + ${entryname|h} + % for link in links[1:]: + , [${i}] + % endfor + % else: + ${entryname|h} + % endif + + % if subitems: +
      + % for subentryname, subentrylinks in subitems: +
      ${subentryname|h} + % for j, link in enumerate(subentrylinks[1:]): + [${j}] + % endfor +
      + % endfor +
      + % endif + <% + numitems = numitems + 1 + len(subitems) + %> + % if numcols <2 and numitems > breakat: + <% + numcols = numcols + 1 + %> +
      +% endif + +% endfor +
      +% endfor + +<%def name="sidebarrel()"> +% if split_index: +

      ${_('Index')}

      +

      + % for i, (key, dummy) in enumerate(genindexentries): + ${i > 0 and '| ' or ''} + ${key} + % endfor +

      + +

      ${_('Full index on one page')}

      +% endif + ${parent.sidebarrel()} + diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/templates/layout.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/templates/layout.mako Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,130 @@ +## coding: utf-8 +<%inherit file="${context['mako_layout']}"/> + +<%def name="headers()"> + + + + + % for scriptfile in script_files + self.attr.local_script_files: + + % endfor + + % if hasdoc('about'): + + % endif + + + % if hasdoc('copyright'): + + % endif + + % if parents: + + % endif + % if nexttopic: + + % endif + % if prevtopic: + + % endif + ${self.extrahead()} + +<%def name="extrahead()"> + +

      ${docstitle|h}

      + + + +
      + Version: ${release} Last Updated: ${last_updated} +
      +
      + +
      +
      + Index + + % if sourcename: + + % endif +
      + + + % if display_toc and not current_page_name.startswith('index'): + ${toc} + % endif +
      +
      + +
      +
      + ${next.body()} +
      +
      + + <%def name="footer()"> +
      + ${prevnext()} + +
      + + ${self.footer()} + +<%def name="prevnext()"> +
      + % if prevtopic: + Previous: + ${prevtopic['title']} + % endif + % if nexttopic: + Next: + ${nexttopic['title']} + % endif +
      + + +<%def name="show_title()"> +% if title: + ${title} +% endif + + diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/templates/page.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/templates/page.mako Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,2 @@ +<%inherit file="layout.mako"/> +${body| util.strip_toplevel_anchors} \ No newline at end of file diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/templates/search.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/templates/search.mako Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,22 @@ +<%inherit file="layout.mako"/> + +<%! + local_script_files = ['_static/searchtools.js'] +%> +<%def name="show_title()">${_('Search')} + +
      +

      Enter Search Terms:

      + +
      + +
      + +<%def name="footer()"> + ${parent.footer()} + + diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/templates/site_base.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/templates/site_base.mako Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,30 @@ +<%text>#coding:utf-8 +<%inherit file="/root.html"/> +<%page cache_type="file" cached="True"/> +<%! + in_docs=True +%> + + +<%doc> +## TODO: pdf +
      +PDF Download: download +
      + + +${'<%text>'} +${next.body()} +${''} + +<%text><%def name="style()"> + ${self.headers()} + <%text>${parent.style()} + +<%text> + +<%text><%def name="title()">${capture(self.show_title)|util.striptags} — ${docstitle|h}<%text> + +<%! + local_script_files = [] +%> diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/templates/static_base.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/templates/static_base.mako Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,19 @@ + + + + + + ${metatags and metatags or ''} + ${capture(self.show_title)|util.striptags} — ${docstitle|h} + ${self.headers()} + + + ${next.body()} + + + + +<%! + local_script_files = [] +%> diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/unicode.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/unicode.rst Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,337 @@ +.. _unicode_toplevel: + +=================== +The Unicode Chapter +=================== + +The Python language supports two ways of representing what we +know as "strings", i.e. series of characters. In Python 2, the +two types are ``string`` and ``unicode``, and in Python 3 they are +``bytes`` and ``string``. A key aspect of the Python 2 ``string`` and +Python 3 ``bytes`` types are that they contain no information +regarding what **encoding** the data is stored in. For this +reason they were commonly referred to as **byte strings** on +Python 2, and Python 3 makes this name more explicit. The +origins of this come from Python's background of being developed +before the Unicode standard was even available, back when +strings were C-style strings and were just that, a series of +bytes. Strings that had only values below 128 just happened to +be **ascii** strings and were printable on the console, whereas +strings with values above 128 would produce all kinds of +graphical characters and bells. + +Contrast the "bytestring" types with the "unicode/string" type. +Objects of this type are created whenever you say something like +``u"hello world"`` (or in Python 3, just ``"hello world"``). In this +case, Python represents each character in the string internally +using multiple bytes per character (something similar to +UTF-16). Whats important is that when using the +``unicode``/``string`` type to store strings, Python knows the +data's encoding; its in its own internal format. Whereas when +using the ``string``/``bytes`` type, it does not. + +When Python 2 attempts to treat a byte-string as a string, which +means its attempting to compare/parse its characters, to coerce +it into another encoding, or to decode it to a unicode object, +it has to guess what the encoding is. In this case, it will +pretty much always guess the encoding as ``ascii``...and if the +bytestring contains bytes above value 128, you'll get an error. +Python 3 eliminates much of this confusion by just raising an +error unconditionally if a bytestring is used in a +character-aware context. + +There is one operation that Python *can* do with a non-ascii +bytestring, and its a great source of confusion: it can dump the +bytestring straight out to a stream or a file, with nary a care +what the encoding is. To Python, this is pretty much like +dumping any other kind of binary data (like an image) to a +stream somewhere. In Python 2, it is common to see programs that +embed all kinds of international characters and encodings into +plain byte-strings (i.e. using ``"hello world"`` style literals) +can fly right through their run, sending reams of strings out to +whereever they are going, and the programmer, seeing the same +output as was expressed in the input, is now under the illusion +that his or her program is Unicode-compliant. In fact, their +program has no unicode awareness whatsoever, and similarly has +no ability to interact with libraries that *are* unicode aware. +Python 3 makes this much less likely by defaulting to unicode as +the storage format for strings. + +The "pass through encoded data" scheme is what template +languages like Cheetah and earlier versions of Myghty do by +default. Mako as of version 0.2 also supports this mode of +operation when using Python 2, using the "disable_unicode=True" +flag. However, when using Mako in its default mode of +unicode-aware, it requires explicitness when dealing with +non-ascii encodings. Additionally, if you ever need to handle +unicode strings and other kinds of encoding conversions more +intelligently, the usage of raw bytestrings quickly becomes a +nightmare, since you are sending the Python interpreter +collections of bytes for which it can make no intelligent +decisions with regards to encoding. In Python 3 Mako only allows +usage of native, unicode strings. + +In normal Mako operation, all parsed template constructs and +output streams are handled internally as Python ``unicode`` +objects. Its only at the point of :meth:`~.Template.render` that this unicode +stream may be rendered into whatever the desired output encoding +is. The implication here is that the template developer must +ensure that the encoding of all non-ascii templates is explicit +(still required in Python 3), that all non-ascii-encoded +expressions are in one way or another converted to unicode (not +much of a burden in Python 3), and that the output stream of the +template is handled as a unicode stream being encoded to some +encoding (still required in Python 3). + +Specifying the Encoding of a Template File +=========================================== + +This is the most basic encoding-related setting, and it is +equivalent to Python's "magic encoding comment", as described in +`pep-0263 `_. Any +template that contains non-ascii characters requires that this +comment be present so that Mako can decode to unicode (and also +make usage of Python's AST parsing services). Mako's lexer will +use this encoding in order to convert the template source into a +``unicode`` object before continuing its parsing: + +.. sourcecode:: mako + + ## -*- coding: utf-8 -*- + + Alors vous imaginez ma surprise, au lever du jour, quand + une drôle de petite voix m’a réveillé. Elle disait: + « S’il vous plaît… dessine-moi un mouton! » + +For the picky, the regular expression used is derived from that +of the abovementioned pep: + +.. sourcecode:: python + + #.*coding[:=]\s*([-\w.]+).*\n + +The lexer will convert to unicode in all cases, so that if any +characters exist in the template that are outside of the +specified encoding (or the default of ``ascii``), the error will +be immediate. + +As an alternative, the template encoding can be specified +programmatically to either :class:`.Template` or :class:`.TemplateLookup` via +the ``input_encoding`` parameter: + +.. sourcecode:: python + + t = TemplateLookup(directories=['./'], input_encoding='utf-8') + +The above will assume all located templates specify ``utf-8`` +encoding, unless the template itself contains its own magic +encoding comment, which takes precedence. + +Handling Expressions +===================== + +The next area that encoding comes into play is in expression +constructs. By default, Mako's treatment of an expression like +this: + +.. sourcecode:: mako + + ${"hello world"} + +looks something like this: + +.. sourcecode:: python + + context.write(unicode("hello world")) + +In Python 3, its just: + +.. sourcecode:: python + + context.write(str("hello world")) + +That is, **the output of all expressions is run through the +``unicode`` builtin**. This is the default setting, and can be +modified to expect various encodings. The ``unicode`` step serves +both the purpose of rendering non-string expressions into +strings (such as integers or objects which contain ``__str()__`` +methods), and to ensure that the final output stream is +constructed as a unicode object. The main implication of this is +that **any raw bytestrings that contain an encoding other than +ascii must first be decoded to a Python unicode object**. It +means you can't say this in Python 2: + +.. sourcecode:: mako + + ${"voix m’a réveillé."} ## error in Python 2! + +You must instead say this: + +.. sourcecode:: mako + + ${u"voix m’a réveillé."} ## OK ! + +Similarly, if you are reading data from a file that is streaming +bytes, or returning data from some object that is returning a +Python bytestring containing a non-ascii encoding, you have to +explcitly decode to unicode first, such as: + +.. sourcecode:: mako + + ${call_my_object().decode('utf-8')} + +Note that filehandles acquired by ``open()`` in Python 3 default +to returning "text", that is the decoding is done for you. See +Python 3's documentation for the ``open()`` builtin for details on +this. + +If you want a certain encoding applied to *all* expressions, +override the ``unicode`` builtin with the ``decode`` builtin at the +:class:`.Template` or :class:`.TemplateLookup` level: + +.. sourcecode:: python + + t = Template(templatetext, default_filters=['decode.utf8']) + +Note that the built-in ``decode`` object is slower than the +``unicode`` function, since unlike ``unicode`` its not a Python +builtin, and it also checks the type of the incoming data to +determine if string conversion is needed first. + +The ``default_filters`` argument can be used to entirely customize +the filtering process of expressions. This argument is described +in :ref:`filtering_default_filters`. + +Defining Output Encoding +========================= + +Now that we have a template which produces a pure unicode output +stream, all the hard work is done. We can take the output and do +anything with it. + +As stated in the "Usage" chapter, both :class:`.Template` and +:class:`.TemplateLookup` accept ``output_encoding`` and ``encoding_errors`` +parameters which can be used to encode the output in any Python +supported codec: + +.. sourcecode:: python + + from mako.template import Template + from mako.lookup import TemplateLookup + + mylookup = TemplateLookup(directories=['/docs'], output_encoding='utf-8', encoding_errors='replace') + + mytemplate = mylookup.get_template("foo.txt") + print mytemplate.render() + +:meth:`~.Template.render` will return a ``bytes`` object in Python 3 if an output +encoding is specified. By default it performs no encoding and +returns a native string. + +:meth:`~.Template.render_unicode` will return the template output as a Python +``unicode`` object (or ``string`` in Python 3): + +.. sourcecode:: python + + print mytemplate.render_unicode() + +The above method disgards the output encoding keyword argument; +you can encode yourself by saying: + +.. sourcecode:: python + + print mytemplate.render_unicode().encode('utf-8', 'replace') + +Buffer Selection +----------------- + +Mako does play some games with the style of buffering used +internally, to maximize performance. Since the buffer is by far +the most heavily used object in a render operation, its +important! + +When calling :meth:`~.Template.render` on a template that does not specify any +output encoding (i.e. its ``ascii``), Python's ``cStringIO`` module, +which cannot handle encoding of non-ascii ``unicode`` objects +(even though it can send raw bytestrings through), is used for +buffering. Otherwise, a custom Mako class called +``FastEncodingBuffer`` is used, which essentially is a super +dumbed-down version of ``StringIO`` that gathers all strings into +a list and uses ``u''.join(elements)`` to produce the final output +- its markedly faster than ``StringIO``. + +.. _unicode_disabled: + +Saying to Heck with it: Disabling the usage of Unicode entirely +================================================================ + +Some segements of Mako's userbase choose to make no usage of +Unicode whatsoever, and instead would prefer the "passthru" +approach; all string expressions in their templates return +encoded bytestrings, and they would like these strings to pass +right through. The only advantage to this approach is that +templates need not use ``u""`` for literal strings; there's an +arguable speed improvement as well since raw bytestrings +generally perform slightly faster than unicode objects in +Python. For these users, assuming they're sticking with Python +2, they can hit the ``disable_unicode=True`` flag as so: + +.. sourcecode:: python + + # -*- encoding:utf-8 -*- + from mako.template import Template + + t = Template("drôle de petite voix m’a réveillé.", disable_unicode=True, input_encoding='utf-8') + print t.code + +The ``disable_unicode`` mode is strictly a Python 2 thing. It is +not supported at all in Python 3. + +The generated module source code will contain elements like +these: + +.. sourcecode:: python + + # -*- encoding:utf-8 -*- + # ...more generated code ... + + def render_body(context,**pageargs): + context.caller_stack.push_frame() + try: + __M_locals = dict(pageargs=pageargs) + # SOURCE LINE 1 + context.write('dr\xc3\xb4le de petite voix m\xe2\x80\x99a r\xc3\xa9veill\xc3\xa9.') + return '' + finally: + context.caller_stack.pop_frame() + +Where above that the string literal used within :meth:`.Context.write` +is a regular bytestring. + +When ``disable_unicode=True`` is turned on, the ``default_filters`` +argument which normally defaults to ``["unicode"]`` now defaults +to ``["str"]`` instead. Setting default_filters to the empty list +``[]`` can remove the overhead of the ``str`` call. Also, in this +mode you **cannot** safely call :meth:`~.Template.render_unicode` - you'll get +unicode/decode errors. + +The ``h`` filter (html escape) uses a less performant pure Python +escape function in non-unicode mode (note that in versions prior +to 0.3.4, it used cgi.escape(), which has been replaced with a +function that also escapes single quotes). This because +MarkupSafe only supports Python unicode objects for non-ascii +strings. + +**Rules for using disable_unicode=True** + +* don't use this mode unless you really, really want to and you + absolutely understand what you're doing +* don't use this option just because you don't want to learn to + use Unicode properly; we aren't supporting user issues in this + mode of operation. We will however offer generous help for the + vast majority of users who stick to the Unicode program. +* Python 3 is unicode by default, and the flag is not available + when running on Python 3. + + + diff -r 92088733aade -r 395af41fe34e lib/mako/doc/build/usage.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/doc/build/usage.rst Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,484 @@ +.. _usage_toplevel: + +===== +Usage +===== + +Basic Usage +=========== + +This section describes the Python API for Mako templates. If you +are using Mako within a web framework such as Pylons, the work +of integrating Mako's API is already done for you, in which case +you can skip to the next section, :ref:`syntax_toplevel`. + +The most basic way to create a template and render it is through +the :class:`.Template` class:: + + from mako.template import Template + + mytemplate = Template("hello world!") + print mytemplate.render() + +Above, the text argument to :class:`.Template` is **compiled** into a +Python module representation. This module contains a function +called ``render_body()``, which produces the output of the +template. When ``mytemplate.render()`` is called, Mako sets up a +runtime environment for the template and calls the +``render_body()`` function, capturing the output into a buffer and +returning its string contents. + + +The code inside the ``render_body()`` function has access to a +namespace of variables. You can specify these variables by +sending them as additional keyword arguments to the :meth:`~.Template.render` +method:: + + from mako.template import Template + + mytemplate = Template("hello, ${name}!") + print mytemplate.render(name="jack") + +The :meth:`~.Template.render` method calls upon Mako to create a +:class:`.Context` object, which stores all the variable names accessible +to the template and also stores a buffer used to capture output. +You can create this :class:`.Context` yourself and have the template +render with it, using the :meth:`~.Template.render_context` method:: + + from mako.template import Template + from mako.runtime import Context + from StringIO import StringIO + + mytemplate = Template("hello, ${name}!") + buf = StringIO() + ctx = Context(buf, name="jack") + mytemplate.render_context(ctx) + print buf.getvalue() + +Using File-Based Templates +=========================== + +A :class:`.Template` can also load its template source code from a file, +using the ``filename`` keyword argument:: + + from mako.template import Template + + mytemplate = Template(filename='/docs/mytmpl.txt') + print mytemplate.render() + +For improved performance, a :class:`.Template` which is loaded from a +file can also cache the source code to its generated module on +the filesystem as a regular Python module file (i.e. a .py +file). To do this, just add the ``module_directory`` argument to +the template:: + + from mako.template import Template + + mytemplate = Template(filename='/docs/mytmpl.txt', module_directory='/tmp/mako_modules') + print mytemplate.render() + +When the above code is rendered, a file +``/tmp/mako_modules/docs/mytmpl.txt.py`` is created containing the +source code for the module. The next time a :class:`.Template` with the +same arguments is created, this module file will be +automatically re-used. + +.. _usage_templatelookup: + +Using TemplateLookup +==================== + +All of the examples thus far have dealt with the usage of a +single :class:`.Template` object. If the code within those templates +tries to locate another template resource, it will need some way +to find them, using simple URI strings. For this need, the +resolution of other templates from within a template is +accomplished by the :class:`.TemplateLookup` class. This class is +constructed given a list of directories in which to search for +templates, as well as keyword arguments that will be passed to +the :class:`.Template` objects it creates:: + + from mako.template import Template + from mako.lookup import TemplateLookup + + mylookup = TemplateLookup(directories=['/docs']) + mytemplate = Template("""<%include file="header.txt"/> hello world!""", lookup=mylookup) + +Above, we created a textual template which includes the file +``"header.txt"``. In order for it to have somewhere to look for +``"header.txt"``, we passed a :class:`.TemplateLookup` object to it, which +will search in the directory ``/docs`` for the file ``"header.txt"``. + +Usually, an application will store most or all of its templates +as text files on the filesystem. So far, all of our examples +have been a little bit contrived in order to illustrate the +basic concepts. But a real application would get most or all of +its templates directly from the :class:`.TemplateLookup`, using the +aptly named :meth:`~.TemplateLookup.get_template` method, which accepts the URI of the +desired template:: + + from mako.template import Template + from mako.lookup import TemplateLookup + + mylookup = TemplateLookup(directories=['/docs'], module_directory='/tmp/mako_modules') + + def serve_template(templatename, **kwargs): + mytemplate = mylookup.get_template(templatename) + print mytemplate.render(**kwargs) + +In the example above, we create a :class:`.TemplateLookup` which will +look for templates in the ``/docs`` directory, and will store +generated module files in the ``/tmp/mako_modules`` directory. The +lookup locates templates by appending the given URI to each of +its search directories; so if you gave it a URI of +``/etc/beans/info.txt``, it would search for the file +``/docs/etc/beans/info.txt``, else raise a :class:`.TopLevelNotFound` +exception, which is a custom Mako exception. + +When the lookup locates templates, it will also assign a ``uri`` +property to the :class:`.Template` which is the uri passed to the +:meth:`~.TemplateLookup.get_template()` call. :class:`.Template` uses this uri to calculate the +name of its module file. So in the above example, a +``templatename`` argument of ``/etc/beans/info.txt`` will create a +module file ``/tmp/mako_modules/etc/beans/info.txt.py``. + +Setting the Collection Size +--------------------------- + +The :class:`.TemplateLookup` also serves the important need of caching a +fixed set of templates in memory at a given time, so that +successive uri lookups do not result in full template +compilations and/or module reloads on each request. By default, +the :class:`.TemplateLookup` size is unbounded. You can specify a fixed +size using the ``collection_size`` argument:: + + mylookup = TemplateLookup(directories=['/docs'], + module_directory='/tmp/mako_modules', collection_size=500) + +The above lookup will continue to load templates into memory +until it reaches a count of around 500. At that point, it will +clean out a certain percentage of templates using a least +recently used scheme. + +Setting Filesystem Checks +-------------------------- + +Another important flag on :class:`.TemplateLookup` is +``filesystem_checks``. This defaults to ``True``, and says that each +time a template is returned by the :meth:`~.TemplateLookup.get_template()` method, the +revision time of the original template file is checked against +the last time the template was loaded, and if the file is newer +will reload its contents and recompile the template. On a +production system, setting ``filesystem_checks`` to ``False`` can +afford a small to moderate performance increase (depending on +the type of filesystem used). + +.. _usage_unicode: + +Using Unicode and Encoding +=========================== + +Both :class:`.Template` and :class:`.TemplateLookup` accept ``output_encoding`` +and ``encoding_errors`` parameters which can be used to encode the +output in any Python supported codec:: + + from mako.template import Template + from mako.lookup import TemplateLookup + + mylookup = TemplateLookup(directories=['/docs'], output_encoding='utf-8', encoding_errors='replace') + + mytemplate = mylookup.get_template("foo.txt") + print mytemplate.render() + +When using Python 3, the :meth:`~.Template.render` method will return a ``bytes`` +object, **if** ``output_encoding`` is set. Otherwise it returns a +``string``. + +Additionally, the :meth:`~.Template.render_unicode()` method exists which will +return the template output as a Python ``unicode`` object, or in +Python 3 a ``string``:: + + print mytemplate.render_unicode() + +The above method disregards the output encoding keyword +argument; you can encode yourself by saying:: + + print mytemplate.render_unicode().encode('utf-8', 'replace') + +Note that Mako's ability to return data in any encoding and/or +``unicode`` implies that the underlying output stream of the +template is a Python unicode object. This behavior is described +fully in :ref:`unicode_toplevel`. + +.. _handling_exceptions: + +Handling Exceptions +==================== + +Template exceptions can occur in two distinct places. One is +when you **lookup, parse and compile** the template, the other +is when you **run** the template. Within the running of a +template, exceptions are thrown normally from whatever Python +code originated the issue. Mako has its own set of exception +classes which mostly apply to the lookup and lexer/compiler +stages of template construction. Mako provides some library +routines that can be used to help provide Mako-specific +information about any exception's stack trace, as well as +formatting the exception within textual or HTML format. In all +cases, the main value of these handlers is that of converting +Python filenames, line numbers, and code samples into Mako +template filenames, line numbers, and code samples. All lines +within a stack trace which correspond to a Mako template module +will be converted to be against the originating template file. + +To format exception traces, the :func:`.text_error_template` and +:func:`.html_error_template` functions are provided. They make usage of +``sys.exc_info()`` to get at the most recently thrown exception. +Usage of these handlers usually looks like:: + + from mako import exceptions + + try: + template = lookup.get_template(uri) + print template.render() + except: + print exceptions.text_error_template().render() + +Or for the HTML render function:: + + from mako import exceptions + + try: + template = lookup.get_template(uri) + print template.render() + except: + print exceptions.html_error_template().render() + +The :func:`.html_error_template` template accepts two options: +specifying ``full=False`` causes only a section of an HTML +document to be rendered. Specifying ``css=False`` will disable the +default stylesheet from being rendered. + +E.g.:: + + print exceptions.html_error_template().render(full=False) + +The HTML render function is also available built-in to +:class:`.Template` using the ``format_exceptions`` flag. In this case, any +exceptions raised within the **render** stage of the template +will result in the output being substituted with the output of +:func:`.html_error_template`:: + + template = Template(filename="/foo/bar", format_exceptions=True) + print template.render() + +Note that the compile stage of the above template occurs when +you construct the :class:`.Template` itself, and no output stream is +defined. Therefore exceptions which occur within the +lookup/parse/compile stage will not be handled and will +propagate normally. While the pre-render traceback usually will +not include any Mako-specific lines anyway, it will mean that +exceptions which occur previous to rendering and those which +occur within rendering will be handled differently...so the +try/except patterns described previously are probably of more +general use. + +The underlying object used by the error template functions is +the :class:`.RichTraceback` object. This object can also be used +directly to provide custom error views. Here's an example usage +which describes its general API:: + + from mako.exceptions import RichTraceback + + try: + template = lookup.get_template(uri) + print template.render() + except: + traceback = RichTraceback() + for (filename, lineno, function, line) in traceback.traceback: + print "File %s, line %s, in %s" % (filename, lineno, function) + print line, "\n" + print "%s: %s" % (str(traceback.error.__class__.__name__), traceback.error) + +Common Framework Integrations +============================= + +The Mako distribution includes a little bit of helper code for +the purpose of using Mako in some popular web framework +scenarios. This is a brief description of whats included. + +WSGI +---- + +A sample WSGI application is included in the distrubution in the +file ``examples/wsgi/run_wsgi.py``. This runner is set up to pull +files from a `templates` as well as an `htdocs` directory and +includes a rudimental two-file layout. The WSGI runner acts as a +fully functional standalone web server, using ``wsgiutils`` to run +itself, and propagates GET and POST arguments from the request +into the :class:`.Context`, can serve images, css files and other kinds +of files, and also displays errors using Mako's included +exception-handling utilities. + +Pygments +--------- + +A `Pygments `_-compatible syntax +highlighting module is included under :mod:`mako.ext.pygmentplugin`. +This module is used in the generation of Mako documentation and +also contains various setuptools entry points under the heading +``pygments.lexers``, including ``mako``, ``html+mako``, ``xml+mako`` +(see the ``setup.py`` file for all the entry points). + +Babel +------ + +Mako provides support for extracting gettext messages from +templates via a `Babel`_ extractor +entry point under `mako.ext.babelplugin`. + +Gettext messages are extracted from all Python code sections, +including those of control lines and expressions embedded +in tags. + +`Translator +comments `_ +may also be extracted from Mako templates when a comment tag is +specified to `Babel`_ (such as with +the -c option). + +For example, a project ``"myproj"`` contains the following Mako +template at ``myproj/myproj/templates/name.html``: + +.. sourcecode:: mako + +
      + Name: + ## TRANSLATORS: This is a proper name. See the gettext + ## manual, section Names. + ${_('Francois Pinard')} +
      + +To extract gettext messages from this template the project needs +a Mako section in its `Babel Extraction Method Mapping +file `_ +(typically located at ``myproj/babel.cfg``):: + + # Extraction from Python source files + + [python: myproj/**.py] + + # Extraction from Mako templates + + [mako: myproj/templates/**.html] + input_encoding = utf-8 + +The Mako extractor supports an optional ``input_encoding`` +parameter specifying the encoding of the templates (identical to +:class:`.Template`/:class:`.TemplateLookup`'s ``input_encoding`` parameter). + +Invoking `Babel`_'s extractor at the +command line in the project's root directory:: + + myproj$ pybabel extract -F babel.cfg -c "TRANSLATORS:" . + +Will output a gettext catalog to stdout including the following:: + + #. TRANSLATORS: This is a proper name. See the gettext + #. manual, section Names. + #: myproj/templates/name.html:5 + msgid "Francois Pinard" + msgstr "" + +This is only a basic example: +`Babel`_ can be invoked from setup.py +and its command line options specified in the accompanying +setup.cfg via `Babel Distutils/Setuptools +Integration `_. + +Comments must immediately precede a gettext message to be +extracted. In the following case the TRANSLATORS: comment would +not have been extracted: + +.. sourcecode:: mako + +
      + ## TRANSLATORS: This is a proper name. See the gettext + ## manual, section Names. + Name: ${_('Francois Pinard')} +
      + +See the `Babel User +Guide `_ +for more information. + +.. _babel: http://babel.edgewall.org/ + + +API Reference +================= + +.. autoclass:: mako.template.Template + :show-inheritance: + :members: + +.. autoclass:: mako.template.DefTemplate + :show-inheritance: + :members: + +.. autoclass:: mako.lookup.TemplateCollection + :show-inheritance: + :members: + +.. autoclass:: mako.lookup.TemplateLookup + :show-inheritance: + :members: + +.. autoclass:: mako.exceptions.RichTraceback + :show-inheritance: + + .. py:attribute:: error + + the exception instance. + + .. py:attribute:: message + + the exception error message as unicode + + .. py:attribute:: source + + source code of the file where the error occured. + if the error occured within a compiled template, + this is the template source. + + .. py:attribute:: lineno + + line number where the error occured. if the error + occured within a compiled template, the line number + is adjusted to that of the template source + + .. py:attribute:: records + + a list of 8-tuples containing the original + python traceback elements, plus the + filename, line number, source line, and full template source + for the traceline mapped back to its originating source + template, if any for that traceline (else the fields are None). + + .. py:attribute:: reverse_records + + the list of records in reverse + traceback - a list of 4-tuples, in the same format as a regular + python traceback, with template-corresponding + traceback records replacing the originals + + .. py:attribute:: reverse_traceback + + the traceback list in reverse + +.. autofunction:: mako.exceptions.html_error_template + +.. autofunction:: mako.exceptions.text_error_template + + + diff -r 92088733aade -r 395af41fe34e lib/mako/examples/bench/basic.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/examples/bench/basic.py Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,161 @@ +# basic.py - basic benchmarks adapted from Genshi +# Copyright (C) 2006 Edgewall Software +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. The name of the author may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from cgi import escape +import os +from StringIO import StringIO +import sys +import timeit + +__all__ = ['mako', 'mako_inheritance', 'cheetah', 'django', 'myghty', 'genshi', 'kid'] + +def genshi(dirname, verbose=False): + from genshi.template import TemplateLoader + loader = TemplateLoader([dirname], auto_reload=False) + template = loader.load('template.html') + def render(): + data = dict(title='Just a test', user='joe', + items=['Number %d' % num for num in range(1, 15)]) + return template.generate(**data).render('xhtml') + + if verbose: + print render() + return render + +def myghty(dirname, verbose=False): + from myghty import interp + interpreter = interp.Interpreter(component_root=dirname) + def render(): + data = dict(title='Just a test', user='joe', + items=['Number %d' % num for num in range(1, 15)]) + buffer = StringIO() + interpreter.execute("template.myt", request_args=data, out_buffer=buffer) + return buffer.getvalue() + if verbose: + print render() + return render + +def mako(dirname, verbose=False): + from mako.template import Template + from mako.lookup import TemplateLookup + lookup = TemplateLookup(directories=[dirname], filesystem_checks=False, disable_unicode=True) + template = lookup.get_template('template.html') + def render(): + return template.render(title="Just a test", user="joe", list_items=[u'Number %d' % num for num in range(1,15)]) + if verbose: + print template.code, render() + return render +mako_inheritance = mako + +def cheetah(dirname, verbose=False): + from Cheetah.Template import Template + filename = os.path.join(dirname, 'template.tmpl') + template = Template(file=filename) + def render(): + template.__dict__.update({'title': 'Just a test', 'user': 'joe', + 'list_items': [u'Number %d' % num for num in range(1, 15)]}) + return template.respond() + + if verbose: + print dir(template) + print template.generatedModuleCode() + print render() + return render + +def django(dirname, verbose=False): + from django.conf import settings + settings.configure(TEMPLATE_DIRS=[os.path.join(dirname, 'templates')]) + from django import template, templatetags + from django.template import loader + templatetags.__path__.append(os.path.join(dirname, 'templatetags')) + tmpl = loader.get_template('template.html') + + def render(): + data = {'title': 'Just a test', 'user': 'joe', + 'items': ['Number %d' % num for num in range(1, 15)]} + return tmpl.render(template.Context(data)) + + if verbose: + print render() + return render + +def kid(dirname, verbose=False): + import kid + kid.path = kid.TemplatePath([dirname]) + template = kid.Template(file='template.kid') + def render(): + template = kid.Template(file='template.kid', + title='Just a test', user='joe', + items=['Number %d' % num for num in range(1, 15)]) + return template.serialize(output='xhtml') + + if verbose: + print render() + return render + + +def run(engines, number=2000, verbose=False): + basepath = os.path.abspath(os.path.dirname(__file__)) + for engine in engines: + dirname = os.path.join(basepath, engine) + if verbose: + print '%s:' % engine.capitalize() + print '--------------------------------------------------------' + else: + print '%s:' % engine.capitalize(), + t = timeit.Timer(setup='from __main__ import %s; render = %s(r"%s", %s)' + % (engine, engine, dirname, verbose), + stmt='render()') + + time = t.timeit(number=number) / number + if verbose: + print '--------------------------------------------------------' + print '%.2f ms' % (1000 * time) + if verbose: + print '--------------------------------------------------------' + + +if __name__ == '__main__': + engines = [arg for arg in sys.argv[1:] if arg[0] != '-'] + if not engines: + engines = __all__ + + verbose = '-v' in sys.argv + + if '-p' in sys.argv: + import hotshot, hotshot.stats + prof = hotshot.Profile("template.prof") + benchtime = prof.runcall(run, engines, number=100, verbose=verbose) + stats = hotshot.stats.load("template.prof") + stats.strip_dirs() + stats.sort_stats('time', 'calls') + stats.print_stats() + else: + run(engines, verbose=verbose) diff -r 92088733aade -r 395af41fe34e lib/mako/examples/bench/basic.py.orig --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/examples/bench/basic.py.orig Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,161 @@ +# basic.py - basic benchmarks adapted from Genshi +# Copyright (C) 2006 Edgewall Software +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. The name of the author may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from cgi import escape +import os +from StringIO import StringIO +import sys +import timeit + +__all__ = ['mako', 'mako_inheritance', 'cheetah', 'django', 'myghty', 'genshi', 'kid'] + +def genshi(dirname, verbose=False): + from genshi.template import TemplateLoader + loader = TemplateLoader([dirname], auto_reload=False) + template = loader.load('template.html') + def render(): + data = dict(title='Just a test', user='joe', + items=['Number %d' % num for num in range(1, 15)]) + return template.generate(**data).render('xhtml') + + if verbose: + print render() + return render + +def myghty(dirname, verbose=False): + from myghty import interp + interpreter = interp.Interpreter(component_root=dirname) + def render(): + data = dict(title='Just a test', user='joe', + items=['Number %d' % num for num in range(1, 15)]) + buffer = StringIO() + interpreter.execute("template.myt", request_args=data, out_buffer=buffer) + return buffer.getvalue() + if verbose: + print render() + return render + +def mako(dirname, verbose=False): + from mako.template import Template + from mako.lookup import TemplateLookup + lookup = TemplateLookup(directories=[dirname], filesystem_checks=False, disable_unicode=True) + template = lookup.get_template('template.html') + def render(): + return template.render(title="Just a test", user="joe", list_items=[u'Number %d' % num for num in range(1,15)]) + if verbose: + print template.code, render() + return render +mako_inheritance = mako + +def cheetah(dirname, verbose=False): + from Cheetah.Template import Template + filename = os.path.join(dirname, 'template.tmpl') + template = Template(file=filename) + def render(): + template.__dict__.update({'title': 'Just a test', 'user': 'joe', + 'list_items': [u'Number %d' % num for num in range(1, 15)]}) + return template.respond() + + if verbose: + print dir(template) + print template.generatedModuleCode() + print render() + return render + +def django(dirname, verbose=False): + from django.conf import settings + settings.configure(TEMPLATE_DIRS=[os.path.join(dirname, 'templates')]) + from django import template, templatetags + from django.template import loader + templatetags.__path__.append(os.path.join(dirname, 'templatetags')) + tmpl = loader.get_template('template.html') + + def render(): + data = {'title': 'Just a test', 'user': 'joe', + 'items': ['Number %d' % num for num in range(1, 15)]} + return tmpl.render(template.Context(data)) + + if verbose: + print render() + return render + +def kid(dirname, verbose=False): + import kid + kid.path = kid.TemplatePath([dirname]) + template = kid.Template(file='template.kid') + def render(): + template = kid.Template(file='template.kid', + title='Just a test', user='joe', + items=['Number %d' % num for num in range(1, 15)]) + return template.serialize(output='xhtml') + + if verbose: + print render() + return render + + +def run(engines, number=2000, verbose=False): + basepath = os.path.abspath(os.path.dirname(__file__)) + for engine in engines: + dirname = os.path.join(basepath, engine) + if verbose: + print '%s:' % engine.capitalize() + print '--------------------------------------------------------' + else: + print '%s:' % engine.capitalize(), + t = timeit.Timer(setup='from __main__ import %s; render = %s(r"%s", %s)' + % (engine, engine, dirname, verbose), + stmt='render()') + + time = t.timeit(number=number) / number + if verbose: + print '--------------------------------------------------------' + print '%.2f ms' % (1000 * time) + if verbose: + print '--------------------------------------------------------' + + +if __name__ == '__main__': + engines = [arg for arg in sys.argv[1:] if arg[0] != '-'] + if not engines: + engines = __all__ + + verbose = '-v' in sys.argv + + if '-p' in sys.argv: + import hotshot, hotshot.stats + prof = hotshot.Profile("template.prof") + benchtime = prof.runcall(run, engines, number=100, verbose=verbose) + stats = hotshot.stats.load("template.prof") + stats.strip_dirs() + stats.sort_stats('time', 'calls') + stats.print_stats() + else: + run(engines, verbose=verbose) diff -r 92088733aade -r 395af41fe34e lib/mako/examples/bench/cheetah/footer.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/examples/bench/cheetah/footer.tmpl Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,2 @@ + diff -r 92088733aade -r 395af41fe34e lib/mako/examples/bench/cheetah/header.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/examples/bench/cheetah/header.tmpl Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,5 @@ + + + diff -r 92088733aade -r 395af41fe34e lib/mako/examples/bench/cheetah/template.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/examples/bench/cheetah/template.tmpl Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,31 @@ + + + + ${title} + + + + #def greeting(name) +

      hello ${name}!

      + #end def + + #include "cheetah/header.tmpl" + + $greeting($user) + $greeting('me') + $greeting('world') + +

      Loop

      + #if $list_items +
        + #for $list_item in $list_items +
      • $list_item
      • + #end for +
      + #end if + + #include "cheetah/footer.tmpl" + + diff -r 92088733aade -r 395af41fe34e lib/mako/examples/bench/django/templates/base.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/examples/bench/django/templates/base.html Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,14 @@ + + + + {% block body %} + + {{ block.super }} + + {% endblock %} + + diff -r 92088733aade -r 395af41fe34e lib/mako/examples/bench/django/templates/template.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/examples/bench/django/templates/template.html Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% load bench %} + + + ${title|escape} + + +{% block body %} +
      {% greeting user %}
      +
      {% greeting "me" %}
      +
      {% greeting "world" %}
      + +

      Loop

      + {% if items %} +
        + {% for item in items %} + {{ item }} + {% endfor %} +
      + {% endif %} + +{% endblock %} diff -r 92088733aade -r 395af41fe34e lib/mako/examples/bench/django/templatetags/bench.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/examples/bench/django/templatetags/bench.py Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,8 @@ +from django.template import Library, Node, resolve_variable +from django.utils.html import escape + +register = Library() + +def greeting(name): + return 'Hello, %s!' % escape(name) +greeting = register.simple_tag(greeting) diff -r 92088733aade -r 395af41fe34e lib/mako/examples/bench/genshi/base.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/mako/examples/bench/genshi/base.html Wed Nov 17 11:18:39 2010 -0800 @@ -0,0 +1,17 @@ + + +

      + Hello, ${name}! +

      + + + + ${select('*')} +