diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-04-13 13:48:48 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-04-13 13:48:48 -0400 |
commit | 10c0aae0e06832d7a1684063246568dc93217b7a (patch) | |
tree | a73ba37e9f28d7121b1d08cb793d56552584a6de | |
parent | 73be27022328eb60f719431fd70a63f1265cf473 (diff) | |
parent | c2373cede978a62eb3c142c5ca4bb1a02a32b3ad (diff) | |
download | mako-10c0aae0e06832d7a1684063246568dc93217b7a.tar.gz |
Merge branch 'master' into w_json_metadata
Conflicts:
doc/build/changelog.rst
-rw-r--r-- | doc/build/changelog.rst | 16 | ||||
-rw-r--r-- | doc/build/inheritance.rst | 107 | ||||
-rw-r--r-- | mako/pyparser.py | 2 | ||||
-rw-r--r-- | mako/util.py | 13 | ||||
-rw-r--r-- | setup.cfg | 5 | ||||
-rw-r--r-- | test/__init__.py | 6 | ||||
-rw-r--r-- | test/test_ast.py | 25 |
7 files changed, 160 insertions, 14 deletions
diff --git a/doc/build/changelog.rst b/doc/build/changelog.rst index 7664284..27c12e5 100644 --- a/doc/build/changelog.rst +++ b/doc/build/changelog.rst @@ -21,6 +21,22 @@ Changelog here is to allow integration with coverage tools. .. change:: + :tags: bug, py3k + :tickets: 227 + + Fixed bug in Python parsing logic which would fail on Python 3 + when a "try/except" targeted a tuple of exception types, rather + than a single exception. + + .. change:: + :tags: feature + :pullreq: bitbucket:4 + + The mako-render script will now catch exceptions and run them + into the text error handler, and exit with a non-zero exit code. + Pull request courtesy Derek Harland. + + .. change:: :tags: feature, py3k :pullreq: github:7 diff --git a/doc/build/inheritance.rst b/doc/build/inheritance.rst index 9eba053..5b29574 100644 --- a/doc/build/inheritance.rst +++ b/doc/build/inheritance.rst @@ -192,6 +192,8 @@ a section that is used more than once, such as the title of a page: Where above an inheriting template can define ``<%block name="title">`` just once, and it will be used in the base template both in the ``<title>`` section as well as the ``<h2>``. + + But what about Defs? ==================== @@ -494,6 +496,111 @@ thing is now: and you're now a template inheritance ninja! +Using ``<%include>`` with Template Inheritance +============================================== + +A common source of confusion is the behavior of the ``<%include>`` tag, +often in conjunction with its interaction within template inheritance. +Key to understanding the ``<%include>`` tag is that it is a *dynamic*, e.g. +runtime, include, and not a static include. The ``<%include>`` is only processed +as the template renders, and not at inheritance setup time. When encountered, +the referenced template is run fully as an entirely separate template with no +linkage to any current inheritance structure. + +If the tag were on the other hand a *static* include, this would allow source +within the included template to interact within the same inheritance context +as the calling template, but currently Mako has no static include facility. + +In practice, this means that ``<%block>`` elements defined in an ``<%include>`` +file will not interact with corresponding ``<%block>`` elements in the calling +template. + +A common mistake is along these lines: + +.. sourcecode:: mako + + ## partials.mako + <%block name="header"> + Global Header + </%block> + + ## parent.mako + <%include file="partials.mako"> + + ## child.mako + <%inherit file="parent.mako"> + <%block name="header"> + Custom Header + </%block> + +Above, one might expect that the ``"header"`` block declared in ``child.mako`` +might be invoked, as a result of it overriding the same block present in +``parent.mako`` via the include for ``partials.mako``. But this is not the case. +Instead, ``parent.mako`` will invoke ``partials.mako``, which then invokes +``"header"`` in ``partials.mako``, and then is finished rendering. Nothing +from ``child.mako`` will render; there is no interaction between the ``"header"`` +block in ``child.mako`` and the ``"header"`` block in ``partials.mako``. + +Instead, ``parent.mako`` must explicitly state the inheritance structure. +In order to call upon specific elements of ``partials.mako``, we will call upon +it as a namespace: + +.. sourcecode:: mako + + ## partials.mako + <%block name="header"> + Global Header + </%block> + + ## parent.mako + <%namespace name="partials" file="partials.mako"/> + <%block name="header"> + ${partials.header()} + </%block> + + ## child.mako + <%inherit file="parent.mako"> + <%block name="header"> + Custom Header + </%block> + +Where above, ``parent.mako`` states the inheritance structure that ``child.mako`` +is to participate within. ``partials.mako`` only defines defs/blocks that can be +used on a per-name basis. + +Another scenario is below, which results in both ``"SectionA"`` blocks being rendered for the ``child.mako`` document: + +.. sourcecode:: mako + + ## base.mako + ${self.body()} + <%block name="SectionA"> + base.mako + </%block> + + ## parent.mako + <%inherit file="base.mako"> + <%include file="child.mako"> + + ## child.mako + <%block name="SectionA"> + child.mako + </%block> + +The resolution is similar; instead of using ``<%include>``, we call upon the blocks +of ``child.mako`` using a namespace: + +.. sourcecode:: mako + + ## parent.mako + <%inherit file="base.mako"> + <%namespace name="child" file="child.mako"> + + <%block name="SectionA"> + ${child.SectionA()} + </%block> + + .. _inheritance_attr: Inheritable Attributes diff --git a/mako/pyparser.py b/mako/pyparser.py index aa2d882..db5c295 100644 --- a/mako/pyparser.py +++ b/mako/pyparser.py @@ -102,7 +102,7 @@ if _ast: if node.name is not None: self._add_declared(node.name) if node.type is not None: - self.listener.undeclared_identifiers.add(node.type.id) + self.visit(node.type) for statement in node.body: self.visit(statement) diff --git a/mako/util.py b/mako/util.py index 0108109..48e3a23 100644 --- a/mako/util.py +++ b/mako/util.py @@ -11,15 +11,10 @@ import os from mako import compat import operator -def function_named(fn, name): - """Return a function with a given __name__. - - Will assign to __name__ and return the original function if possible on - the Python implementation, otherwise a new function will be constructed. - - """ - fn.__name__ = name - return fn +def update_wrapper(decorated, fn): + decorated.__wrapped__ = fn + decorated.__name__ = fn.__name__ + return decorated class PluginLoader(object): @@ -5,6 +5,11 @@ tag_build = dev [wheel] universal = 1 +[pytest] +addopts= --tb native -v -r fxX +python_files=test/*test_*.py + + [upload] sign = 1 identity = C4DAFEE1 diff --git a/test/__init__.py b/test/__init__.py index f114f64..64dde8e 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -2,7 +2,7 @@ from mako.template import Template import unittest import os from mako.compat import py3k, py26, py25 -from mako.util import function_named +from mako.util import update_wrapper import re from mako.cache import CacheImpl, register_plugin from nose import SkipTest @@ -93,7 +93,7 @@ def skip_if(predicate, reason=None): raise SkipTest(msg) else: return fn(*args, **kw) - return function_named(maybe, fn_name) + return update_wrapper(maybe, fn) return decorate def requires_python_3(fn): @@ -124,7 +124,7 @@ def requires_no_pygments_exceptions(fn): return fn(*arg, **kw) finally: exceptions._install_highlighting() - return function_named(go, fn.__name__) + return update_wrapper(go, fn) class PlainCacheImpl(CacheImpl): """Simple memory cache impl so that tests which diff --git a/test/test_ast.py b/test/test_ast.py index 9f9ec10..32a1d74 100644 --- a/test/test_ast.py +++ b/test/test_ast.py @@ -1,7 +1,8 @@ import unittest from mako import ast, exceptions, pyparser, util, compat -from test import eq_, requires_python_2, requires_python_3 +from test import eq_, requires_python_2, requires_python_3, \ + requires_python_26_or_greater exception_kwargs = { 'source': '', @@ -222,6 +223,28 @@ t2 = lambda (x,y):(x+5, y+4) eq_(parsed.declared_identifiers, set(['t1', 't2'])) eq_(parsed.undeclared_identifiers, set()) + @requires_python_26_or_greater + def test_locate_identifiers_16(self): + code = """ +try: + print(x) +except Exception as e: + print(y) +""" + parsed = ast.PythonCode(code, **exception_kwargs) + eq_(parsed.undeclared_identifiers, set(['x', 'y', 'Exception'])) + + @requires_python_26_or_greater + def test_locate_identifiers_17(self): + code = """ +try: + print(x) +except (Foo, Bar) as e: + print(y) +""" + parsed = ast.PythonCode(code, **exception_kwargs) + eq_(parsed.undeclared_identifiers, set(['x', 'y', 'Foo', 'Bar'])) + def test_no_global_imports(self): code = """ from foo import * |