diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-07-07 20:52:31 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-07-07 20:52:31 -0400 |
commit | 0f58ab39bf6753492e565a85f9f436d476a550b1 (patch) | |
tree | 241293f5643da3d12148af15af3c81a79ea8c59e | |
parent | 936efb91e23058a0450eea823489514f4d422a51 (diff) | |
download | mako-0f58ab39bf6753492e565a85f9f436d476a550b1.tar.gz |
epic trailing whitespace removal
-rw-r--r-- | CHANGES | 310 | ||||
-rw-r--r-- | mako/ast.py | 42 | ||||
-rw-r--r-- | mako/cache.py | 24 | ||||
-rw-r--r-- | mako/codegen.py | 388 | ||||
-rw-r--r-- | mako/exceptions.py | 42 | ||||
-rw-r--r-- | mako/filters.py | 18 | ||||
-rw-r--r-- | mako/lexer.py | 148 | ||||
-rw-r--r-- | mako/lookup.py | 110 | ||||
-rw-r--r-- | mako/parsetree.py | 244 | ||||
-rw-r--r-- | mako/pygen.py | 114 | ||||
-rw-r--r-- | mako/pyparser.py | 12 | ||||
-rw-r--r-- | mako/runtime.py | 164 | ||||
-rw-r--r-- | mako/template.py | 208 | ||||
-rw-r--r-- | mako/util.py | 44 |
14 files changed, 934 insertions, 934 deletions
@@ -14,7 +14,7 @@ Thanks to Ben Trofatter for all the work on this [ticket:125] -- [feature] Added a real check for "reserved" +- [feature] Added a real check for "reserved" names, that is names which are never pulled from the context and cannot be passed to the template.render() method. Current names @@ -22,15 +22,15 @@ - [feature] The html_error_template() will now apply Pygments highlighting to the source - code displayed in the traceback, if Pygments - if available. Courtesy Ben Trofatter + code displayed in the traceback, if Pygments + if available. Courtesy Ben Trofatter [ticket:95] - [feature] Added support for context managers, i.e. "% with x as e:/ % endwith" support. Courtesy Ben Trofatter [ticket:147] -- [feature] Added class-level flag to CacheImpl +- [feature] Added class-level flag to CacheImpl "pass_context"; when True, the keyword argument 'context' will be passed to get_or_create() containing the Mako Context object. @@ -40,7 +40,7 @@ to filehandles being implicitly closed. [ticket:182] -- [bug] Fixed endless recursion bug when +- [bug] Fixed endless recursion bug when nesting multiple def-calls with content. Thanks to Jeff Dairiki. [ticket:186] @@ -56,7 +56,7 @@ 0.6.1 - [bug] Added special compatibility for the 0.5.0 Cache() constructor, which was preventing file - version checks and not allowing Mako 0.6 to + version checks and not allowing Mako 0.6 to recompile the module files. 0.6.0 @@ -66,7 +66,7 @@ default plugin. Template and TemplateLookup now accept a string "cache_impl" parameter which refers to the name of a cache plugin, defaulting - to the name 'beaker'. New plugins can be + to the name 'beaker'. New plugins can be registered as pkg_resources entrypoints under the group "mako.cache", or registered directly using mako.cache.register_plugin(). The @@ -75,7 +75,7 @@ - [feature] Added support for Beaker cache regions in templates. Usage of regions should be considered - as superseding the very obsolete idea of passing in + as superseding the very obsolete idea of passing in backend options, timeouts, etc. within templates. - [feature] The 'put' method on Cache is now @@ -86,10 +86,10 @@ minus the "cache_" prefix will be passed as keyword arguments to the CacheImpl methods. -- [feature] Template and TemplateLookup now accept an argument +- [feature] Template and TemplateLookup now accept an argument cache_args, which refers to a dictionary containing - cache parameters. The cache_dir, cache_url, cache_type, - cache_timeout arguments are deprecated (will probably + cache parameters. The cache_dir, cache_url, cache_type, + cache_timeout arguments are deprecated (will probably never be removed, however) and can be passed now as cache_args={'url':<some url>, 'type':'memcached', 'timeout':50, 'dir':'/path/to/some/directory'} @@ -102,11 +102,11 @@ then template.render(myfilter=some_callable) [ticket:180] -- [feature] Added "--var name=value" option to the mako-render - script, allows passing of kw to the template from +- [feature] Added "--var name=value" option to the mako-render + script, allows passing of kw to the template from the command line. [ticket:178] -- [feature] Added module_writer argument to Template, +- [feature] Added module_writer argument to Template, TemplateLookup, allows a callable to be passed which takes over the writing of the template's module source file, so that special environment-specific steps @@ -120,33 +120,33 @@ [ticket:173] - [bug] The "benchmark" example is now Python 3 compatible - (even though several of those old template libs aren't + (even though several of those old template libs aren't available on Py3K, so YMMV) [ticket:175] 0.5 - A Template is explicitly disallowed from having a url that normalizes to relative outside - of the root. That is, if the Lookup is based + of the root. That is, if the Lookup is based at /home/mytemplates, an include that would place - the ultimate template at + the ultimate template at /home/mytemplates/../some_other_directory, i.e. outside of /home/mytemplates, is disallowed. This usage was never intended despite the lack of an explicit check. The main issue this causes - is that module files can be written outside + is that module files can be written outside of the module root (or raise an error, if file perms aren't - set up), and can also lead to the same template being - cached in the lookup under multiple, relative roots. - TemplateLookup instead has always supported multiple + set up), and can also lead to the same template being + cached in the lookup under multiple, relative roots. + TemplateLookup instead has always supported multiple file roots for this purpose. [ticket:174] 0.4.2 - Fixed bug regarding <%call>/def calls w/ content whereby the identity of the "caller" callable - inside the <%def> would be corrupted by the - presence of another <%call> in the same block. + inside the <%def> would be corrupted by the + presence of another <%call> in the same block. [ticket:170] - Fixed the babel plugin to accommodate <%block> @@ -154,18 +154,18 @@ 0.4.1 - New tag: <%block>. A variant on <%def> that - evaluates its contents in-place. + evaluates its contents in-place. Can be named or anonymous, the named version is intended for inheritance - layouts where any given section can be + layouts where any given section can be surrounded by the <%block> tag in order for it to become overrideable by inheriting templates, without the need to specify a top-level <%def> plus explicit call. - Modified scoping and argument rules as well as a - more strictly enforced usage scheme make it ideal - for this purpose without at all replacing most - other things that defs are still good for. + Modified scoping and argument rules as well as a + more strictly enforced usage scheme make it ideal + for this purpose without at all replacing most + other things that defs are still good for. Lots of new docs. [ticket:164] - a slight adjustment to the "highlight" logic @@ -174,12 +174,12 @@ without any extra guessing. [ticket:165] 0.4.0 -- A 20% speedup for a basic two-page +- A 20% speedup for a basic two-page inheritance setup rendering a table of escaped data (see http://techspot.zzzeek.org/2010/11/19/quick-mako-vs.-jinja-speed-test/). A few configurational changes which - affect those in the I-don't-do-unicode + affect those in the I-don't-do-unicode camp should be noted below. - The FastEncodingBuffer is now used @@ -187,30 +187,30 @@ regardless of whether output_encoding is set to None or not. FEB is faster than both. Only StringIO allows bytestrings - of unknown encoding to pass right - through, however - while it is of course - not recommended to send bytestrings of unknown + of unknown encoding to pass right + through, however - while it is of course + not recommended to send bytestrings of unknown encoding to the output stream, this mode of usage can be re-enabled by setting the flag bytestring_passthrough to True. -- disable_unicode mode requires that +- disable_unicode mode requires that output_encoding be set to None - it also forces the bytestring_passthrough flag to True. - the <%namespace> tag raises an error if the 'template' and 'module' attributes - are specified at the same time in - one tag. A different class is used - for each case which allows a reduction in + are specified at the same time in + one tag. A different class is used + for each case which allows a reduction in runtime conditional logic and function call overhead. [ticket:156] -- the keys() in the Context, as well as +- the keys() in the Context, as well as it's internal _data dictionary, now - include just what was specified to + include just what was specified to render() as well as Mako builtins 'caller', 'capture'. The contents of __builtin__ are no longer copied. @@ -225,45 +225,45 @@ 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 + 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 + 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 + +- Fixed missing **extra collection in setup.py which prevented setup.py from running 2to3 on install. [ticket:148] - -- New flag on Template, TemplateLookup - + +- New flag on Template, TemplateLookup - strict_undefined=True, will cause - variables not found in the context to + 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 + 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 + list comprehensions), as well as in the argument list of lambdas. This to better support the strict_undefined - feature. The change should be + 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 + 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 ${}. @@ -271,22 +271,22 @@ must be referenced explicitly. [ticket:141] -- ${} expressions embedded in tags, - such as <%foo:bar x="${...}">, now +- ${} 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 + +- 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, + [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 @@ -294,19 +294,19 @@ 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 + + Note that Pylons by default doesn't + use Mako's filter - check your environment.py file. - -- Fixed call to "unicode.strip" in + +- 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 @@ -314,13 +314,13 @@ the formatter just returns blank for the "traceback" portion. [ticket:135] - -- Fixed sometimes incorrect usage of + +- Fixed sometimes incorrect usage of exc.__class__.__name__ - in html/text error templates when using + in html/text error templates when using Python 2.4 [ticket:131] -- Fixed broken @property decorator on +- Fixed broken @property decorator on template.last_modified - Fixed error formatting when a stacktrace @@ -332,18 +332,18 @@ 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 + 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 +- 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] @@ -354,24 +354,24 @@ - 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 + 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 + 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 + and "traceback" as optional arguments, and these are now actually used. [ticket:122] - -- The exception output generated when + +- 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(). @@ -379,11 +379,11 @@ - 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 +- 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 @@ -396,16 +396,16 @@ breakage. [ticket:109] - Windows paths are handled correctly if a Template - is passed only an absolute filename (i.e. with c: + 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 + instead of passing through to the template to generate IOError. [ticket:73] - + 0.2.6 - Fix mako function decorators to preserve the @@ -429,15 +429,15 @@ - 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 + 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 + +- 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] @@ -446,28 +446,28 @@ around '=' sign in defs. [ticket:102] - Removed errant "lower()" in the lexer which - was causing tags to compile with + 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 + 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, + The %call tag itself will always remain, with <%namespacename:defname> presenting a more HTML-like - alternative to calling defs, both plain and + 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 @@ -479,42 +479,42 @@ e.g. ${local.cache.get('somekey')} or template.cache.invalidate_body() -- added "cache_enabled=True" flag to Template, +- 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 + 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 + 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 + 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 +- 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() + 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 @@ -532,16 +532,16 @@ - 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 +- 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 +cache-refresh operation that was initiated after the initiating template had completed rendering. 0.2.1 -- fixed bug where 'output_encoding' parameter would prevent +- 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 +- 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. @@ -566,18 +566,18 @@ help with memcached. template variable. This affords a 12-30% speedup in template render time. (idea courtesy same anonymous guest) [ticket:76] - -- New Features, API changes: - + +- 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} @@ -585,7 +585,7 @@ help with memcached. 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}"/> @@ -595,18 +595,18 @@ help with memcached. _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] + - 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', + names without explicit declaration (i.e. 'id', 'exception', 'range', etc.) [ticket:83] [ticket:84] - - - can also use builtin names as local variable names + + - 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] @@ -621,10 +621,10 @@ help with memcached. - 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] @@ -635,17 +635,17 @@ help with memcached. 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 +- 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 +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 @@ -654,7 +654,7 @@ Eric Woroshow exception reporter [ticket:51] 0.1.8 -- variable names declared in render methods by internal +- 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 @@ -668,14 +668,14 @@ 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 +- 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 +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 + (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] @@ -700,14 +700,14 @@ python 2.3 installations 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 +- 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 +- 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) @@ -716,7 +716,7 @@ 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 @@ -742,10 +742,10 @@ via passing special attributes or objects. 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. +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:<someencoding>. +## coding:<someencoding>. - new multiline comment form: "<%doc> a comment </%doc>" - UNDEFINED evaluates to False - improvement to scoping of "caller" variable when using <%call> tag @@ -756,7 +756,7 @@ either one "#" sign or two for now; two is preferred going forward, i.e. - 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 diff --git a/mako/ast.py b/mako/ast.py index d6fa215..76311e9 100644 --- a/mako/ast.py +++ b/mako/ast.py @@ -4,7 +4,7 @@ # This module is part of Mako and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php -"""utilities for analyzing expressions and blocks of Python +"""utilities for analyzing expressions and blocks of Python code, as well as generating Python from AST nodes""" from mako import exceptions, pyparser, util @@ -14,23 +14,23 @@ class PythonCode(object): """represents information about a string containing Python code""" def __init__(self, code, **exception_kwargs): self.code = code - + # represents all identifiers which are assigned to at some point in # the code self.declared_identifiers = set() - + # represents all identifiers which are referenced before their # assignment, if any self.undeclared_identifiers = set() - + # note that an identifier can be in both the undeclared and declared # lists. - # using AST to parse instead of using code.co_varnames, + # using AST to parse instead of using code.co_varnames, # code.co_names has several advantages: - # - we can locate an identifier as "undeclared" even if + # - we can locate an identifier as "undeclared" even if # its declared later in the same block of code - # - AST is less likely to break with version changes + # - AST is less likely to break with version changes # (for example, the behavior of co_names changed a little bit # in python version 2.5) if isinstance(code, basestring): @@ -59,12 +59,12 @@ class ArgumentList(object): f = pyparser.FindTuple(self, PythonCode, **exception_kwargs) f.visit(expr) - + class PythonFragment(PythonCode): """extends PythonCode to provide identifier lookups in partial control statements - - e.g. + + e.g. for x in 5: elif y==9: except (MyException, e): @@ -74,7 +74,7 @@ class PythonFragment(PythonCode): m = re.match(r'^(\w+)(?:\s+(.*?))?:\s*(#|$)', code.strip(), re.S) if not m: raise exceptions.CompileException( - "Fragment '%s' is not a partial control statement" % + "Fragment '%s' is not a partial control statement" % code, **exception_kwargs) if m.group(3): code = code[:m.start(3)] @@ -91,17 +91,17 @@ class PythonFragment(PythonCode): code = code + "pass" else: raise exceptions.CompileException( - "Unsupported control keyword: '%s'" % + "Unsupported control keyword: '%s'" % keyword, **exception_kwargs) super(PythonFragment, self).__init__(code, **exception_kwargs) - - + + class FunctionDecl(object): """function declaration""" def __init__(self, code, allow_kwargs=True, **exception_kwargs): self.code = code expr = pyparser.parse(code, "exec", **exception_kwargs) - + f = pyparser.ParseFunc(self, **exception_kwargs) f.visit(expr) if not hasattr(self, 'funcname'): @@ -110,13 +110,13 @@ class FunctionDecl(object): **exception_kwargs) if not allow_kwargs and self.kwargs: raise exceptions.CompileException( - "'**%s' keyword argument not allowed here" % + "'**%s' keyword argument not allowed here" % self.argnames[-1], **exception_kwargs) - + def get_argument_expressions(self, include_defaults=True): """return the argument declarations of this FunctionDecl as a printable list.""" - + namedecls = [] defaults = [d for d in self.defaults] kwargs = self.kwargs @@ -134,8 +134,8 @@ class FunctionDecl(object): else: default = len(defaults) and defaults.pop() or None if include_defaults and default: - namedecls.insert(0, "%s=%s" % - (arg, + namedecls.insert(0, "%s=%s" % + (arg, pyparser.ExpressionGenerator(default).value() ) ) @@ -145,7 +145,7 @@ class FunctionDecl(object): class FunctionArgs(FunctionDecl): """the argument portion of a function declaration""" - + def __init__(self, code, **kwargs): super(FunctionArgs, self).__init__("def ANON(%s):pass" % code, **kwargs) diff --git a/mako/cache.py b/mako/cache.py index 09b04fb..f50ce58 100644 --- a/mako/cache.py +++ b/mako/cache.py @@ -82,14 +82,14 @@ class Cache(object): return self._ctx_get_or_create(key, creation_function, None, **kw) def _ctx_get_or_create(self, key, creation_function, context, **kw): - """Retrieve a value from the cache, using the given creation function + """Retrieve a value from the cache, using the given creation function to generate a new value.""" if not self.template.cache_enabled: return creation_function() - return self.impl.get_or_create(key, - creation_function, + return self.impl.get_or_create(key, + creation_function, **self._get_cache_kw(kw, context)) def set(self, key, value, **kw): @@ -121,7 +121,7 @@ class Cache(object): """ return self.impl.get(key, **self._get_cache_kw(kw, None)) - + def invalidate(self, key, **kw): """Invalidate a value in the cache. @@ -133,22 +133,22 @@ class Cache(object): """ self.impl.invalidate(key, **self._get_cache_kw(kw, None)) - + def invalidate_body(self): """Invalidate the cached content of the "body" method for this template. """ self.invalidate('render_body', __M_defname='render_body') - + def invalidate_def(self, name): """Invalidate the cached content of a particular ``<%def>`` within this template. """ - + self.invalidate('render_%s' % name, __M_defname='render_%s' % name) - + def invalidate_closure(self, name): """Invalidate a nested ``<%def>`` within this template. @@ -159,9 +159,9 @@ class Cache(object): each other. """ - + self.invalidate(name, __M_defname=name) - + def _get_cache_kw(self, kw, context): defname = kw.pop('__M_defname', None) if not defname: @@ -216,7 +216,7 @@ class CacheImpl(object): """ raise NotImplementedError() - + def get(self, key, **kw): """Retrieve a value from the cache. @@ -225,7 +225,7 @@ class CacheImpl(object): """ raise NotImplementedError() - + def invalidate(self, key, **kw): """Invalidate a value in the cache. diff --git a/mako/codegen.py b/mako/codegen.py index 6ab6095..3cec0ee 100644 --- a/mako/codegen.py +++ b/mako/codegen.py @@ -14,61 +14,61 @@ from mako import util, ast, parsetree, filters, exceptions MAGIC_NUMBER = 8 -# names which are hardwired into the -# template and are not accessed via the +# names which are hardwired into the +# template and are not accessed via the # context itself RESERVED_NAMES = set(['context', 'loop', 'UNDEFINED']) -def compile(node, - uri, - filename=None, - default_filters=None, - buffer_filters=None, - imports=None, - source_encoding=None, +def compile(node, + uri, + filename=None, + default_filters=None, + buffer_filters=None, + imports=None, + source_encoding=None, generate_magic_comment=True, disable_unicode=False, strict_undefined=False, enable_loop=True, reserved_names=()): - - """Generate module source code given a parsetree node, + + """Generate module source code given a parsetree node, uri, and optional source filename""" # if on Py2K, push the "source_encoding" string to be - # a bytestring itself, as we will be embedding it into - # the generated source and we don't want to coerce the + # a bytestring itself, as we will be embedding it into + # the generated source and we don't want to coerce the # result into a unicode object, in "disable_unicode" mode if not util.py3k and isinstance(source_encoding, unicode): source_encoding = source_encoding.encode(source_encoding) - - + + buf = util.FastEncodingBuffer() printer = PythonPrinter(buf) - _GenerateRenderMethod(printer, - _CompileContext(uri, - filename, - default_filters, + _GenerateRenderMethod(printer, + _CompileContext(uri, + filename, + default_filters, buffer_filters, - imports, + imports, source_encoding, generate_magic_comment, disable_unicode, strict_undefined, enable_loop, - reserved_names), + reserved_names), node) return buf.getvalue() class _CompileContext(object): - def __init__(self, - uri, - filename, - default_filters, - buffer_filters, - imports, - source_encoding, + def __init__(self, + uri, + filename, + default_filters, + buffer_filters, + imports, + source_encoding, generate_magic_comment, disable_unicode, strict_undefined, @@ -85,11 +85,11 @@ class _CompileContext(object): self.strict_undefined = strict_undefined self.enable_loop = enable_loop self.reserved_names = reserved_names - + class _GenerateRenderMethod(object): - """A template visitor object which generates the + """A template visitor object which generates the full module source for a template. - + """ def __init__(self, printer, compiler, node): self.printer = printer @@ -97,13 +97,13 @@ class _GenerateRenderMethod(object): self.compiler = compiler self.node = node self.identifier_stack = [None] - + self.in_def = isinstance(node, (parsetree.DefTag, parsetree.BlockTag)) if self.in_def: name = "render_%s" % node.funcname args = node.get_argument_expressions() - filtered = len(node.filter_args.args) > 0 + filtered = len(node.filter_args.args) > 0 buffered = eval(node.attributes.get('buffered', 'False')) cached = eval(node.attributes.get('cached', 'False')) defs = None @@ -131,24 +131,24 @@ class _GenerateRenderMethod(object): args = ['context'] else: args = [a for a in ['context'] + args] - + self.write_render_callable( - pagetag or node, - name, args, + pagetag or node, + name, args, buffered, filtered, cached) - + if defs is not None: for node in defs: _GenerateRenderMethod(printer, compiler, node) - + @property def identifiers(self): return self.identifier_stack[-1] - + def write_toplevel(self): """Traverse a template structure for module-level directives and generate the start of module-level code. - + """ inherit = [] namespaces = {} @@ -156,7 +156,7 @@ class _GenerateRenderMethod(object): encoding =[None] self.compiler.pagetag = None - + class FindTopLevel(object): def visitInheritTag(s, node): inherit.append(node) @@ -167,7 +167,7 @@ class _GenerateRenderMethod(object): def visitCode(s, node): if node.ismodule: module_code.append(node) - + f = FindTopLevel() for n in self.node.nodes: n.accept_visitor(f) @@ -180,13 +180,13 @@ class _GenerateRenderMethod(object): module_identifiers = _Identifiers(self.compiler) module_identifiers.declared = module_ident - + # module-level names, python code if self.compiler.generate_magic_comment and \ self.compiler.source_encoding: self.printer.writeline("# -*- encoding:%s -*-" % self.compiler.source_encoding) - + self.printer.writeline("from mako import runtime, filters, cache") self.printer.writeline("UNDEFINED = runtime.UNDEFINED") self.printer.writeline("__M_dict_builtin = dict") @@ -205,13 +205,13 @@ class _GenerateRenderMethod(object): buf += imp + "\n" self.printer.writeline(imp) impcode = ast.PythonCode( - buf, - source='', lineno=0, - pos=0, + buf, + source='', lineno=0, + pos=0, filename='template defined imports') else: impcode = None - + main_identifiers = module_identifiers.branch(self.node) module_identifiers.topleveldefs = \ module_identifiers.topleveldefs.\ @@ -219,9 +219,9 @@ class _GenerateRenderMethod(object): module_identifiers.declared.add("UNDEFINED") if impcode: module_identifiers.declared.update(impcode.declared_identifiers) - + self.compiler.identifiers = module_identifiers - self.printer.writeline("_exports = %r" % + self.printer.writeline("_exports = %r" % [n.name for n in main_identifiers.topleveldefs.values()] ) @@ -241,15 +241,15 @@ class _GenerateRenderMethod(object): def write_render_callable(self, node, name, args, buffered, filtered, cached): """write a top-level render callable. - + this could be the main render() method or that of a top-level def.""" - + if self.in_def: decorator = node.decorator if decorator: self.printer.writeline( "@runtime._decorate_toplevel(%s)" % decorator) - + self.printer.writelines( "def %s(%s):" % (name, ','.join(args)), # push new frame, assign current frame to __M_caller @@ -258,7 +258,7 @@ class _GenerateRenderMethod(object): ) if buffered or filtered or cached: self.printer.writeline("context._push_buffer()") - + self.identifier_stack.append( self.compiler.identifiers.branch(self.node)) if (not self.in_def or self.node.is_block) and '**pageargs' in args: @@ -268,7 +268,7 @@ class _GenerateRenderMethod(object): len(self.identifiers.locally_assigned) > 0 or len(self.identifiers.argument_declared) > 0 ): - self.printer.writeline("__M_locals = __M_dict_builtin(%s)" % + self.printer.writeline("__M_locals = __M_dict_builtin(%s)" % ','.join([ "%s=%s" % (x, x) for x in self.identifiers.argument_declared @@ -284,12 +284,12 @@ class _GenerateRenderMethod(object): self.printer.write("\n\n") if cached: self.write_cache_decorator( - node, name, - args, buffered, + node, name, + args, buffered, self.identifiers, toplevel=True) - + def write_module_code(self, module_code): - """write module-level template code, i.e. that which + """write module-level template code, i.e. that which is enclosed in <%! %> tags in the template.""" for n in module_code: self.write_source_comment(n) @@ -297,7 +297,7 @@ class _GenerateRenderMethod(object): def write_inherit(self, node): """write the module-level inheritance-determination callable.""" - + self.printer.writelines( "def _mako_inherit(template, context):", "_mako_generate_namespaces(context)", @@ -319,7 +319,7 @@ class _GenerateRenderMethod(object): ) self.printer.writeline("def _mako_generate_namespaces(context):") - + for node in namespaces.values(): if node.attributes.has_key('import'): self.compiler.has_ns_imports = True @@ -363,8 +363,8 @@ class _GenerateRenderMethod(object): " calling_uri=_template_uri)" % ( node.name, - node.parsed_attributes.get('file', 'None'), - callable_name, + node.parsed_attributes.get('file', 'None'), + callable_name, ) ) elif 'module' in node.parsed_attributes: @@ -375,7 +375,7 @@ class _GenerateRenderMethod(object): " module=%s)" % ( node.name, - callable_name, + callable_name, node.parsed_attributes.get('module', 'None') ) ) @@ -386,22 +386,22 @@ class _GenerateRenderMethod(object): " callables=%s, calling_uri=_template_uri)" % ( node.name, - callable_name, + callable_name, ) ) if eval(node.attributes.get('inheritable', "False")): self.printer.writeline("context['self'].%s = ns" % (node.name)) - + self.printer.writeline( "context.namespaces[(__name__, %s)] = ns" % repr(node.name)) self.printer.write("\n") if not len(namespaces): self.printer.writeline("pass") self.printer.writeline(None) - + def write_variable_declares(self, identifiers, toplevel=False, limit=None): """write variable declarations at the top of a function. - + the variable declarations are in the form of callable definitions for defs and/or name lookup within the function's context argument. the names declared are based @@ -410,36 +410,36 @@ class _GenerateRenderMethod(object): operation. names that are assigned within the body are assumed to be locally-scoped variables and are not separately declared. - + for def callable definitions, if the def is a top-level callable then a 'stub' callable is generated which wraps the current Context into a closure. if the def is not top-level, it is fully rendered as a local closure. - + """ - + # collection of all defs available to us in this scope comp_idents = dict([(c.funcname, c) for c in identifiers.defs]) to_write = set() - - # write "context.get()" for all variables we are going to + + # write "context.get()" for all variables we are going to # need that arent in the namespace yet to_write = to_write.union(identifiers.undeclared) - - # write closure functions for closures that we define + + # write closure functions for closures that we define # right here to_write = to_write.union( [c.funcname for c in identifiers.closuredefs.values()]) - # remove identifiers that are declared in the argument + # remove identifiers that are declared in the argument # signature of the callable to_write = to_write.difference(identifiers.argument_declared) - # remove identifiers that we are going to assign to. + # remove identifiers that we are going to assign to. # in this way we mimic Python's behavior, - # i.e. assignment to a variable within a block + # i.e. assignment to a variable within a block # means that variable is now a "locally declared" var, - # which cannot be referenced beforehand. + # which cannot be referenced beforehand. to_write = to_write.difference(identifiers.locally_declared) if self.compiler.enable_loop: @@ -447,7 +447,7 @@ class _GenerateRenderMethod(object): to_write.discard("loop") else: has_loop = False - + # if a limiting set was sent, constraint to those items in that list # (this is used for the caching decorator) if limit is not None: @@ -487,20 +487,20 @@ class _GenerateRenderMethod(object): elif ident in self.compiler.namespaces: self.printer.writeline( - "%s = _mako_get_namespace(context, %r)" % + "%s = _mako_get_namespace(context, %r)" % (ident, ident) ) else: if getattr(self.compiler, 'has_ns_imports', False): if self.compiler.strict_undefined: self.printer.writelines( - "%s = _import_ns.get(%r, UNDEFINED)" % + "%s = _import_ns.get(%r, UNDEFINED)" % (ident, ident), "if %s is UNDEFINED:" % ident, "try:", "%s = context[%r]" % (ident, ident), "except KeyError:", - "raise NameError(\"'%s' is not defined\")" % + "raise NameError(\"'%s' is not defined\")" % ident, None, None ) @@ -514,7 +514,7 @@ class _GenerateRenderMethod(object): "try:", "%s = context[%r]" % (ident, ident), "except KeyError:", - "raise NameError(\"'%s' is not defined\")" % + "raise NameError(\"'%s' is not defined\")" % ident, None ) @@ -522,9 +522,9 @@ class _GenerateRenderMethod(object): self.printer.writeline( "%s = context.get(%r, UNDEFINED)" % (ident, ident) ) - + self.printer.writeline("__M_writer = context.writer()") - + def write_source_comment(self, node): """write a source comment containing the line number of the corresponding template line.""" @@ -537,7 +537,7 @@ class _GenerateRenderMethod(object): funcname = node.funcname namedecls = node.get_argument_expressions() nameargs = node.get_argument_expressions(include_defaults=False) - + if not self.in_def and ( len(self.identifiers.locally_assigned) > 0 or len(self.identifiers.argument_declared) > 0): @@ -548,19 +548,19 @@ class _GenerateRenderMethod(object): self.printer.writeline( "return render_%s(%s)" % (funcname, ",".join(nameargs))) self.printer.writeline(None) - + def write_inline_def(self, node, identifiers, nested): """write a locally-available def callable inside an enclosing def.""" namedecls = node.get_argument_expressions() - + decorator = node.decorator if decorator: self.printer.writeline( "@runtime._decorate_inline(context, %s)" % decorator) self.printer.writeline( "def %s(%s):" % (node.funcname, ",".join(namedecls))) - filtered = len(node.filter_args.args) > 0 + filtered = len(node.filter_args.args) > 0 buffered = eval(node.attributes.get('buffered', 'False')) cached = eval(node.attributes.get('cached', 'False')) self.printer.writelines( @@ -576,29 +576,29 @@ class _GenerateRenderMethod(object): identifiers = identifiers.branch(node, nested=nested) self.write_variable_declares(identifiers) - + self.identifier_stack.append(identifiers) for n in node.nodes: n.accept_visitor(self) self.identifier_stack.pop() - + self.write_def_finish(node, buffered, filtered, cached) self.printer.writeline(None) if cached: - self.write_cache_decorator(node, node.funcname, - namedecls, False, identifiers, + self.write_cache_decorator(node, node.funcname, + namedecls, False, identifiers, inline=True, toplevel=False) - + def write_def_finish(self, node, buffered, filtered, cached, callstack=True): """write the end section of a rendering function, either outermost or inline. - + this takes into account if the rendering function was filtered, buffered, etc. and closes the corresponding try: block if any, and writes code to retrieve captured content, apply filters, send proper return value.""" - + if not buffered and not cached and not filtered: self.printer.writeline("return ''") if callstack: @@ -607,7 +607,7 @@ class _GenerateRenderMethod(object): "context.caller_stack._pop_frame()", None ) - + if buffered or filtered or cached: if buffered or cached: # in a caching scenario, don't try to get a writer @@ -623,10 +623,10 @@ class _GenerateRenderMethod(object): "finally:", "__M_buf, __M_writer = context._pop_buffer_and_writer()" ) - + if callstack: self.printer.writeline("context.caller_stack._pop_frame()") - + s = "__M_buf.getvalue()" if filtered: s = self.create_filter_callable(node.filter_args.args, s, @@ -643,12 +643,12 @@ class _GenerateRenderMethod(object): "return ''" ) - def write_cache_decorator(self, node_or_pagetag, name, - args, buffered, identifiers, + def write_cache_decorator(self, node_or_pagetag, name, + args, buffered, identifiers, inline=False, toplevel=False): - """write a post-function decorator to replace a rendering + """write a post-function decorator to replace a rendering callable with a cached version of itself.""" - + self.printer.writeline("__M_%s = %s" % (name, name)) cachekey = node_or_pagetag.parsed_attributes.get('cache_key', repr(name)) @@ -657,42 +657,42 @@ class _GenerateRenderMethod(object): if self.compiler.pagetag is not None: cache_args.update( ( - pa[6:], + pa[6:], self.compiler.pagetag.parsed_attributes[pa] - ) - for pa in self.compiler.pagetag.parsed_attributes + ) + for pa in self.compiler.pagetag.parsed_attributes if pa.startswith('cache_') and pa != 'cache_key' ) cache_args.update( ( - pa[6:], + pa[6:], node_or_pagetag.parsed_attributes[pa] - ) for pa in node_or_pagetag.parsed_attributes + ) for pa in node_or_pagetag.parsed_attributes if pa.startswith('cache_') and pa != 'cache_key' ) if 'timeout' in cache_args: cache_args['timeout'] = int(eval(cache_args['timeout'])) - + self.printer.writeline("def %s(%s):" % (name, ','.join(args))) - + # form "arg1, arg2, arg3=arg3, arg4=arg4", etc. pass_args = [ - '=' in a and "%s=%s" % ((a.split('=')[0],)*2) or a + '=' in a and "%s=%s" % ((a.split('=')[0],)*2) or a for a in args ] self.write_variable_declares( - identifiers, - toplevel=toplevel, + identifiers, + toplevel=toplevel, limit=node_or_pagetag.undeclared_identifiers() ) if buffered: s = "context.get('local')."\ "cache._ctx_get_or_create("\ "%s, lambda:__M_%s(%s), context, %s__M_defname=%r)" % \ - (cachekey, name, ','.join(pass_args), - ''.join(["%s=%s, " % (k,v) - for k, v in cache_args.items()]), + (cachekey, name, ','.join(pass_args), + ''.join(["%s=%s, " % (k,v) + for k, v in cache_args.items()]), name ) # apply buffer_filters @@ -703,21 +703,21 @@ class _GenerateRenderMethod(object): self.printer.writelines( "__M_writer(context.get('local')." "cache._ctx_get_or_create("\ - "%s, lambda:__M_%s(%s), context, %s__M_defname=%r))" % - (cachekey, name, ','.join(pass_args), - ''.join(["%s=%s, " % (k,v) - for k, v in cache_args.items()]), - name, + "%s, lambda:__M_%s(%s), context, %s__M_defname=%r))" % + (cachekey, name, ','.join(pass_args), + ''.join(["%s=%s, " % (k,v) + for k, v in cache_args.items()]), + name, ), "return ''", None ) def create_filter_callable(self, args, target, is_expression): - """write a filter-applying expression based on the filters - present in the given filter names, adjusting for the global + """write a filter-applying expression based on the filters + present in the given filter names, adjusting for the global 'default' filter aliases as needed.""" - + def locate_encode(name): if re.match(r'decode\..+', name): return "filters." + name @@ -725,7 +725,7 @@ class _GenerateRenderMethod(object): return filters.NON_UNICODE_ESCAPES.get(name, name) else: return filters.DEFAULT_ESCAPES.get(name, name) - + if 'n' not in args: if is_expression: if self.compiler.pagetag: @@ -747,7 +747,7 @@ class _GenerateRenderMethod(object): assert e is not None target = "%s(%s)" % (e, target) return target - + def visitExpression(self, node): self.write_source_comment(node) if len(node.escapes) or \ @@ -756,13 +756,13 @@ class _GenerateRenderMethod(object): len(self.compiler.pagetag.filter_args.args) ) or \ len(self.compiler.default_filters): - + s = self.create_filter_callable(node.escapes_code.args, "%s" % node.text, True) self.printer.writeline("__M_writer(%s)" % s) else: self.printer.writeline("__M_writer(%s)" % node.text) - + def visitControlLine(self, node): if node.isend: self.printer.writeline(None) @@ -791,11 +791,11 @@ class _GenerateRenderMethod(object): for c in children if isinstance(c, parsetree.ControlLine))): self.printer.writeline("pass") - + def visitText(self, node): self.write_source_comment(node) self.printer.writeline("__M_writer(%s)" % repr(node.content)) - + def visitTextTag(self, node): filtered = len(node.filter_args.args) > 0 if filtered: @@ -809,23 +809,23 @@ class _GenerateRenderMethod(object): self.printer.writelines( "finally:", "__M_buf, __M_writer = context._pop_buffer_and_writer()", - "__M_writer(%s)" % + "__M_writer(%s)" % self.create_filter_callable( - node.filter_args.args, - "__M_buf.getvalue()", + node.filter_args.args, + "__M_buf.getvalue()", False), None ) - + def visitCode(self, node): if not node.ismodule: self.write_source_comment(node) self.printer.write_indented_block(node.text) if not self.in_def and len(self.identifiers.locally_assigned) > 0: - # if we are the "template" def, fudge locally + # if we are the "template" def, fudge locally # declared/modified variables into the "__M_locals" dictionary, - # which is used for def calls within the same template, + # which is used for def calls within the same template, # to simulate "enclosing scope" self.printer.writeline( '__M_locals_builtin_stored = __M_locals_builtin()') @@ -846,10 +846,10 @@ class _GenerateRenderMethod(object): self.printer.writeline( "runtime._include_file(context, %s, _template_uri)" % (node.parsed_attributes['file'])) - + def visitNamespaceTag(self, node): pass - + def visitDefTag(self, node): pass @@ -871,18 +871,18 @@ class _GenerateRenderMethod(object): # as ensure the given namespace will be imported, # pre-import the namespace, etc. self.visitCallTag(node) - + def visitCallTag(self, node): self.printer.writeline("def ccall(caller):") export = ['body'] callable_identifiers = self.identifiers.branch(node, nested=True) body_identifiers = callable_identifiers.branch(node, nested=False) - # we want the 'caller' passed to ccall to be used - # for the body() function, but for other non-body() - # <%def>s within <%call> we want the current caller + # we want the 'caller' passed to ccall to be used + # for the body() function, but for other non-body() + # <%def>s within <%call> we want the current caller # off the call stack (if any) body_identifiers.add_declared('caller') - + self.identifier_stack.append(body_identifiers) class DefVisitor(object): def visitDefTag(s, node): @@ -904,11 +904,11 @@ class _GenerateRenderMethod(object): for n in node.nodes: n.accept_visitor(vis) self.identifier_stack.pop() - - bodyargs = node.body_decl.get_argument_expressions() + + bodyargs = node.body_decl.get_argument_expressions() self.printer.writeline("def body(%s):" % ','.join(bodyargs)) - - # TODO: figure out best way to specify + + # TODO: figure out best way to specify # buffering/nonbuffering (at call time would be better) buffered = False if buffered: @@ -918,11 +918,11 @@ class _GenerateRenderMethod(object): ) self.write_variable_declares(body_identifiers) self.identifier_stack.append(body_identifiers) - + for n in node.nodes: n.accept_visitor(self) self.identifier_stack.pop() - + self.write_def_finish(node, buffered, False, False, callstack=False) self.printer.writelines( None, @@ -947,7 +947,7 @@ class _GenerateRenderMethod(object): class _Identifiers(object): """tracks the status of identifier names as template code is rendered.""" - + def __init__(self, compiler, node=None, parent=None, nested=False): if parent is not None: # if we are the branch created in write_namespaces(), @@ -956,20 +956,20 @@ class _Identifiers(object): self.declared = set() self.topleveldefs = util.SetLikeDict() else: - # things that have already been declared + # things that have already been declared # in an enclosing namespace (i.e. names we can just use) self.declared = set(parent.declared).\ union([c.name for c in parent.closuredefs.values()]).\ union(parent.locally_declared).\ union(parent.argument_declared) - - # if these identifiers correspond to a "nested" - # scope, it means whatever the parent identifiers - # had as undeclared will have been declared by that parent, + + # if these identifiers correspond to a "nested" + # scope, it means whatever the parent identifiers + # had as undeclared will have been declared by that parent, # and therefore we have them in our scope. if nested: self.declared = self.declared.union(parent.undeclared) - + # top level defs that are available self.topleveldefs = util.SetLikeDict(**parent.topleveldefs) else: @@ -978,29 +978,29 @@ class _Identifiers(object): self.compiler = compiler - # things within this level that are referenced before they + # things within this level that are referenced before they # are declared (e.g. assigned to) self.undeclared = set() - - # things that are declared locally. some of these things - # could be in the "undeclared" list as well if they are + + # things that are declared locally. some of these things + # could be in the "undeclared" list as well if they are # referenced before declared self.locally_declared = set() - - # assignments made in explicit python blocks. - # these will be propagated to + + # assignments made in explicit python blocks. + # these will be propagated to # the context of local def calls. self.locally_assigned = set() - - # things that are declared in the argument + + # things that are declared in the argument # signature of the def callable self.argument_declared = set() - + # closure defs that are defined in this level self.closuredefs = util.SetLikeDict() - + self.node = node - + if node is not None: node.accept_visitor(self) @@ -1008,20 +1008,20 @@ class _Identifiers(object): self.locally_declared) if illegal_names: raise exceptions.NameConflictError( - "Reserved words declared in template: %s" % + "Reserved words declared in template: %s" % ", ".join(illegal_names)) def branch(self, node, **kwargs): - """create a new Identifiers for a new Node, with + """create a new Identifiers for a new Node, with this Identifiers as the parent.""" - + return _Identifiers(self.compiler, node, self, **kwargs) - + @property def defs(self): return set(self.topleveldefs.union(self.closuredefs).values()) - + def __repr__(self): return "Identifiers(declared=%r, locally_declared=%r, "\ "undeclared=%r, topleveldefs=%r, closuredefs=%r, "\ @@ -1033,38 +1033,38 @@ class _Identifiers(object): [c.name for c in self.topleveldefs.values()], [c.name for c in self.closuredefs.values()], self.argument_declared) - + def check_declared(self, node): - """update the state of this Identifiers with the undeclared + """update the state of this Identifiers with the undeclared and declared identifiers of the given node.""" - + for ident in node.undeclared_identifiers(): if ident != 'context' and\ ident not in self.declared.union(self.locally_declared): self.undeclared.add(ident) for ident in node.declared_identifiers(): self.locally_declared.add(ident) - + def add_declared(self, ident): self.declared.add(ident) if ident in self.undeclared: self.undeclared.remove(ident) - + def visitExpression(self, node): self.check_declared(node) - + def visitControlLine(self, node): self.check_declared(node) - + def visitCode(self, node): if not node.ismodule: self.check_declared(node) self.locally_assigned = self.locally_assigned.union( node.declared_identifiers()) - + def visitNamespaceTag(self, node): - # only traverse into the sub-elements of a - # <%namespace> tag if we are the branch created in + # only traverse into the sub-elements of a + # <%namespace> tag if we are the branch created in # write_namespaces() if self.node is node: for n in node.nodes: @@ -1078,7 +1078,7 @@ class _Identifiers(object): (node.is_block or existing.is_block): raise exceptions.CompileException( "%%def or %%block named '%s' already " - "exists in this template." % + "exists in this template." % node.funcname, **node.exception_kwargs) def visitDefTag(self, node): @@ -1091,7 +1091,7 @@ class _Identifiers(object): if ident != 'context' and\ ident not in self.declared.union(self.locally_declared): self.undeclared.add(ident) - + # visit defs only one level deep if node is self.node: for ident in node.declared_identifiers(): @@ -1106,19 +1106,19 @@ class _Identifiers(object): if isinstance(self.node, parsetree.DefTag): raise exceptions.CompileException( - "Named block '%s' not allowed inside of def '%s'" + "Named block '%s' not allowed inside of def '%s'" % (node.name, self.node.name), **node.exception_kwargs) elif isinstance(self.node, (parsetree.CallTag, parsetree.CallNamespaceTag)): raise exceptions.CompileException( - "Named block '%s' not allowed inside of <%%call> tag" + "Named block '%s' not allowed inside of <%%call> tag" % (node.name, ), **node.exception_kwargs) for ident in node.undeclared_identifiers(): if ident != 'context' and\ ident not in self.declared.union(self.locally_declared): self.undeclared.add(ident) - + if not node.is_anonymous: self._check_name_exists(self.topleveldefs, node) self.undeclared.add(node.funcname) @@ -1131,15 +1131,15 @@ class _Identifiers(object): def visitIncludeTag(self, node): self.check_declared(node) - + def visitPageTag(self, node): for ident in node.declared_identifiers(): self.argument_declared.add(ident) self.check_declared(node) - + def visitCallNamespaceTag(self, node): self.visitCallTag(node) - + def visitCallTag(self, node): if node is self.node: for ident in node.undeclared_identifiers(): @@ -1155,7 +1155,7 @@ class _Identifiers(object): if ident != 'context' and\ ident not in self.declared.union(self.locally_declared): self.undeclared.add(ident) - + _FOR_LOOP = re.compile( r'^for\s+((?:\(?)\s*[A-Za-z_][A-Za-z_0-9]*' @@ -1186,7 +1186,7 @@ def mangle_mako_loop(node, printer): class LoopVariable(object): - """A node visitor which looks for the name 'loop' within undeclared + """A node visitor which looks for the name 'loop' within undeclared identifiers.""" def __init__(self): diff --git a/mako/exceptions.py b/mako/exceptions.py index 0276957..b8d5ef3 100644 --- a/mako/exceptions.py +++ b/mako/exceptions.py @@ -20,8 +20,8 @@ def _format_filepos(lineno, pos, filename): return " at line: %d char: %d" % (lineno, pos) else: return " in file '%s' at line: %d char: %d" % (filename, lineno, pos) - - + + class CompileException(MakoException): def __init__(self, message, source, lineno, pos, filename): MakoException.__init__(self, @@ -30,7 +30,7 @@ class CompileException(MakoException): self.pos = pos self.filename = filename self.source = source - + class SyntaxException(MakoException): def __init__(self, message, source, lineno, pos, filename): MakoException.__init__(self, @@ -42,7 +42,7 @@ class SyntaxException(MakoException): class UnsupportedError(MakoException): """raised when a retired feature is used.""" - + class NameConflictError(MakoException): """raised when a reserved word is used inappropriately""" @@ -51,7 +51,7 @@ class TemplateLookupException(MakoException): class TopLevelLookupException(TemplateLookupException): pass - + class RichTraceback(object): """Pull the current exception from the ``sys`` traceback and extracts Mako-specific template information. @@ -64,28 +64,28 @@ class RichTraceback(object): if error is None or traceback is None: t, value, tback = sys.exc_info() - + if error is None: error = value or t - + if traceback is None: traceback = tback - + self.error = error self.records = self._init(traceback) - + if isinstance(self.error, (CompileException, SyntaxException)): import mako.template self.source = self.error.source self.lineno = self.error.lineno self._has_source = True - + self._init_message() - + @property def errorname(self): return util.exception_name(self.error) - + def _init_message(self): """Find a unicode representation of self.error""" try: @@ -106,7 +106,7 @@ class RichTraceback(object): yield (rec[4], rec[5], rec[2], rec[6]) else: yield tuple(rec[0:4]) - + @property def traceback(self): """Return a list of 4-tuple traceback records (i.e. normal python @@ -115,16 +115,16 @@ class RichTraceback(object): """ return list(self._get_reformatted_records(self.records)) - + @property def reverse_records(self): return reversed(self.records) - + @property def reverse_traceback(self): """Return the same data as traceback, except in reverse order. """ - + return list(self._get_reformatted_records(self.reverse_records)) def _init(self, trcback): @@ -161,7 +161,7 @@ class RichTraceback(object): line = line.decode(encoding) else: line = line.decode('ascii', 'replace') - new_trcback.append((filename, lineno, function, line, + new_trcback.append((filename, lineno, function, line, None, None, None, None)) continue @@ -182,8 +182,8 @@ class RichTraceback(object): template_line = template_lines[template_ln - 1] else: template_line = None - new_trcback.append((filename, lineno, function, - line, template_filename, template_ln, + new_trcback.append((filename, lineno, function, + line, template_filename, template_ln, template_line, template_source)) if not self.source: for l in range(len(new_trcback)-1, 0, -1): @@ -207,13 +207,13 @@ class RichTraceback(object): self.lineno = new_trcback[-1][1] return new_trcback - + def text_error_template(lookup=None): """Provides a template that renders a stack trace in a similar format to the Python interpreter, substituting source template filenames, line numbers and code for that of the originating source template, as applicable. - + """ import mako.template return mako.template.Template(r""" diff --git a/mako/filters.py b/mako/filters.py index 514daf0..37c8fe4 100644 --- a/mako/filters.py +++ b/mako/filters.py @@ -11,10 +11,10 @@ from mako import util xml_escapes = { '&' : '&', - '>' : '>', - '<' : '<', + '>' : '>', + '<' : '<', '"' : '"', # also " in html-only - "'" : ''' # also ' in html-only + "'" : ''' # also ' in html-only } # XXX: " is valid in HTML and XML @@ -31,7 +31,7 @@ try: except ImportError: html_escape = legacy_html_escape - + def xml_escape(string): return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string) @@ -61,14 +61,14 @@ class Decode(object): return unicode(x, encoding=key) return decode decode = Decode() - - + + _ASCII_re = re.compile(r'\A[\x00-\x7f]*\Z') def is_ascii_str(text): return isinstance(text, str) and _ASCII_re.match(text) -################################################################ +################################################################ class XMLEntityEscaper(object): def __init__(self, codepoint2name, name2codepoint): @@ -115,7 +115,7 @@ class XMLEntityEscaper(object): | ( (?!\d) [:\w] [-.:\w]+ ) ) ;''', re.X | re.UNICODE) - + def __unescape(self, m): dval, hval, name = m.groups() if dval: @@ -128,7 +128,7 @@ class XMLEntityEscaper(object): if codepoint < 128: return chr(codepoint) return unichr(codepoint) - + def unescape(self, text): """Unescape character references. diff --git a/mako/lexer.py b/mako/lexer.py index a445f47..267c0d1 100644 --- a/mako/lexer.py +++ b/mako/lexer.py @@ -13,8 +13,8 @@ from mako.pygen import adjust_whitespace _regexp_cache = {} class Lexer(object): - def __init__(self, text, filename=None, - disable_unicode=False, + def __init__(self, text, filename=None, + disable_unicode=False, input_encoding=None, preprocessor=None): self.text = text self.filename = filename @@ -28,29 +28,29 @@ class Lexer(object): self.ternary_stack = [] self.disable_unicode = disable_unicode self.encoding = input_encoding - + if util.py3k and disable_unicode: raise exceptions.UnsupportedError( "Mako for Python 3 does not " "support disabling Unicode") - + if preprocessor is None: self.preprocessor = [] elif not hasattr(preprocessor, '__iter__'): self.preprocessor = [preprocessor] else: self.preprocessor = preprocessor - + @property def exception_kwargs(self): - return {'source':self.text, - 'lineno':self.matched_lineno, - 'pos':self.matched_charpos, + return {'source':self.text, + 'lineno':self.matched_lineno, + 'pos':self.matched_charpos, 'filename':self.filename} - + def match(self, regexp, flags=None): """compile the given regexp, cache the reg, and call match_reg().""" - + try: reg = _regexp_cache[(regexp, flags)] except KeyError: @@ -59,15 +59,15 @@ class Lexer(object): else: reg = re.compile(regexp) _regexp_cache[(regexp, flags)] = reg - + return self.match_reg(reg) - + def match_reg(self, reg): """match the given regular expression object to the current text position. - + if a match occurs, update the current text and line position. - + """ mp = self.match_position @@ -86,12 +86,12 @@ class Lexer(object): cp -=1 self.matched_charpos = mp - cp self.lineno += len(lines) - #print "MATCHED:", match.group(0), "LINE START:", + #print "MATCHED:", match.group(0), "LINE START:", # self.matched_lineno, "LINE END:", self.lineno #print "MATCH:", regexp, "\n", self.text[mp : mp + 15], \ # (match and "TRUE" or "FALSE") return match - + def parse_until_text(self, *text): startpos = self.match_position text_re = r'|'.join(text) @@ -119,10 +119,10 @@ class Lexer(object): brace_level -= match.group(1).count('}') continue raise exceptions.SyntaxException( - "Expected: %s" % - ','.join(text), + "Expected: %s" % + ','.join(text), **self.exception_kwargs) - + def append_node(self, nodecls, *args, **kwargs): kwargs.setdefault('source', self.text) kwargs.setdefault('lineno', self.matched_lineno) @@ -185,8 +185,8 @@ class Lexer(object): if m is not None and m.group(1) != 'utf-8': raise exceptions.CompileException( "Found utf-8 BOM in file, with conflicting " - "magic encoding comment of '%s'" % m.group(1), - text.decode('utf-8', 'ignore'), + "magic encoding comment of '%s'" % m.group(1), + text.decode('utf-8', 'ignore'), 0, 0, filename) else: m = self._coding_re.match(text.decode('utf-8', 'ignore')) @@ -201,31 +201,31 @@ class Lexer(object): except UnicodeDecodeError, e: raise exceptions.CompileException( "Unicode decode operation of encoding '%s' failed" % - parsed_encoding, - text.decode('utf-8', 'ignore'), + parsed_encoding, + text.decode('utf-8', 'ignore'), 0, 0, filename) return parsed_encoding, text def parse(self): - self.encoding, self.text = self.decode_raw_stream(self.text, - not self.disable_unicode, + self.encoding, self.text = self.decode_raw_stream(self.text, + not self.disable_unicode, self.encoding, self.filename,) for preproc in self.preprocessor: self.text = preproc(self.text) - - # push the match marker past the + + # push the match marker past the # encoding comment. self.match_reg(self._coding_re) - + self.textlength = len(self.text) - + while (True): - if self.match_position > self.textlength: + if self.match_position > self.textlength: break - + if self.match_end(): break if self.match_expression(): @@ -234,28 +234,28 @@ class Lexer(object): continue if self.match_comment(): continue - if self.match_tag_start(): + if self.match_tag_start(): continue if self.match_tag_end(): continue if self.match_python_block(): continue - if self.match_text(): + if self.match_text(): continue - - if self.match_position > self.textlength: + + if self.match_position > self.textlength: break raise exceptions.CompileException("assertion failed") - + if len(self.tag): - raise exceptions.SyntaxException("Unclosed tag: <%%%s>" % - self.tag[-1].keyword, + raise exceptions.SyntaxException("Unclosed tag: <%%%s>" % + self.tag[-1].keyword, **self.exception_kwargs) if len(self.control_line): raise exceptions.SyntaxException( "Unterminated control keyword: '%s'" % - self.control_line[-1].keyword, - self.text, + self.control_line[-1].keyword, + self.text, self.control_line[-1].lineno, self.control_line[-1].pos, self.filename) return self.template @@ -263,20 +263,20 @@ class Lexer(object): def match_tag_start(self): match = self.match(r''' \<% # opening tag - + ([\w\.\:]+) # keyword - + ((?:\s+\w+|\s*=\s*|".*?"|'.*?')*) # attrname, = \ # sign, string expression - + \s* # more whitespace - + (/)?> # closing - - ''', - + + ''', + re.I | re.S | re.X) - + if match: keyword, attr, isend = match.groups() self.keyword = keyword @@ -296,22 +296,22 @@ class Lexer(object): match = self.match(r'(.*?)(?=\</%text>)', re.S) if not match: raise exceptions.SyntaxException( - "Unclosed tag: <%%%s>" % - self.tag[-1].keyword, + "Unclosed tag: <%%%s>" % + self.tag[-1].keyword, **self.exception_kwargs) self.append_node(parsetree.Text, match.group(1)) return self.match_tag_end() return True - else: + else: return False - + def match_tag_end(self): match = self.match(r'\</%[\t ]*(.+?)[\t ]*>') if match: if not len(self.tag): raise exceptions.SyntaxException( "Closing tag without opening tag: </%%%s>" % - match.group(1), + match.group(1), **self.exception_kwargs) elif self.tag[-1].keyword != match.group(1): raise exceptions.SyntaxException( @@ -322,7 +322,7 @@ class Lexer(object): return True else: return False - + def match_end(self): match = self.match(r'\Z', re.S) if match: @@ -333,13 +333,13 @@ class Lexer(object): return True else: return False - + def match_text(self): match = self.match(r""" (.*?) # anything, followed by: ( - (?<=\n)(?=[ \t]*(?=%|\#\#)) # an eval or line-based - # comment preceded by a + (?<=\n)(?=[ \t]*(?=%|\#\#)) # an eval or line-based + # comment preceded by a # consumed newline and whitespace | (?=\${) # an expression @@ -353,7 +353,7 @@ class Lexer(object): | \Z # end of string )""", re.X | re.S) - + if match: text = match.group(1) if text: @@ -361,23 +361,23 @@ class Lexer(object): return True else: return False - + def match_python_block(self): match = self.match(r"<%(!)?") if match: line, pos = self.matched_lineno, self.matched_charpos text, end = self.parse_until_text(r'%>') - # the trailing newline helps + # the trailing newline helps # compiler.parse() not complain about indentation - text = adjust_whitespace(text) + "\n" + text = adjust_whitespace(text) + "\n" self.append_node( - parsetree.Code, - text, + parsetree.Code, + text, match.group(1)=='!', lineno=line, pos=pos) return True else: return False - + def match_expression(self): match = self.match(r"\${") if match: @@ -389,8 +389,8 @@ class Lexer(object): escapes = "" text = text.replace('\r\n', '\n') self.append_node( - parsetree.Expression, - text, escapes.strip(), + parsetree.Expression, + text, escapes.strip(), lineno=line, pos=pos) return True else: @@ -407,22 +407,22 @@ class Lexer(object): m2 = re.match(r'(end)?(\w+)\s*(.*)', text) if not m2: raise exceptions.SyntaxException( - "Invalid control line: '%s'" % - text, + "Invalid control line: '%s'" % + text, **self.exception_kwargs) isend, keyword = m2.group(1, 2) isend = (isend is not None) - + if isend: if not len(self.control_line): raise exceptions.SyntaxException( - "No starting keyword '%s' for '%s'" % - (keyword, text), + "No starting keyword '%s' for '%s'" % + (keyword, text), **self.exception_kwargs) elif self.control_line[-1].keyword != keyword: raise exceptions.SyntaxException( - "Keyword '%s' doesn't match keyword '%s'" % - (text, self.control_line[-1].keyword), + "Keyword '%s' doesn't match keyword '%s'" % + (text, self.control_line[-1].keyword), **self.exception_kwargs) self.append_node(parsetree.ControlLine, keyword, isend, text) else: @@ -439,4 +439,4 @@ class Lexer(object): return True else: return False - + diff --git a/mako/lookup.py b/mako/lookup.py index 514ad9d..4d86696 100644 --- a/mako/lookup.py +++ b/mako/lookup.py @@ -12,13 +12,13 @@ try: import threading except: import dummy_threading as threading - + class TemplateCollection(object): """Represent a collection of :class:`.Template` objects, identifiable via URI. A :class:`.TemplateCollection` is linked to the usage of - all template tags that address other templates, such + all template tags that address other templates, such as ``<%include>``, ``<%namespace>``, and ``<%inherit>``. The ``file`` attribute of each of those tags refers to a string URI that is passed to that :class:`.Template` @@ -62,9 +62,9 @@ class TemplateCollection(object): def filename_to_uri(self, uri, filename): """Convert the given ``filename`` to a URI relative to this :class:`.TemplateCollection`.""" - + return uri - + def adjust_uri(self, uri, filename): """Adjust the given ``uri`` based on the calling ``filename``. @@ -78,7 +78,7 @@ class TemplateCollection(object): """ return uri - + class TemplateLookup(TemplateCollection): """Represent a collection of templates that locates template source files from the local filesystem. @@ -143,36 +143,36 @@ class TemplateLookup(TemplateCollection): to each new :class:`.Template`. """ - - def __init__(self, - directories=None, - module_directory=None, - filesystem_checks=True, - collection_size=-1, - format_exceptions=False, - error_handler=None, - disable_unicode=False, + + def __init__(self, + directories=None, + module_directory=None, + filesystem_checks=True, + collection_size=-1, + format_exceptions=False, + error_handler=None, + disable_unicode=False, bytestring_passthrough=False, - output_encoding=None, - encoding_errors='strict', + output_encoding=None, + encoding_errors='strict', cache_args=None, cache_impl='beaker', cache_enabled=True, - cache_type=None, - cache_dir=None, - cache_url=None, + cache_type=None, + cache_dir=None, + cache_url=None, - modulename_callable=None, + modulename_callable=None, module_writer=None, - default_filters=None, - buffer_filters=(), + default_filters=None, + buffer_filters=(), strict_undefined=False, - imports=None, + imports=None, enable_loop=True, - input_encoding=None, + input_encoding=None, preprocessor=None): - + self.directories = [posixpath.normpath(d) for d in util.to_list(directories, ()) ] @@ -192,22 +192,22 @@ class TemplateLookup(TemplateCollection): cache_args.setdefault('type', cache_type) self.template_args = { - 'format_exceptions':format_exceptions, - 'error_handler':error_handler, - 'disable_unicode':disable_unicode, + 'format_exceptions':format_exceptions, + 'error_handler':error_handler, + 'disable_unicode':disable_unicode, 'bytestring_passthrough':bytestring_passthrough, - 'output_encoding':output_encoding, + 'output_encoding':output_encoding, 'cache_impl':cache_impl, - 'encoding_errors':encoding_errors, - 'input_encoding':input_encoding, - 'module_directory':module_directory, + 'encoding_errors':encoding_errors, + 'input_encoding':input_encoding, + 'module_directory':module_directory, 'module_writer':module_writer, 'cache_args':cache_args, - 'cache_enabled':cache_enabled, - 'default_filters':default_filters, - 'buffer_filters':buffer_filters, + 'cache_enabled':cache_enabled, + 'default_filters':default_filters, + 'buffer_filters':buffer_filters, 'strict_undefined':strict_undefined, - 'imports':imports, + 'imports':imports, 'enable_loop':enable_loop, 'preprocessor':preprocessor} @@ -218,7 +218,7 @@ class TemplateLookup(TemplateCollection): self._collection = util.LRUCache(collection_size) self._uri_cache = util.LRUCache(collection_size) self._mutex = threading.Lock() - + def get_template(self, uri): """Return a :class:`.Template` object corresponding to the given ``uri``. @@ -244,7 +244,7 @@ class TemplateLookup(TemplateCollection): def adjust_uri(self, uri, relativeto): """Adjust the given ``uri`` based on the given relative URI.""" - + key = (uri, relativeto) if key in self._uri_cache: return self._uri_cache[key] @@ -258,8 +258,8 @@ class TemplateLookup(TemplateCollection): else: v = self._uri_cache[key] = uri return v - - + + def filename_to_uri(self, filename): """Convert the given ``filename`` to a URI relative to this :class:`.TemplateCollection`.""" @@ -270,25 +270,25 @@ class TemplateLookup(TemplateCollection): value = self._relativeize(filename) self._uri_cache[filename] = value return value - + def _relativeize(self, filename): - """Return the portion of a filename that is 'relative' + """Return the portion of a filename that is 'relative' to the directories in this lookup. - + """ - + filename = posixpath.normpath(filename) for dir in self.directories: if filename[0:len(dir)] == dir: return filename[len(dir):] else: return None - + def _load(self, filename, uri): self._mutex.acquire() try: try: - # try returning from collection one + # try returning from collection one # more time in case concurrent thread already loaded return self._collection[uri] except KeyError: @@ -301,19 +301,19 @@ class TemplateLookup(TemplateCollection): self._collection[uri] = template = Template( uri=uri, filename=posixpath.normpath(filename), - lookup=self, + lookup=self, module_filename=module_filename, **self.template_args) return template except: - # if compilation fails etc, ensure + # if compilation fails etc, ensure # template is removed from collection, # re-raise self._collection.pop(uri, None) raise finally: self._mutex.release() - + def _check(self, uri, template): if template.filename is None: return template @@ -331,7 +331,7 @@ class TemplateLookup(TemplateCollection): raise exceptions.TemplateLookupException( "Cant locate template for uri %r" % uri) - + def put_string(self, uri, text): """Place a new :class:`.Template` object into this :class:`.TemplateLookup`, based on the given string of @@ -339,11 +339,11 @@ class TemplateLookup(TemplateCollection): """ self._collection[uri] = Template( - text, - lookup=self, - uri=uri, + text, + lookup=self, + uri=uri, **self.template_args) - + def put_template(self, uri, template): """Place a new :class:`.Template` object into this :class:`.TemplateLookup`, based on the given @@ -351,4 +351,4 @@ class TemplateLookup(TemplateCollection): """ self._collection[uri] = template - + diff --git a/mako/parsetree.py b/mako/parsetree.py index e72ac04..9b488d3 100644 --- a/mako/parsetree.py +++ b/mako/parsetree.py @@ -8,7 +8,7 @@ from mako import exceptions, ast, util, filters import re - + class Node(object): """base class for a Node in the parse tree.""" @@ -17,15 +17,15 @@ class Node(object): self.lineno = lineno self.pos = pos self.filename = filename - + @property def exception_kwargs(self): - return {'source':self.source, 'lineno':self.lineno, + return {'source':self.source, 'lineno':self.lineno, 'pos':self.pos, 'filename':self.filename} - + def get_children(self): return [] - + def accept_visitor(self, visitor): def traverse(node): for n in node.get_children(): @@ -36,29 +36,29 @@ class Node(object): class TemplateNode(Node): """a 'container' node that stores the overall collection of nodes.""" - + def __init__(self, filename): super(TemplateNode, self).__init__('', 0, 0, filename) self.nodes = [] self.page_attributes = {} - + def get_children(self): return self.nodes - + def __repr__(self): return "TemplateNode(%s, %r)" % ( - util.sorted_dict_repr(self.page_attributes), + util.sorted_dict_repr(self.page_attributes), self.nodes) - + class ControlLine(Node): """defines a control line, a line-oriented python line or end tag. - + e.g.:: % if foo: (markup) % endif - + """ has_loop_context = False @@ -75,7 +75,7 @@ class ControlLine(Node): self._undeclared_identifiers = [] else: code = ast.PythonFragment(text, **self.exception_kwargs) - self._declared_identifiers = code.declared_identifiers + self._declared_identifiers = code.declared_identifiers self._undeclared_identifiers = code.undeclared_identifiers def get_children(self): @@ -86,50 +86,50 @@ class ControlLine(Node): def undeclared_identifiers(self): return self._undeclared_identifiers - + def is_ternary(self, keyword): """return true if the given keyword is a ternary keyword for this ControlLine""" - + return keyword in { 'if':set(['else', 'elif']), 'try':set(['except', 'finally']), 'for':set(['else']) }.get(self.keyword, []) - + def __repr__(self): return "ControlLine(%r, %r, %r, %r)" % ( - self.keyword, - self.text, - self.isend, + self.keyword, + self.text, + self.isend, (self.lineno, self.pos) ) class Text(Node): """defines plain text in the template.""" - + def __init__(self, content, **kwargs): super(Text, self).__init__(**kwargs) self.content = content - + def __repr__(self): return "Text(%r, %r)" % (self.content, (self.lineno, self.pos)) - + class Code(Node): """defines a Python code block, either inline or module level. - + e.g.:: inline: <% x = 12 %> - + module level: <%! import logger %> - + """ def __init__(self, text, ismodule, **kwargs): @@ -146,32 +146,32 @@ class Code(Node): def __repr__(self): return "Code(%r, %r, %r)" % ( - self.text, - self.ismodule, + self.text, + self.ismodule, (self.lineno, self.pos) ) - + class Comment(Node): """defines a comment line. - + # this is a comment - + """ - + def __init__(self, text, **kwargs): super(Comment, self).__init__(**kwargs) self.text = text def __repr__(self): return "Comment(%r, %r)" % (self.text, (self.lineno, self.pos)) - + class Expression(Node): """defines an inline expression. - + ${x+y} - + """ - + def __init__(self, text, escapes, **kwargs): super(Expression, self).__init__(**kwargs) self.text = text @@ -192,74 +192,74 @@ class Expression(Node): def __repr__(self): return "Expression(%r, %r, %r)" % ( - self.text, - self.escapes_code.args, + self.text, + self.escapes_code.args, (self.lineno, self.pos) ) - + class _TagMeta(type): """metaclass to allow Tag to produce a subclass according to its keyword""" - + _classmap = {} - + def __init__(cls, clsname, bases, dict): if cls.__keyword__ is not None: cls._classmap[cls.__keyword__] = cls super(_TagMeta, cls).__init__(clsname, bases, dict) - + def __call__(cls, keyword, attributes, **kwargs): if ":" in keyword: ns, defname = keyword.split(':') - return type.__call__(CallNamespaceTag, ns, defname, + return type.__call__(CallNamespaceTag, ns, defname, attributes, **kwargs) try: cls = _TagMeta._classmap[keyword] except KeyError: raise exceptions.CompileException( - "No such tag: '%s'" % keyword, - source=kwargs['source'], - lineno=kwargs['lineno'], - pos=kwargs['pos'], + "No such tag: '%s'" % keyword, + source=kwargs['source'], + lineno=kwargs['lineno'], + pos=kwargs['pos'], filename=kwargs['filename'] ) return type.__call__(cls, keyword, attributes, **kwargs) - + class Tag(Node): """abstract base class for tags. - + <%sometag/> - + <%someothertag> stuff </%someothertag> - + """ - + __metaclass__ = _TagMeta __keyword__ = None - - def __init__(self, keyword, attributes, expressions, + + def __init__(self, keyword, attributes, expressions, nonexpressions, required, **kwargs): """construct a new Tag instance. - + this constructor not called directly, and is only called by subclasses. - + :param keyword: the tag keyword - + :param attributes: raw dictionary of attribute key/value pairs - - :param expressions: a set of identifiers that are legal attributes, + + :param expressions: a set of identifiers that are legal attributes, which can also contain embedded expressions - - :param nonexpressions: a set of identifiers that are legal + + :param nonexpressions: a set of identifiers that are legal attributes, which cannot contain embedded expressions - + :param \**kwargs: other arguments passed to the Node superclass (lineno, pos) - + """ super(Tag, self).__init__(**kwargs) self.keyword = keyword @@ -268,18 +268,18 @@ class Tag(Node): missing = [r for r in required if r not in self.parsed_attributes] if len(missing): raise exceptions.CompileException( - "Missing attribute(s): %s" % - ",".join([repr(m) for m in missing]), + "Missing attribute(s): %s" % + ",".join([repr(m) for m in missing]), **self.exception_kwargs) self.parent = None self.nodes = [] - + def is_root(self): return self.parent is None - + def get_children(self): return self.nodes - + def _parse_attributes(self, expressions, nonexpressions): undeclared_identifiers = set() self.parsed_attributes = {} @@ -293,8 +293,8 @@ class Tag(Node): code = ast.PythonCode(m.group(1).rstrip(), **self.exception_kwargs) # we aren't discarding "declared_identifiers" here, - # which we do so that list comprehension-declared - # variables aren't counted. As yet can't find a + # which we do so that list comprehension-declared + # variables aren't counted. As yet can't find a # condition that requires it here. undeclared_identifiers = \ undeclared_identifiers.union( @@ -308,13 +308,13 @@ class Tag(Node): if re.search(r'\${.+?}', self.attributes[key]): raise exceptions.CompileException( "Attibute '%s' in tag '%s' does not allow embedded " - "expressions" % (key, self.keyword), + "expressions" % (key, self.keyword), **self.exception_kwargs) self.parsed_attributes[key] = repr(self.attributes[key]) else: raise exceptions.CompileException( "Invalid attribute for tag '%s': '%s'" % - (self.keyword, key), + (self.keyword, key), **self.exception_kwargs) self.expression_undeclared_identifiers = undeclared_identifiers @@ -325,21 +325,21 @@ class Tag(Node): return self.expression_undeclared_identifiers def __repr__(self): - return "%s(%r, %s, %r, %r)" % (self.__class__.__name__, - self.keyword, + return "%s(%r, %s, %r, %r)" % (self.__class__.__name__, + self.keyword, util.sorted_dict_repr(self.attributes), - (self.lineno, self.pos), + (self.lineno, self.pos), self.nodes ) - + class IncludeTag(Tag): __keyword__ = 'include' def __init__(self, keyword, attributes, **kwargs): super(IncludeTag, self).__init__( - keyword, - attributes, - ('file', 'import', 'args'), + keyword, + attributes, + ('file', 'import', 'args'), (), ('file',), **kwargs) self.page_args = ast.PythonCode( "__DUMMY(%s)" % attributes.get('args', ''), @@ -354,18 +354,18 @@ class IncludeTag(Tag): difference(self.page_args.declared_identifiers) return identifiers.union(super(IncludeTag, self). undeclared_identifiers()) - + class NamespaceTag(Tag): __keyword__ = 'namespace' def __init__(self, keyword, attributes, **kwargs): super(NamespaceTag, self).__init__( - keyword, attributes, - ('file',), + keyword, attributes, + ('file',), ('name','inheritable', - 'import','module'), + 'import','module'), (), **kwargs) - + self.name = attributes.get('name', '__anon_%s' % hex(abs(id(self)))) if not 'name' in attributes and not 'import' in attributes: raise exceptions.CompileException( @@ -386,13 +386,13 @@ class TextTag(Tag): def __init__(self, keyword, attributes, **kwargs): super(TextTag, self).__init__( - keyword, - attributes, (), + keyword, + attributes, (), ('filter'), (), **kwargs) self.filter_args = ast.ArgumentList( - attributes.get('filter', ''), + attributes.get('filter', ''), **self.exception_kwargs) - + class DefTag(Tag): __keyword__ = 'def' @@ -402,23 +402,23 @@ class DefTag(Tag): super(DefTag, self).__init__( - keyword, - attributes, - expressions, - ('name','filter', 'decorator'), - ('name',), + keyword, + attributes, + expressions, + ('name','filter', 'decorator'), + ('name',), **kwargs) name = attributes['name'] if re.match(r'^[\w_]+$',name): raise exceptions.CompileException( - "Missing parenthesis in %def", + "Missing parenthesis in %def", **self.exception_kwargs) - self.function_decl = ast.FunctionDecl("def " + name + ":pass", + self.function_decl = ast.FunctionDecl("def " + name + ":pass", **self.exception_kwargs) self.name = self.function_decl.funcname self.decorator = attributes.get('decorator', '') self.filter_args = ast.ArgumentList( - attributes.get('filter', ''), + attributes.get('filter', ''), **self.exception_kwargs) is_anonymous = False @@ -455,29 +455,29 @@ class BlockTag(Tag): c for c in attributes if c.startswith('cache_')] super(BlockTag, self).__init__( - keyword, - attributes, + keyword, + attributes, expressions, - ('name','filter', 'decorator'), - (), + ('name','filter', 'decorator'), + (), **kwargs) name = attributes.get('name') if name and not re.match(r'^[\w_]+$',name): raise exceptions.CompileException( - "%block may not specify an argument signature", + "%block may not specify an argument signature", **self.exception_kwargs) if not name and attributes.get('args', None): raise exceptions.CompileException( "Only named %blocks may specify args", **self.exception_kwargs ) - self.body_decl = ast.FunctionArgs(attributes.get('args', ''), + self.body_decl = ast.FunctionArgs(attributes.get('args', ''), **self.exception_kwargs) self.name = name self.decorator = attributes.get('decorator', '') self.filter_args = ast.ArgumentList( - attributes.get('filter', ''), + attributes.get('filter', ''), **self.exception_kwargs) @@ -509,11 +509,11 @@ class CallTag(Tag): __keyword__ = 'call' def __init__(self, keyword, attributes, **kwargs): - super(CallTag, self).__init__(keyword, attributes, + super(CallTag, self).__init__(keyword, attributes, ('args'), ('expr',), ('expr',), **kwargs) self.expression = attributes['expr'] self.code = ast.PythonCode(self.expression, **self.exception_kwargs) - self.body_decl = ast.FunctionArgs(attributes.get('args', ''), + self.body_decl = ast.FunctionArgs(attributes.get('args', ''), **self.exception_kwargs) def declared_identifiers(self): @@ -527,23 +527,23 @@ class CallNamespaceTag(Tag): def __init__(self, namespace, defname, attributes, **kwargs): super(CallNamespaceTag, self).__init__( - namespace + ":" + defname, - attributes, - tuple(attributes.keys()) + ('args', ), - (), - (), + namespace + ":" + defname, + attributes, + tuple(attributes.keys()) + ('args', ), + (), + (), **kwargs) - + self.expression = "%s.%s(%s)" % ( - namespace, - defname, + namespace, + defname, ",".join(["%s=%s" % (k, v) for k, v in - self.parsed_attributes.iteritems() + self.parsed_attributes.iteritems() if k != 'args']) ) self.code = ast.PythonCode(self.expression, **self.exception_kwargs) self.body_decl = ast.FunctionArgs( - attributes.get('args', ''), + attributes.get('args', ''), **self.exception_kwargs) def declared_identifiers(self): @@ -558,7 +558,7 @@ class InheritTag(Tag): def __init__(self, keyword, attributes, **kwargs): super(InheritTag, self).__init__( - keyword, attributes, + keyword, attributes, ('file',), (), ('file',), **kwargs) class PageTag(Tag): @@ -569,13 +569,13 @@ class PageTag(Tag): c for c in attributes if c.startswith('cache_')] super(PageTag, self).__init__( - keyword, - attributes, + keyword, + attributes, expressions, - (), - (), + (), + (), **kwargs) - self.body_decl = ast.FunctionArgs(attributes.get('args', ''), + self.body_decl = ast.FunctionArgs(attributes.get('args', ''), **self.exception_kwargs) self.filter_args = ast.ArgumentList( attributes.get('expression_filter', ''), @@ -583,5 +583,5 @@ class PageTag(Tag): def declared_identifiers(self): return self.body_decl.argnames - - + + diff --git a/mako/pygen.py b/mako/pygen.py index 76e7f6a..e946de5 100644 --- a/mako/pygen.py +++ b/mako/pygen.py @@ -14,48 +14,48 @@ class PythonPrinter(object): def __init__(self, stream): # indentation counter self.indent = 0 - - # a stack storing information about why we incremented + + # a stack storing information about why we incremented # the indentation counter, to help us determine if we # should decrement it self.indent_detail = [] - + # the string of whitespace multiplied by the indent # counter to produce a line self.indentstring = " " - + # the stream we are writing to self.stream = stream - + # a list of lines that represents a buffered "block" of code, - # which can be later printed relative to an indent level + # which can be later printed relative to an indent level self.line_buffer = [] - + self.in_indent_lines = False - + self._reset_multi_line_flags() def write(self, text): self.stream.write(text) - + def write_indented_block(self, block): """print a line or lines of python which already contain indentation. - + The indentation of the total block of lines will be adjusted to that of - the current indent level.""" + the current indent level.""" self.in_indent_lines = False for l in re.split(r'\r?\n', block): self.line_buffer.append(l) - + def writelines(self, *lines): """print a series of lines of python.""" for line in lines: self.writeline(line) - + def writeline(self, line): """print a line of python, indenting it according to the current indent level. - + this also adjusts the indentation counter according to the content of the line. @@ -65,7 +65,7 @@ class PythonPrinter(object): self._flush_adjusted_lines() self.in_indent_lines = True - if (line is None or + if (line is None or re.match(r"^\s*#",line) or re.match(r"^\s*$", line) ): @@ -74,30 +74,30 @@ class PythonPrinter(object): hastext = True is_comment = line and len(line) and line[0] == '#' - + # see if this line should decrease the indentation level - if (not is_comment and + if (not is_comment and (not hastext or self._is_unindentor(line)) ): - - if self.indent > 0: + + if self.indent > 0: self.indent -=1 # if the indent_detail stack is empty, the user # probably put extra closures - the resulting - # module wont compile. - if len(self.indent_detail) == 0: + # module wont compile. + if len(self.indent_detail) == 0: raise exceptions.SyntaxException( "Too many whitespace closures") self.indent_detail.pop() - + if line is None: return - + # write the line self.stream.write(self._indent_line(line) + "\n") - + # see if this line should increase the indentation level. - # note that a line can both decrase (before printing) and + # note that a line can both decrase (before printing) and # then increase (after printing) the indentation level. if re.search(r":[ \t]*(?:#.*)?$", line): @@ -125,53 +125,53 @@ class PythonPrinter(object): def close(self): """close this printer, flushing any remaining lines.""" self._flush_adjusted_lines() - + def _is_unindentor(self, line): - """return true if the given line is an 'unindentor', + """return true if the given line is an 'unindentor', relative to the last 'indent' event received. - + """ - + # no indentation detail has been pushed on; return False - if len(self.indent_detail) == 0: + if len(self.indent_detail) == 0: return False indentor = self.indent_detail[-1] - - # the last indent keyword we grabbed is not a + + # the last indent keyword we grabbed is not a # compound statement keyword; return False - if indentor is None: + if indentor is None: return False - + # if the current line doesnt have one of the "unindentor" keywords, # return False match = re.match(r"^\s*(else|elif|except|finally).*\:", line) - if not match: + if not match: return False - + # whitespace matches up, we have a compound indentor, # and this line has an unindentor, this # is probably good enough return True - + # should we decide that its not good enough, heres # more stuff to check. #keyword = match.group(1) - - # match the original indent keyword + + # match the original indent keyword #for crit in [ # (r'if|elif', r'else|elif'), # (r'try', r'except|finally|else'), # (r'while|for', r'else'), #]: - # if re.match(crit[0], indentor) and re.match(crit[1], keyword): + # if re.match(crit[0], indentor) and re.match(crit[1], keyword): # return True - + #return False - + def _indent_line(self, line, stripspace=''): """indent the given line according to the current indent level. - + stripspace is a string of space that will be truncated from the start of the line before indenting.""" @@ -183,7 +183,7 @@ class PythonPrinter(object): or triple-quoted section.""" self.backslashed, self.triplequoted = False, False - + def _in_multi_line(self, line): """return true if the given line is part of a multi-line block, via backslash or triple-quote.""" @@ -193,24 +193,24 @@ class PythonPrinter(object): # guard against the possibility of modifying the space inside of # a literal multiline string with unfortunately placed # whitespace - - current_state = (self.backslashed or self.triplequoted) - + + current_state = (self.backslashed or self.triplequoted) + if re.search(r"\\$", line): self.backslashed = True else: self.backslashed = False - + triples = len(re.findall(r"\"\"\"|\'\'\'", line)) if triples == 1 or triples % 2 != 0: self.triplequoted = not self.triplequoted - + return current_state def _flush_adjusted_lines(self): stripspace = None self._reset_multi_line_flags() - + for entry in self.line_buffer: if self._in_multi_line(entry): self.stream.write(entry + "\n") @@ -219,32 +219,32 @@ class PythonPrinter(object): if stripspace is None and re.search(r"^[ \t]*[^# \t]", entry): stripspace = re.match(r"^([ \t]*)", entry).group(1) self.stream.write(self._indent_line(entry, stripspace) + "\n") - + self.line_buffer = [] self._reset_multi_line_flags() def adjust_whitespace(text): """remove the left-whitespace margin of a block of Python code.""" - + state = [False, False] (backslashed, triplequoted) = (0, 1) def in_multi_line(line): start_state = (state[backslashed] or state[triplequoted]) - + if re.search(r"\\$", line): state[backslashed] = True else: state[backslashed] = False - + def match(reg, t): m = re.match(reg, t) if m: return m, t[len(m.group(0)):] else: return None, t - + while line: if state[triplequoted]: m, line = match(r"%s" % state[triplequoted], line) @@ -256,14 +256,14 @@ def adjust_whitespace(text): m, line = match(r'#', line) if m: return start_state - + m, line = match(r"\"\"\"|\'\'\'", line) if m: state[triplequoted] = m.group(0) continue m, line = match(r".*?(?=\"\"\"|\'\'\'|#|$)", line) - + return start_state def _indent_line(line, stripspace = ''): diff --git a/mako/pyparser.py b/mako/pyparser.py index cd7d04f..95ec86a 100644 --- a/mako/pyparser.py +++ b/mako/pyparser.py @@ -15,17 +15,17 @@ from mako import exceptions, util import operator if util.py3k: - # words that cannot be assigned to (notably + # words that cannot be assigned to (notably # smaller than the total keys in __builtins__) reserved = set(['True', 'False', 'None', 'print']) # the "id" attribute on a function node arg_id = operator.attrgetter('arg') else: - # words that cannot be assigned to (notably + # words that cannot be assigned to (notably # smaller than the total keys in __builtins__) reserved = set(['True', 'False', 'None']) - + # the "id" attribute on a function node arg_id = operator.attrgetter('id') @@ -42,7 +42,7 @@ except ImportError: def parse(code, mode='exec', **exception_kwargs): """Parse an expression into AST""" - + try: if _ast: @@ -54,8 +54,8 @@ def parse(code, mode='exec', **exception_kwargs): except Exception, e: raise exceptions.SyntaxException( "(%s) %s (%r)" % ( - e.__class__.__name__, - e, + e.__class__.__name__, + e, code[0:50] ), **exception_kwargs) diff --git a/mako/runtime.py b/mako/runtime.py index 640bc89..f890c80 100644 --- a/mako/runtime.py +++ b/mako/runtime.py @@ -19,21 +19,21 @@ class Context(object): :class:`.Context`. """ - + def __init__(self, buffer, **data): self._buffer_stack = [buffer] - + self._data = data self._kwargs = data.copy() self._with_template = None self._outputting_as_unicode = None self.namespaces = {} - - # "capture" function which proxies to the + + # "capture" function which proxies to the # generic "capture" function self._data['capture'] = util.partial(capture, self) - + # "caller" stack used by def calls with content self.caller_stack = self._data['caller'] = CallerStack() @@ -42,7 +42,7 @@ class Context(object): illegal_names = t.reserved_names.intersection(self._data) if illegal_names: raise exceptions.NameConflictError( - "Reserved words passed to render(): %s" % + "Reserved words passed to render(): %s" % ", ".join(illegal_names)) @property @@ -52,7 +52,7 @@ class Context(object): """ return self._with_template.lookup - + @property def kwargs(self): """Return the dictionary of keyword arguments associated with this @@ -60,25 +60,25 @@ class Context(object): """ return self._kwargs.copy() - + def push_caller(self, caller): """Push a ``caller`` callable onto the callstack for this :class:`.Context`.""" - - + + self.caller_stack.append(caller) - + def pop_caller(self): """Pop a ``caller`` callable onto the callstack for this :class:`.Context`.""" del self.caller_stack[-1] - + def keys(self): """Return a list of all names established in this :class:`.Context`.""" return self._data.keys() - + def __getitem__(self, key): if key in self._data: return self._data[key] @@ -88,43 +88,43 @@ class Context(object): def _push_writer(self): """push a capturing buffer onto this Context and return the new writer function.""" - + buf = util.FastEncodingBuffer() self._buffer_stack.append(buf) return buf.write def _pop_buffer_and_writer(self): - """pop the most recent capturing buffer from this Context + """pop the most recent capturing buffer from this Context and return the current writer after the pop. - + """ buf = self._buffer_stack.pop() return buf, self._buffer_stack[-1].write - + def _push_buffer(self): """push a capturing buffer onto this Context.""" - + self._push_writer() - + def _pop_buffer(self): """pop the most recent capturing buffer from this Context.""" - + return self._buffer_stack.pop() - + def get(self, key, default=None): """Return a value from this :class:`.Context`.""" - - return self._data.get(key, + + return self._data.get(key, __builtin__.__dict__.get(key, default) ) - + def write(self, string): """Write a string to this :class:`.Context` object's underlying output buffer.""" - + self._buffer_stack[-1].write(string) - + def writer(self): """Return the current writer function.""" @@ -140,17 +140,17 @@ class Context(object): c.namespaces = self.namespaces c.caller_stack = self.caller_stack return c - + def locals_(self, d): """Create a new :class:`.Context` with a copy of this :class:`.Context`'s current state, updated with the given dictionary.""" - + if len(d) == 0: return self c = self._copy() c._data.update(d) return c - + def _clean_inheritance_tokens(self): """create a new copy of this :class:`.Context`. with tokens related to inheritance state removed.""" @@ -180,8 +180,8 @@ class CallerStack(list): return frame def _pop_frame(self): self.nextcaller = self.pop() - - + + class Undefined(object): """Represents an undefined value in a template. @@ -311,8 +311,8 @@ class _NSAttr(object): return getattr(ns.module, key) else: ns = ns.inherits - raise AttributeError(key) - + raise AttributeError(key) + class Namespace(object): """Provides access to collections of rendering methods, which can be local, from other templates, or from imported modules. @@ -328,9 +328,9 @@ class Namespace(object): described here. """ - - def __init__(self, name, context, - callables=None, inherits=None, + + def __init__(self, name, context, + callables=None, inherits=None, populate_self=True, calling_uri=None): self.name = name self.context = context @@ -365,7 +365,7 @@ class Namespace(object): one-another. """ - + filename = None """The path of the filesystem file used for this :class:`.Namespace`'s module or template. @@ -376,7 +376,7 @@ class Namespace(object): template file location. """ - + uri = None """The URI for this :class:`.Namespace`'s template. @@ -424,12 +424,12 @@ class Namespace(object): if key in self.context.namespaces: return self.context.namespaces[key] else: - ns = TemplateNamespace(uri, self.context._copy(), - templateuri=uri, - calling_uri=self._templateuri) + ns = TemplateNamespace(uri, self.context._copy(), + templateuri=uri, + calling_uri=self._templateuri) self.context.namespaces[key] = ns return ns - + def get_template(self, uri): """Return a :class:`.Template` from the given ``uri``. @@ -438,7 +438,7 @@ class Namespace(object): """ return _lookup_template(self.context, uri, self._templateuri) - + def get_cached(self, key, **kwargs): """Return a value from the :class:`.Cache` referenced by this :class:`.Namespace` object's :class:`.Template`. @@ -452,7 +452,7 @@ class Namespace(object): """ return self.cache.get(key, **kwargs) - + @property def cache(self): """Return the :class:`.Cache` object referenced @@ -461,12 +461,12 @@ class Namespace(object): """ return self.template.cache - + def include_file(self, uri, **kwargs): """Include a file at the given ``uri``.""" - + _include_file(self.context, uri, self._templateuri, **kwargs) - + def _populate(self, d, l): for ident in l: if ident == '*': @@ -474,7 +474,7 @@ class Namespace(object): d[k] = v else: d[ident] = getattr(self, ident) - + def _get_star(self): if self.callables: for key in self.callables: @@ -487,7 +487,7 @@ class Namespace(object): val = getattr(self.inherits, key) else: raise AttributeError( - "Namespace '%s' has no member '%s'" % + "Namespace '%s' has no member '%s'" % (self.name, key)) setattr(self, key, val) return val @@ -495,8 +495,8 @@ class Namespace(object): class TemplateNamespace(Namespace): """A :class:`.Namespace` specific to a :class:`.Template` instance.""" - def __init__(self, name, context, template=None, templateuri=None, - callables=None, inherits=None, + def __init__(self, name, context, template=None, templateuri=None, + callables=None, inherits=None, populate_self=True, calling_uri=None): self.name = name self.context = context @@ -505,7 +505,7 @@ class TemplateNamespace(Namespace): self.callables = dict([(c.func_name, c) for c in callables]) if templateuri is not None: - self.template = _lookup_template(context, templateuri, + self.template = _lookup_template(context, templateuri, calling_uri) self._templateuri = self.template.module._template_uri elif template is not None: @@ -516,7 +516,7 @@ class TemplateNamespace(Namespace): if populate_self: lclcallable, lclcontext = \ - _populate_self_namespace(context, self.template, + _populate_self_namespace(context, self.template, self_ns=self) @property @@ -569,7 +569,7 @@ class TemplateNamespace(Namespace): else: raise AttributeError( - "Namespace '%s' has no member '%s'" % + "Namespace '%s' has no member '%s'" % (self.name, key)) setattr(self, key, val) return val @@ -577,8 +577,8 @@ class TemplateNamespace(Namespace): class ModuleNamespace(Namespace): """A :class:`.Namespace` specific to a Python module instance.""" - def __init__(self, name, context, module, - callables=None, inherits=None, + def __init__(self, name, context, module, + callables=None, inherits=None, populate_self=True, calling_uri=None): self.name = name self.context = context @@ -619,7 +619,7 @@ class ModuleNamespace(Namespace): val = getattr(self.inherits, key) else: raise AttributeError( - "Namespace '%s' has no member '%s'" % + "Namespace '%s' has no member '%s'" % (self.name, key)) setattr(self, key, val) return val @@ -631,7 +631,7 @@ def supports_caller(func): See the example in :ref:`namespaces_python_modules`. """ - + def wrap_stackframe(context, *args, **kwargs): context.caller_stack._push_frame() try: @@ -639,7 +639,7 @@ def supports_caller(func): finally: context.caller_stack._pop_frame() return wrap_stackframe - + def capture(context, callable_, *args, **kwargs): """Execute the given template def, capturing the output into a buffer. @@ -647,7 +647,7 @@ def capture(context, callable_, *args, **kwargs): See the example in :ref:`namespaces_python_modules`. """ - + if not callable(callable_): raise exceptions.RuntimeException( "capture() function expects a callable as " @@ -673,7 +673,7 @@ def _decorate_toplevel(fn): return fn(y)(context, *args, **kw) return go return decorate_render - + def _decorate_inline(context, fn): def decorate_render(render_fn): dec = fn(render_fn) @@ -681,17 +681,17 @@ def _decorate_inline(context, fn): return dec(context, *args, **kw) return go return decorate_render - + def _include_file(context, uri, calling_uri, **kwargs): """locate the template from the given uri and include it in the current output.""" - + template = _lookup_template(context, uri, calling_uri) (callable_, ctx) = _populate_self_namespace( - context._clean_inheritance_tokens(), + context._clean_inheritance_tokens(), template) callable_(ctx, **_kwargs_for_include(callable_, context._data, **kwargs)) - + def _inherit_from(context, uri, calling_uri): """called by the _inherit method in template modules to set up the inheritance chain at the start of a template's @@ -705,9 +705,9 @@ def _inherit_from(context, uri, calling_uri): while ih.inherits is not None: ih = ih.inherits lclcontext = context.locals_({'next':ih}) - ih.inherits = TemplateNamespace("self:%s" % template.uri, - lclcontext, - template = template, + ih.inherits = TemplateNamespace("self:%s" % template.uri, + lclcontext, + template = template, populate_self=False) context._data['parent'] = lclcontext._data['local'] = ih.inherits callable_ = getattr(template.module, '_mako_inherit', None) @@ -725,7 +725,7 @@ def _lookup_template(context, uri, relativeto): lookup = context._with_template.lookup if lookup is None: raise exceptions.TemplateLookupException( - "Template '%s' has no TemplateLookup associated" % + "Template '%s' has no TemplateLookup associated" % context._with_template.uri) uri = lookup.adjust_uri(uri, relativeto) try: @@ -735,8 +735,8 @@ def _lookup_template(context, uri, relativeto): def _populate_self_namespace(context, template, self_ns=None): if self_ns is None: - self_ns = TemplateNamespace('self:%s' % template.uri, - context, template=template, + self_ns = TemplateNamespace('self:%s' % template.uri, + context, template=template, populate_self=False) context._data['self'] = context._data['local'] = self_ns if hasattr(template.module, '_mako_inherit'): @@ -746,7 +746,7 @@ def _populate_self_namespace(context, template, self_ns=None): return (template.callable_, context) def _render(template, callable_, args, data, as_unicode=False): - """create a Context and return the string + """create a Context and return the string output of the given template and template callable.""" if as_unicode: @@ -755,14 +755,14 @@ def _render(template, callable_, args, data, as_unicode=False): buf = util.StringIO() else: buf = util.FastEncodingBuffer( - unicode=as_unicode, - encoding=template.output_encoding, + unicode=as_unicode, + encoding=template.output_encoding, errors=template.encoding_errors) context = Context(buf, **data) context._outputting_as_unicode = as_unicode context._set_with_template(template) - - _render_context(template, callable_, context, *args, + + _render_context(template, callable_, context, *args, **_kwargs_for_callable(callable_, data)) return context._pop_buffer().getvalue() @@ -771,7 +771,7 @@ def _kwargs_for_callable(callable_, data): # for normal pages, **pageargs is usually present if argspec[2]: return data - + # for rendering defs from the top level, figure out the args namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None] kwargs = {} @@ -787,10 +787,10 @@ def _kwargs_for_include(callable_, data, **kwargs): if arg != 'context' and arg in data and arg not in kwargs: kwargs[arg] = data[arg] return kwargs - + def _render_context(tmpl, callable_, context, *args, **kwargs): import mako.template as template - # create polymorphic 'self' namespace for this + # create polymorphic 'self' namespace for this # template with possibly updated context if not isinstance(tmpl, template.DefTemplate): # if main render method, call from the base of the inheritance stack @@ -800,7 +800,7 @@ def _render_context(tmpl, callable_, context, *args, **kwargs): # otherwise, call the actual rendering method specified (inherit, lclcontext) = _populate_self_namespace(context, tmpl.parent) _exec_template(callable_, context, args=args, kwargs=kwargs) - + def _exec_template(callable_, context, args=None, kwargs=None): """execute a rendering callable given the callable, a Context, and optional explicit arguments @@ -817,7 +817,7 @@ def _exec_template(callable_, context, args=None, kwargs=None): callable_(context, *args, **kwargs) except Exception, e: _render_error(template, context, e) - except: + except: e = sys.exc_info()[0] _render_error(template, context, e) else: @@ -836,6 +836,6 @@ def _render_error(template, context, error): context._buffer_stack[:] = [util.FastEncodingBuffer( error_template.output_encoding, error_template.encoding_errors)] - + context._set_with_template(error_template) error_template.render_context(context, error=error) diff --git a/mako/template.py b/mako/template.py index 7ab6a46..b069139 100644 --- a/mako/template.py +++ b/mako/template.py @@ -11,7 +11,7 @@ from mako.lexer import Lexer from mako import runtime, util, exceptions, codegen, cache import os, re, shutil, stat, sys, tempfile, types, weakref - + class Template(object): """Represents a compiled template. @@ -86,7 +86,7 @@ class Template(object): :param enable_loop: When ``True``, enable the ``loop`` context variable. This can be set to ``False`` to support templates that may be making usage of the name "``loop``". Individual templates can - re-enable the "loop" context by placing the directive + re-enable the "loop" context by placing the directive ``enable_loop="True"`` inside the ``<%page>`` tag -- see :ref:`migrating_loop`. @@ -172,7 +172,7 @@ class Template(object): result of the callable will be used as the template source code. - :param strict_undefined: Replaces the automatic usage of + :param strict_undefined: Replaces the automatic usage of ``UNDEFINED`` for any undeclared variables not located in the :class:`.Context` with an immediate raise of ``NameError``. The advantage is immediate reporting of @@ -190,31 +190,31 @@ class Template(object): """ - def __init__(self, - text=None, - filename=None, - uri=None, - format_exceptions=False, - error_handler=None, - lookup=None, - output_encoding=None, - encoding_errors='strict', - module_directory=None, + def __init__(self, + text=None, + filename=None, + uri=None, + format_exceptions=False, + error_handler=None, + lookup=None, + output_encoding=None, + encoding_errors='strict', + module_directory=None, cache_args=None, cache_impl='beaker', cache_enabled=True, - cache_type=None, - cache_dir=None, - cache_url=None, - module_filename=None, - input_encoding=None, + cache_type=None, + cache_dir=None, + cache_url=None, + module_filename=None, + input_encoding=None, disable_unicode=False, module_writer=None, - bytestring_passthrough=False, - default_filters=None, - buffer_filters=(), + bytestring_passthrough=False, + default_filters=None, + buffer_filters=(), strict_undefined=False, - imports=None, + imports=None, enable_loop=True, preprocessor=None): if uri: @@ -264,10 +264,10 @@ class Template(object): else: self.default_filters = default_filters self.buffer_filters = buffer_filters - + self.imports = imports self.preprocessor = preprocessor - + # if plain text, compile code in memory only if text is not None: (code, module) = _compile_text(self, text, filename) @@ -282,7 +282,7 @@ class Template(object): elif module_directory is not None: path = os.path.abspath( os.path.join( - os.path.normpath(module_directory), + os.path.normpath(module_directory), u_norm + ".py" ) ) @@ -314,7 +314,7 @@ class Template(object): else: return codegen.RESERVED_NAMES.difference(['loop']) - def _setup_cache_args(self, + def _setup_cache_args(self, cache_impl, cache_enabled, cache_args, cache_type, cache_dir, cache_url): self.cache_impl = cache_impl @@ -340,9 +340,9 @@ class Template(object): os.stat(path)[stat.ST_MTIME] < filemtime: data = util.read_file(filename) _compile_module_file( - self, + self, data, - filename, + filename, path, self.module_writer) module = util.load_module(self.module_id, path) @@ -350,9 +350,9 @@ class Template(object): if module._magic_number != codegen.MAGIC_NUMBER: data = util.read_file(filename) _compile_module_file( - self, - data, - filename, + self, + data, + filename, path, self.module_writer) module = util.load_module(self.module_id, path) @@ -363,26 +363,26 @@ class Template(object): # in memory data = util.read_file(filename) code, module = _compile_text( - self, - data, + self, + data, filename) self._source = None self._code = code ModuleInfo(module, None, self, filename, code, None) return module - + @property def source(self): """Return the template source code for this :class:`.Template`.""" - + return _get_module_info_from_callable(self.callable_).source @property def code(self): """Return the module source code for this :class:`.Template`.""" - + return _get_module_info_from_callable(self.callable_).code - + @util.memoized_property def cache(self): return cache.Cache(self) @@ -396,7 +396,7 @@ class Template(object): @property def cache_type(self): return self.cache_args['type'] - + def render(self, *args, **data): """Render the output of this template as a string. @@ -410,16 +410,16 @@ class Template(object): """ return runtime._render(self, self.callable_, args, data) - + def render_unicode(self, *args, **data): """Render the output of this template as a unicode object.""" - - return runtime._render(self, - self.callable_, - args, - data, + + return runtime._render(self, + self.callable_, + args, + data, as_unicode=True) - + def render_context(self, context, *args, **kwargs): """Render this :class:`.Template` with the given context. @@ -428,63 +428,63 @@ class Template(object): """ if getattr(context, '_with_template', None) is None: context._set_with_template(self) - runtime._render_context(self, - self.callable_, - context, - *args, + runtime._render_context(self, + self.callable_, + context, + *args, **kwargs) - + def has_def(self, name): return hasattr(self.module, "render_%s" % name) - + def get_def(self, name): """Return a def of this template as a :class:`.DefTemplate`.""" - + return DefTemplate(self, getattr(self.module, "render_%s" % name)) def _get_def_callable(self, name): return getattr(self.module, "render_%s" % name) - + @property - def last_modified(self): - return self.module._modified_time - + def last_modified(self): + return self.module._modified_time + class ModuleTemplate(Template): """A Template which is constructed given an existing Python module. - + e.g.:: - + t = Template("this is a template") f = file("mymodule.py", "w") f.write(t.code) f.close() - + import mymodule - + t = ModuleTemplate(mymodule) print t.render() - + """ - - def __init__(self, module, - module_filename=None, - template=None, - template_filename=None, - module_source=None, + + def __init__(self, module, + module_filename=None, + template=None, + template_filename=None, + module_source=None, template_source=None, - output_encoding=None, + output_encoding=None, encoding_errors='strict', - disable_unicode=False, + disable_unicode=False, bytestring_passthrough=False, format_exceptions=False, - error_handler=None, - lookup=None, + error_handler=None, + lookup=None, cache_args=None, cache_impl='beaker', cache_enabled=True, - cache_type=None, - cache_dir=None, - cache_url=None, + cache_type=None, + cache_dir=None, + cache_url=None, ): self.module_id = re.sub(r'\W', "_", module._template_uri) self.uri = module._template_uri @@ -506,13 +506,13 @@ class ModuleTemplate(Template): self.module = module self.filename = template_filename - ModuleInfo(module, - module_filename, - self, - template_filename, - module_source, + ModuleInfo(module, + module_filename, + self, + template_filename, + module_source, template_source) - + self.callable_ = self.module.render_body self.format_exceptions = format_exceptions self.error_handler = error_handler @@ -521,11 +521,11 @@ class ModuleTemplate(Template): cache_impl, cache_enabled, cache_args, cache_type, cache_dir, cache_url ) - + class DefTemplate(Template): """A :class:`.Template` which represents a callable def in a parent template.""" - + def __init__(self, parent, callable_): self.parent = parent self.callable_ = callable_ @@ -545,16 +545,16 @@ class ModuleInfo(object): """Stores information about a module currently loaded into memory, provides reverse lookups of template source, module source code based on a module's identifier. - + """ _modules = weakref.WeakValueDictionary() - def __init__(self, - module, - module_filename, - template, - template_filename, - module_source, + def __init__(self, + module, + module_filename, + template, + template_filename, + module_source, template_source): self.module = module self.module_filename = module_filename @@ -564,14 +564,14 @@ class ModuleInfo(object): self._modules[module.__name__] = template._mmarker = self if module_filename: self._modules[module_filename] = self - + @property def code(self): if self.module_source is not None: return self.module_source else: return util.read_file(self.module_filename) - + @property def source(self): if self.template_source is not None: @@ -589,18 +589,18 @@ class ModuleInfo(object): return data def _compile(template, text, filename, generate_magic_comment): - lexer = Lexer(text, - filename, + lexer = Lexer(text, + filename, disable_unicode=template.disable_unicode, input_encoding=template.input_encoding, preprocessor=template.preprocessor) node = lexer.parse() - source = codegen.compile(node, - template.uri, + source = codegen.compile(node, + template.uri, filename, default_filters=template.default_filters, - buffer_filters=template.buffer_filters, - imports=template.imports, + buffer_filters=template.buffer_filters, + imports=template.imports, source_encoding=lexer.encoding, generate_magic_comment=generate_magic_comment, disable_unicode=template.disable_unicode, @@ -611,7 +611,7 @@ def _compile(template, text, filename, generate_magic_comment): def _compile_text(template, text, filename): identifier = template.module_id - source, lexer = _compile(template, text, filename, + source, lexer = _compile(template, text, filename, generate_magic_comment=template.disable_unicode) cid = identifier @@ -624,27 +624,27 @@ def _compile_text(template, text, filename): def _compile_module_file(template, text, filename, outputpath, module_writer): identifier = template.module_id - source, lexer = _compile(template, text, filename, + source, lexer = _compile(template, text, filename, generate_magic_comment=True) - + if isinstance(source, unicode): source = source.encode(lexer.encoding or 'ascii') if module_writer: module_writer(source, outputpath) else: - # make tempfiles in the same location as the ultimate + # make tempfiles in the same location as the ultimate # location. this ensures they're on the same filesystem, # avoiding synchronization issues. (dest, name) = tempfile.mkstemp(dir=os.path.dirname(outputpath)) - + os.write(dest, source) os.close(dest) shutil.move(name, outputpath) def _get_module_info_from_callable(callable_): return _get_module_info(callable_.func_globals['__name__']) - + def _get_module_info(filename): return ModuleInfo._modules[filename] - + diff --git a/mako/util.py b/mako/util.py index dc070a0..df4bf4b 100644 --- a/mako/util.py +++ b/mako/util.py @@ -35,7 +35,7 @@ except ImportError: if win32 or jython: time_func = time.clock else: - time_func = time.time + time_func = time.time def function_named(fn, name): """Return a function with a given __name__. @@ -87,13 +87,13 @@ class PluginLoader(object): else: import pkg_resources for impl in pkg_resources.iter_entry_points( - self.group, + self.group, name): self.impls[name] = impl.load return impl.load() else: raise exceptions.RuntimeException( - "Can't load plugin %s %s" % + "Can't load plugin %s %s" % (self.group, name)) def register(self, name, modulepath, objname): @@ -106,9 +106,9 @@ class PluginLoader(object): def verify_directory(dir): """create and/or verify a filesystem directory.""" - + tries = 0 - + while not os.path.exists(dir): try: tries += 1 @@ -170,16 +170,16 @@ class SetLikeDict(dict): """a dictionary that has some setlike methods on it""" def union(self, other): """produce a 'union' of this dict and another (at the key level). - + values in the second dict take precedence over that of the first""" x = SetLikeDict(**self) x.update(other) return x class FastEncodingBuffer(object): - """a very rudimentary buffer that is faster than StringIO, + """a very rudimentary buffer that is faster than StringIO, but doesn't crash on unicode data like cStringIO.""" - + def __init__(self, encoding=None, errors='strict', unicode=False): self.data = collections.deque() self.encoding = encoding @@ -190,11 +190,11 @@ class FastEncodingBuffer(object): self.unicode = unicode self.errors = errors self.write = self.data.append - + def truncate(self): self.data = collections.deque() self.write = self.data.append - + def getvalue(self): if self.encoding: return self.delim.join(self.data).encode(self.encoding, @@ -205,12 +205,12 @@ class FastEncodingBuffer(object): class LRUCache(dict): """A dictionary-like object that stores a limited number of items, discarding lesser used items periodically. - + this is a rewrite of LRUCache from Myghty to use a periodic timestamp-based paradigm so that synchronization is not really needed. the size management is inexact. """ - + class _Item(object): def __init__(self, key, value): self.key = key @@ -218,26 +218,26 @@ class LRUCache(dict): self.timestamp = time_func() def __repr__(self): return repr(self.value) - + def __init__(self, capacity, threshold=.5): self.capacity = capacity self.threshold = threshold - + def __getitem__(self, key): item = dict.__getitem__(self, key) item.timestamp = time_func() return item.value - + def values(self): return [i.value for i in dict.values(self)] - + def setdefault(self, key, value): if key in self: return self[key] else: self[key] = value return value - + def __setitem__(self, key, value): item = dict.get(self, key) if item is None: @@ -246,10 +246,10 @@ class LRUCache(dict): else: item.value = value self._manage_size() - + def _manage_size(self): while len(self) > self.capacity + self.capacity * self.threshold: - bytime = sorted(dict.values(self), + bytime = sorted(dict.values(self), key=operator.attrgetter('timestamp'), reverse=True) for item in bytime[self.capacity:]: try: @@ -313,14 +313,14 @@ def parse_encoding(fp): def sorted_dict_repr(d): """repr() a dictionary with the keys in order. - + Used by the lexer unit test to compare parse trees based on strings. - + """ keys = d.keys() keys.sort() return "{" + ", ".join(["%r: %r" % (k, d[k]) for k in keys]) + "}" - + def restore__ast(_ast): """Attempt to restore the required classes to the _ast module if it appears to be missing them |