summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2011-04-06 18:00:20 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2011-04-06 18:00:20 -0400
commit4a19e4d9d9b2f952e9afd93311ef19dc395ac46a (patch)
tree5b8892d2ad46c2827ac16e5052f11d1abb207c70
parent266abebaf9aad33e139f5f2ebd50ad604d6f3f86 (diff)
downloadmako-4a19e4d9d9b2f952e9afd93311ef19dc395ac46a.tar.gz
- New tag: <%block>. A variant on <%def> that
evaluates its contents in-place. Can be named or anonymous, the named version is intended for inheritance 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. Lots of new docs. [ticket:164]
-rw-r--r--CHANGES15
-rw-r--r--doc/build/builder/builders.py1
-rw-r--r--doc/build/caching.rst10
-rw-r--r--doc/build/defs.rst206
-rw-r--r--doc/build/filtering.rst8
-rw-r--r--doc/build/inheritance.rst283
-rw-r--r--doc/build/namespaces.rst8
-rw-r--r--doc/build/syntax.rst32
-rw-r--r--doc/build/templates/genindex.mako4
-rw-r--r--doc/build/templates/layout.mako27
-rw-r--r--doc/build/templates/search.mako9
-rw-r--r--doc/build/templates/site_base.mako5
-rw-r--r--doc/build/templates/static_base.mako4
-rw-r--r--mako/codegen.py126
-rw-r--r--mako/parsetree.py61
-rw-r--r--test/test_block.py569
16 files changed, 1261 insertions, 107 deletions
diff --git a/CHANGES b/CHANGES
index 76a023b..b99ff1d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,19 @@
0.4.1
+- New tag: <%block>. A variant on <%def> that
+ evaluates its contents in-place.
+ Can be named or anonymous,
+ the named version is intended for inheritance
+ 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.
+ Lots of new docs. [ticket:164]
+
- a slight adjustment to the "highlight" logic
for generating template bound stacktraces.
Will stick to known template source lines
diff --git a/doc/build/builder/builders.py b/doc/build/builder/builders.py
index 89eb350..b1311b4 100644
--- a/doc/build/builder/builders.py
+++ b/doc/build/builder/builders.py
@@ -17,7 +17,6 @@ class MakoBridge(TemplateBridge):
self.layout = builder.config.html_context.get('mako_layout', 'html')
self.lookup = TemplateLookup(directories=builder.config.templates_path,
- format_exceptions=True,
imports=[
"from builder import util"
]
diff --git a/doc/build/caching.rst b/doc/build/caching.rst
index 6cf7197..19656c8 100644
--- a/doc/build/caching.rst
+++ b/doc/build/caching.rst
@@ -5,7 +5,7 @@ Caching
========
Any template or component can be cached using the ``cache``
-argument to the ``<%page>`` or ``<%def>`` directives:
+argument to the ``<%page>``, ``<%def>`` or ``<%block>`` directives:
.. sourcecode:: mako
@@ -32,6 +32,14 @@ The caching flag and all its options can be used with the
other text
</%def>
+... and equivalently with the ``<%block>`` tag, anonymous or named:
+
+.. sourcecode:: mako
+
+ <%block cached="True" cache_timeout="30" cache_type="memory">
+ other text
+ </%block>
+
Cache arguments
================
diff --git a/doc/build/defs.rst b/doc/build/defs.rst
index 42380ee..12243d5 100644
--- a/doc/build/defs.rst
+++ b/doc/build/defs.rst
@@ -1,12 +1,20 @@
.. _defs_toplevel:
-====
-Defs
-====
+===============
+Defs and Blocks
+===============
-``<%def>`` is the single tag used to demarcate any block of text
-and/or code. It exists within generated Python as a callable
-function:
+``<%def>`` and ``<%block>`` are two tags that both demarcate any block of text
+and/or code. They both exist within generated Python as a callable function,
+i.e., a Python ``def``. They differ in their scope and calling semantics.
+Whereas ``<%def>`` provides a construct that is very much like a named Python
+``def``, the ``<%block>`` is more layout oriented.
+
+Using Defs
+==========
+
+The ``<%def>`` tag requires a ``name`` attribute, where the ``name`` references
+a Python function signature:
.. sourcecode:: mako
@@ -14,7 +22,7 @@ function:
hello world
</%def>
-They are normally called as expressions:
+To invoke the ``<%def>``, it is normally called as an expression:
.. sourcecode:: mako
@@ -66,7 +74,7 @@ which evaluate to ``UNDEFINED`` if you reference a name that
does not exist.
Calling defs from Other Files
-==============================
+-----------------------------
Top level ``<%defs>`` are **exported** by your template's
module, and can be called from the outside; including from other
@@ -110,7 +118,7 @@ these docs. For more detail and examples, see
:ref:`namespaces_toplevel`.
Calling defs programmatically
-==============================
+-----------------------------
You can call def's programmatically from any :class:`.Template` object
using the :meth:`~.Template.get_def()` method, which returns a :class:`.DefTemplate`
@@ -136,7 +144,7 @@ object. This is a :class:`.Template` subclass which the parent
Defs within Defs
-================
+----------------
The def model follows regular Python rules for closures.
Declaring ``<%def>`` inside another ``<%def>`` declares it
@@ -195,7 +203,7 @@ in the expression that tries to render it.
.. _defs_with_content:
Calling a def with embedded content and/or other defs
-=====================================================
+-----------------------------------------------------
A flip-side to def within def is a def call with content. This
is where you call a def, and at the same time declare a block of
@@ -432,5 +440,181 @@ with a "custom tag" or tag library in some other system, Mako
provides via ``<%def>`` tags and plain Python callables which are
invoked via ``<%namespacename:defname>`` or ``<%call>``.
+.. _blocks:
+
+Using Blocks
+============
+
+The ``<%block>`` tag is new as of Mako 0.4.1, and introduces some new twists on the
+``<%def>`` tag which make it more closely tailored towards layout.
+
+An example of a block:
+
+.. sourcecode:: mako
+
+ <html>
+ <body>
+ <%block>
+ this is a block.
+ </%block>
+ </body>
+ </html>
+
+In the above example, we define a simple block. The block renders its content in the place
+that it's defined. Since the block is called for us, it doesn't need a name and the above
+is referred to as an **anonymous block**. So the output of the above template will be:
+
+.. sourcecode:: html
+
+ <html>
+ <body>
+ this is a block.
+ </body>
+ </html>
+
+So in fact the above block has absolutely no effect. Its usefulness comes when we start
+using modifiers. Such as, we can apply a filter to our block:
+
+.. sourcecode:: mako
+
+ <html>
+ <body>
+ <%block filter="h">
+ <html>this is some escaped html.</html>
+ </%block>
+ </body>
+ </html>
+
+or perhaps a caching directive:
+
+.. sourcecode:: mako
+
+ <html>
+ <body>
+ <%block cached="True" cache_timeout="60">
+ This content will be cached for 60 seconds.
+ </%block>
+ </body>
+ </html>
+
+Blocks also work in iterations, conditionals, just like defs:
+
+.. sourcecode:: mako
+
+ % if some_condition:
+ <%block>condition is met</%block>
+ % endif
+
+While the block renders at the point it is defined in the template,
+the underlying function is present in the generated Python code only
+once, so there's no issue with placing a block inside of a loop or
+similar. Anonymous blocks are defined as closures in the local
+rendering body, so have access to local variable scope:
+
+.. sourcecode:: mako
+
+ % for i in range(1, 4):
+ <%block>i is ${i}</%block>
+ % endfor
+
+
+Using Named Blocks
+------------------
+
+Possibly the more important area where blocks are useful is when we
+do actually give them names. Named blocks are tailored to behave
+somewhat closely to Jinja2's block tag, in that they define an area
+of a layout which can be overridden by an inheriting template. In
+sharp contrast to the ``<%def>`` tag, the name given to a block is
+global for the entire template regardless of how deeply it's nested:
+
+.. sourcecode:: mako
+
+ <html>
+ <%block name="header">
+ <head>
+ <title>
+ <%block name="title">Title</%block>
+ </title>
+ </head>
+ </%block>
+ <body>
+ ${next.body()}
+ </body>
+ </html>
+
+The above example has two named blocks "``header``" and "``title``", both of which can be referred to
+by an inheriting template. A detailed walkthrough of this usage can be found at :ref:`inheritance_toplevel`.
+
+Note above that named blocks don't have any argument declaration the way defs do. They still implement themselves
+as Python functions, however, so they can be invoked additional times beyond their initial definition:
+
+.. sourcecode:: mako
+
+ <div name="page">
+ <%block name="pagecontrol">
+ <a href="">previous page</a> |
+ <a href="">next page</a>
+ </%block>
+
+ <table>
+ ## some content
+ </table>
+
+ ${pagecontrol()}
+ </div>
+
+The content referenced by ``pagecontrol`` above will be rendered both above and below the ``<table>`` tags.
+
+To keep things sane, named blocks have restrictions that defs do not:
+
+* The ``<%block>`` declaration cannot have any argument signature.
+* The name of a ``<%block>`` can only be defined once in a template - an error is raised if two blocks of the same
+ name occur anywhere in a single template, regardless of nesting. A similar error is raised if a top level def
+ shares the same name as that of a block.
+* A named ``<%block>`` cannot be defined within a ``<%def>``, or inside the body of a "call", i.e.
+ ``<%call>`` or ``<%namespacename:defname>`` tag. Anonymous blocks can, however.
+
+Using page arguments in named blocks
+-------------------------------------
+
+A named block is very much like a top level def. It has a similar
+restriction to these types of defs in that arguments passed to the
+template via the ``<%page>`` tag aren't automatically available.
+Using arguments with the ``<%page>`` tag is described in the section
+:ref:`namespaces_body`, and refers to scenarios such as when the
+``body()`` method of a template is called from an inherited template passing
+arguments, or the template is invoked from an ``<%include>`` tag
+with arguments. To allow a named block to share the same arguments
+passed to the page, the ``args`` attribute can be used:
+
+.. sourcecode:: mako
+
+ <%page args="post"/>
+
+ <a name="${post.title}" />
+
+ <span class="post_prose">
+ <%block name="post_prose" args="post">
+ ${post.content}
+ </%block>
+ </span>
+
+Where above, if the template is called via a directive like
+``<%include file="post.mako" args="post=post" />``, the ``post``
+variable is available both in the main body as well as the
+``post_prose`` block.
+
+Similarly, the ``**pageargs`` variable is present, in named blocks only,
+for those arguments not explicit in the ``<%page>`` tag:
+
+.. sourcecode:: mako
+ <%block name="post_prose">
+ ${pageargs['post'].content}
+ </%block>
+The ``args`` attribute is only allowed with named blocks. With
+anonymous blocks, the Python function is always rendered in the same
+scope as the call itself, so anything available directly outside the
+anonymous block is available inside as well.
diff --git a/doc/build/filtering.rst b/doc/build/filtering.rst
index 9cf65ec..ad42dc4 100644
--- a/doc/build/filtering.rst
+++ b/doc/build/filtering.rst
@@ -166,10 +166,10 @@ Will render ``myexpression`` with no filtering of any kind, and
will render ``myexpression`` using the ``trim`` filter only.
-Filtering Defs
-=================
+Filtering Defs and Blocks
+==========================
-The ``%def`` tag has a filter argument which will apply the
+The ``%def`` and ``%block`` tags have an argument called ``filter`` which will apply the
given list of filter functions to the output of the ``%def``:
.. sourcecode:: mako
@@ -334,7 +334,7 @@ as just ``runtime``):
${foo()}
The decorator can be used with top-level defs as well as nested
-defs. Note that when calling a top-level def from the
+defs, and blocks too. Note that when calling a top-level def from the
``Template`` api, i.e. ``template.get_def('somedef').render()``,
the decorator has to write the output to the ``context``, i.e.
as in the first example. The return value gets discarded.
diff --git a/doc/build/inheritance.rst b/doc/build/inheritance.rst
index e8fe2c2..60775fa 100644
--- a/doc/build/inheritance.rst
+++ b/doc/build/inheritance.rst
@@ -4,6 +4,12 @@
Inheritance
===========
+.. note:: Most of the inheritance examples here take advantage of a feature that's
+ new in Mako as of version 0.4.1 called the "block". This tag is very similar to
+ the "def" tag but is more streamlined for usage with inheritance. Note that
+ all of the examples here which use blocks can also use defs instead. Constrasting
+ usages will be illustrated.
+
Using template inheritance, two or more templates can organize
themselves into an **inheritance chain**, where content and
functions from all involved templates can be intermixed. The
@@ -14,7 +20,7 @@ to send the executional control to template ``B`` at runtime
the **inherited** template, then makes decisions as to what
resources from ``A`` shall be executed.
-In practice, it looks like this. Heres a hypothetical inheriting
+In practice, it looks like this. Here's a hypothetical inheriting
template, ``index.html``:
.. sourcecode:: mako
@@ -22,9 +28,9 @@ template, ``index.html``:
## index.html
<%inherit file="base.html"/>
- <%def name="header()">
+ <%block name="header">
this is some header content
- </%def>
+ </%block>
this is the body content.
@@ -36,42 +42,47 @@ And ``base.html``, the inherited template:
<html>
<body>
<div class="header">
- ${self.header()}
+ <%block name="header"/>
</div>
${self.body()}
<div class="footer">
- ${self.footer()}
+ <%block name="footer">
+ this is the footer
+ </%block>
</div>
</body>
</html>
- <%def name="footer()">
- this is the footer
- </%def>
-
Here is a breakdown of the execution:
* When ``index.html`` is rendered, control immediately passes to
``base.html``.
* ``base.html`` then renders the top part of an HTML document,
- then calls the method ``header()`` off of a built in namespace
+ then invokes the ``<%block name="header">`` block. It invokes the
+ underlying ``header()`` function off of a built in namespace
called ``self`` (this namespace was first introduced in the
- Namespaces chapter in
- :ref:`namespace_self`). Since
- ``index.html`` is the topmost template and also defines a def
- called ``header()``, its this ``header()`` def that gets
- executed.
+ Namespaces chapter in :ref:`namespace_self`). Since
+ ``index.html`` is the topmost template and also defines a block
+ called ``header``, its this ``header`` block that ultimately gets
+ executed - instead of the one that's present in ``base.html``.
* Control comes back to ``base.html``. Some more HTML is
rendered.
* ``base.html`` executes ``self.body()``. The ``body()``
function on all template-based namespaces refers to the main
body of the template, therefore the main body of
``index.html`` is rendered.
+* When ``<%block name="header">`` is encountered in ``index.html``
+ during the ``self.body()`` call, a conditional is checked - does the
+ current inherited template, i.e. ``base.html``, also define this block ? If yes,
+ the ``<%block>`` is **not** executed here - the inheritance
+ mechanism knows that the parent template is responsible for rendering
+ this block (and in fact it already has). In other words a block
+ only renders in its *basemost scope*.
* Control comes back to ``base.html``. More HTML is rendered,
- then the ``self.footer()`` expression is invoked.
-* The ``footer`` def is only defined in ``base.html``, so being
+ then the ``<%block name="footer">`` expression is invoked.
+* The ``footer`` block is only defined in ``base.html``, so being
the topmost definition of ``footer``, its the one that
executes. If ``index.html`` also specified ``footer``, then
its version would **override** that of the base.
@@ -104,6 +115,208 @@ seriously; while useful to setup some commonly recognized
semantics, a textual template is not very much like an
object-oriented class construct in practice).
+Nesting Blocks
+==============
+
+The named blocks defined in an inherited template can also be nested within
+other blocks. The name given to each block is globally accessible via any inheriting
+template. We can add a new block ``title`` to our ``header`` block:
+
+.. sourcecode:: mako
+
+ ## base.html
+ <html>
+ <body>
+ <div class="header">
+ <%block name="header">
+ <h2>
+ <%block name="title"/>
+ </h2>
+ </%block>
+ </div>
+
+ ${self.body()}
+
+ <div class="footer">
+ <%block name="footer">
+ this is the footer
+ </%block>
+ </div>
+ </body>
+ </html>
+
+The inheriting template can name either or both of ``header`` and ``title``, separately
+or nested themselves:
+
+.. sourcecode:: mako
+
+ ## index.html
+ <%inherit file="base.html"/>
+
+ <%block name="header">
+ this is some header content
+ ${parent.header()}
+ </%block>
+
+ <%block name="title">
+ this is the title
+ </%block>
+
+ this is the body content.
+
+Note when we overrode ``header``, we added an extra call ``${parent.header()}`` in order to invoke
+the parent's ``header`` block in addition to our own. That's described in more detail below,
+in :ref:`parent_namespace`.
+
+Rendering a named block multiple times
+======================================
+
+Recall from the section :ref:`blocks` that a named block is just like a ``<%def>``,
+with some different usage rules. We can call one of our named sections distinctly, for example
+a section that is used more than once, such as the title of a page:
+
+.. sourcecode:: mako
+
+ <html>
+ <head>
+ <title>${self.title()}</title>
+ </head>
+ <body>
+ <%block name="header">
+ <h2><%block name="title"/></h2>
+ </%block>
+ ${self.body()}
+ </body>
+ </html>
+
+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 ?
+=====================
+
+The previous example used the ``<%block>`` tag to produce areas of content
+to be overridden. Before Mako 0.4.1, there wasn't any such tag - instead
+there was only the ``<%def>`` tag. As it turns out, named blocks and defs are
+largely interchangeable. The def simply doesn't call itself automatically,
+and has more open-ended naming and scoping rules that are more flexible and similar
+to Python itself, but less suited towards layout. The first example from
+this chapter using defs would look like:
+
+.. sourcecode:: mako
+
+ ## index.html
+ <%inherit file="base.html"/>
+
+ <%def name="header()">
+ this is some header content
+ </%def>
+
+ this is the body content.
+
+And ``base.html``, the inherited template:
+
+.. sourcecode:: mako
+
+ ## base.html
+ <html>
+ <body>
+ <div class="header">
+ ${self.header()}
+ </div>
+
+ ${self.body()}
+
+ <div class="footer">
+ ${self.footer()}
+ </div>
+ </body>
+ </html>
+
+ <%def name="header()"/>
+ <%def name="footer()">
+ this is the footer
+ </%def>
+
+Above, we illustrate that defs differ from blocks in that their definition
+and invocation are defined in two separate places, instead of at once. You can *almost* do exactly what a
+block does if you put the two together:
+
+.. sourcecode:: mako
+
+ <div class="header">
+ <%def name="header()"></%def>${self.header()}
+ </div>
+
+The ``<%block>`` is obviously more streamlined than the ``<%def>`` for this kind
+of usage. In addition,
+the above "inline" approach with ``<%def>`` does not work with nesting:
+
+.. sourcecode:: mako
+
+ <head>
+ <%def name="header()">
+ <title>
+ ## this won't work !
+ <%def name="title()">default title</%def>${self.title()}
+ </title>
+ </%def>${self.header()}
+ </head>
+
+Where above, the ``title()`` def, because it's a def within a def, is not part of the
+template's exported namespace and will not be part of ``self``. If the inherited template
+did define its own ``title`` def at the top level, it would be called, but the "default title"
+above is not present at all on ``self`` no matter what. For this to work as expected
+you'd instead need to say:
+
+.. sourcecode:: mako
+
+ <head>
+ <%def name="header()">
+ <title>
+ ${self.title()}
+ </title>
+ </%def>${self.header()}
+
+ <%def name="title()"/>
+ </head>
+
+That is, ``title`` is defined outside of any other defs so that it is in the ``self`` namespace.
+It works, but the definition needs to be potentially far away from the point of render.
+
+A named block is always placed in the ``self`` namespace, regardless of nesting,
+so this restriction is lifted:
+
+.. sourcecode:: mako
+
+ ## base.html
+ <head>
+ <%block name="header">
+ <title>
+ <%block name="title"/>
+ </title>
+ </%block>
+ </head>
+
+The above template defines ``title`` inside of ``header``, and an inheriting template can define
+one or both in **any** configuration, nested inside each other or not, in order for them to be used:
+
+.. sourcecode:: mako
+
+ ## index.html
+ <%inherit file="base.html"/>
+ <%block name="title">
+ the title
+ </%block>
+ <%block name="header">
+ the header
+ </%block>
+
+So while the ``<%block>`` tag lifts the restriction of nested blocks not being available externally,
+in order to achieve this it *adds* the restriction that all block names in a single template need
+to be globally unique within the template, and additionally that a ``<%block>`` can't be defined
+inside of a ``<%def>``. It's a more restricted tag suited towards a more specific use case than ``<%def>``.
+
Using the "next" namespace to produce content wrapping
=======================================================
@@ -127,20 +340,19 @@ Lets change the line in ``base.html`` which calls upon
<html>
<body>
<div class="header">
- ${self.header()}
+ <%block name="header"/>
</div>
${next.body()}
<div class="footer">
- ${self.footer()}
+ <%block name="footer">
+ this is the footer
+ </%block>
</div>
</body>
</html>
- <%def name="footer()">
- this is the footer
- </%def>
Lets also add an intermediate template called ``layout.html``,
which inherits from ``base.html``:
@@ -150,17 +362,16 @@ which inherits from ``base.html``:
## layout.html
<%inherit file="base.html"/>
<ul>
- ${self.toolbar()}
+ <%block name="toolbar">
+ <li>selection 1</li>
+ <li>selection 2</li>
+ <li>selection 3</li>
+ </%block>
</ul>
<div class="mainlayout">
${next.body()}
</div>
- <%def name="toolbar()">
- <li>selection 1</li>
- <li>selection 2</li>
- <li>selection 3</li>
- </%def>
And finally change ``index.html`` to inherit from
``layout.html`` instead:
@@ -215,15 +426,17 @@ Without the ``next`` namespace, only the main body of
``index.html`` could be used; there would be no way to call
``layout.html``'s body content.
+.. _parent_namespace:
+
Using the "parent" namespace to augment defs
=============================================
Lets now look at the other inheritance-specific namespace, the
opposite of ``next`` called ``parent``. ``parent`` is the
namespace of the template **immediately preceding** the current
-template. What is most useful about this namespace is the
-methods within it which can be accessed within overridden
-versions of those methods. This is not as hard as it sounds and
+template. What's useful about this namespace is that
+defs or blocks can call upon their overridden versions.
+This is not as hard as it sounds and
is very much like using the ``super`` keyword in Python. Lets
modify ``index.html`` to augment the list of selections provided
by the ``toolbar`` function in ``layout.html``:
@@ -233,16 +446,16 @@ by the ``toolbar`` function in ``layout.html``:
## index.html
<%inherit file="layout.html"/>
- <%def name="header()">
+ <%block name="header">
this is some header content
- </%def>
+ </%block>
- <%def name="toolbar()">
+ <%block name="toolbar">
## call the parent's toolbar first
${parent.toolbar()}
<li>selection 4</li>
<li>selection 5</li>
- </%def>
+ </%block>
this is the body content.
diff --git a/doc/build/namespaces.rst b/doc/build/namespaces.rst
index 394750a..f3f66e6 100644
--- a/doc/build/namespaces.rst
+++ b/doc/build/namespaces.rst
@@ -4,10 +4,10 @@
Namespaces
==========
-Namespaces are used to organize groups of components into
-categories, and also to "import" components from other files.
+Namespaces are used to organize groups of defs into
+categories, and also to "import" defs from other files.
-If the file ``components.html`` defines these two components:
+If the file ``components.html`` defines these two defs:
.. sourcecode:: mako
@@ -21,7 +21,7 @@ If the file ``components.html`` defines these two components:
</%def>
You can make another file, for example ``index.html``, that
-pulls those two components into a namespace called ``comp``:
+pulls those two defs into a namespace called ``comp``:
.. sourcecode:: mako
diff --git a/doc/build/syntax.rst b/doc/build/syntax.rst
index 7251e9b..7305d58 100644
--- a/doc/build/syntax.rst
+++ b/doc/build/syntax.rst
@@ -47,7 +47,6 @@ result.
Expression Escaping
===================
-
Mako includes a number of built-in escaping mechanisms,
including HTML, URI and XML escaping, as well as a "trim"
function. These escapes can be added to an expression
@@ -300,6 +299,35 @@ with content, which enable packages of defs to be sent as
arguments to other def calls (not as hard as it sounds). Get the
full deal on what %def can do in :ref:`defs_toplevel`.
+<%block>
+---------
+
+``%block`` is a tag that's new as of Mako 0.4.1. It's close to
+a ``%def``, except executes itself immediately in its base-most scope,
+and can also be anonymous (i.e. with no name):
+
+.. sourcecode:: mako
+
+ <%block filter="h">
+ some <html> stuff.
+ </%block>
+
+Inspired by Jinja2 blocks, named blocks offer a syntactically pleasing way
+to do inheritance:
+
+.. sourcecode:: mako
+
+ <html>
+ <body>
+ <%block name="header">
+ <h2><%block name="title"/></h2>
+ </%block>
+ ${self.body()}
+ </body>
+ </html>
+
+Blocks are introduced in :ref:`blocks` and further described in :ref:`inheritance_toplevel`.
+
<%namespace>
-------------
@@ -360,7 +388,7 @@ To create custom tags which accept a body, see
The call tag is the "classic" form of a user-defined tag, and is
roughly equiavlent to the ``<%namespacename:defname>`` syntax
-described above. This tag is also described in `defs_with_content`.
+described above. This tag is also described in :ref:`defs_with_content`.
<%doc>
------
diff --git a/doc/build/templates/genindex.mako b/doc/build/templates/genindex.mako
index 1543314..4933da4 100644
--- a/doc/build/templates/genindex.mako
+++ b/doc/build/templates/genindex.mako
@@ -1,6 +1,8 @@
<%inherit file="layout.mako"/>
-<%def name="show_title()">${_('Index')}</%def>
+<%block name="show_title" filter="util.striptags">
+ ${_('Index')}
+</%block>
<h1 id="index">${_('Index')}</h1>
diff --git a/doc/build/templates/layout.mako b/doc/build/templates/layout.mako
index 0355cd1..59c3f07 100644
--- a/doc/build/templates/layout.mako
+++ b/doc/build/templates/layout.mako
@@ -1,7 +1,7 @@
## coding: utf-8
<%inherit file="${context['mako_layout']}"/>
-<%def name="headers()">
+<%block name="headers">
<link rel="stylesheet" href="${pathto('_static/pygments.css', 1)}" type="text/css" />
<link rel="stylesheet" href="${pathto('_static/docs.css', 1)}" type="text/css" />
@@ -35,9 +35,10 @@
% if prevtopic:
<link rel="prev" title="${prevtopic['title']|util.striptags}" href="${prevtopic['link']|h}" />
% endif
- ${self.extrahead()}
-</%def>
-<%def name="extrahead()"></%def>
+
+ <%block name="extrahead">
+ </%block>
+</%block>
<h1>${docstitle|h}</h1>
@@ -74,10 +75,14 @@
% if current_page_name != master_doc:
ยป ${self.show_title()}
% endif
-
+
${prevnext()}
<h2>
- ${self.show_title()}
+ <%block name="show_title" filter="util.striptags">
+ % if title:
+ ${title}
+ % endif
+ </%block>
</h2>
</div>
% if display_toc and not current_page_name.startswith('index'):
@@ -92,7 +97,7 @@
</div>
</div>
- <%def name="footer()">
+ <%block name="footer">
<div class="bottomnav">
${prevnext()}
<div class="doc_copyright">
@@ -106,8 +111,7 @@
% endif
</div>
</div>
- </%def>
- ${self.footer()}
+ </%block>
<%def name="prevnext()">
<div class="prevnext">
@@ -122,9 +126,4 @@
</div>
</%def>
-<%def name="show_title()">
-% if title:
- ${title}
-% endif
-</%def>
diff --git a/doc/build/templates/search.mako b/doc/build/templates/search.mako
index dfad4d0..eb21a24 100644
--- a/doc/build/templates/search.mako
+++ b/doc/build/templates/search.mako
@@ -3,7 +3,10 @@
<%!
local_script_files = ['_static/searchtools.js']
%>
-<%def name="show_title()">${_('Search')}</%def>
+
+<%block name="show_title" filter="util.striptags">
+ ${_('Search')}
+</%block>
<div id="searchform">
<h3>Enter Search Terms:</h3>
@@ -16,7 +19,7 @@
<div id="search-results"></div>
-<%def name="footer()">
+<%block name="footer">
${parent.footer()}
<script type="text/javascript" src="searchindex.js"></script>
-</%def>
+</%block>
diff --git a/doc/build/templates/site_base.mako b/doc/build/templates/site_base.mako
index 8ab31d9..6090480 100644
--- a/doc/build/templates/site_base.mako
+++ b/doc/build/templates/site_base.mako
@@ -18,12 +18,13 @@ ${next.body()}
${'</%text>'}
<%text><%def name="style()"></%text>
- ${self.headers()}
+ <%block name="headers"/>
+
<%text>${parent.style()}</%text>
<link href="/css/site_docs.css" rel="stylesheet" type="text/css"></link>
<%text></%def></%text>
-<%text><%def name="title()"></%text>${capture(self.show_title)|util.striptags} &mdash; ${docstitle|h}<%text></%def></%text>
+<%text><%def name="title()"></%text><%block name="show_title"/> &mdash; ${docstitle|h}<%text></%def></%text>
<%!
local_script_files = []
diff --git a/doc/build/templates/static_base.mako b/doc/build/templates/static_base.mako
index eaca5ce..23ebf0a 100644
--- a/doc/build/templates/static_base.mako
+++ b/doc/build/templates/static_base.mako
@@ -5,8 +5,8 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
${metatags and metatags or ''}
- <title>${capture(self.show_title)|util.striptags} &mdash; ${docstitle|h}</title>
- ${self.headers()}
+ <title><%block name="show_title"/> &mdash; ${docstitle|h}</title>
+ <%block name="headers"/>
</head>
<body>
${next.body()}
diff --git a/mako/codegen.py b/mako/codegen.py
index 2af1eac..5369180 100644
--- a/mako/codegen.py
+++ b/mako/codegen.py
@@ -9,7 +9,7 @@
import time
import re
from mako.pygen import PythonPrinter
-from mako import util, ast, parsetree, filters
+from mako import util, ast, parsetree, filters, exceptions
MAGIC_NUMBER = 6
@@ -84,16 +84,18 @@ class _GenerateRenderMethod(object):
self.node = node
self.identifier_stack = [None]
- self.in_def = isinstance(node, parsetree.DefTag)
+ self.in_def = isinstance(node, (parsetree.DefTag, parsetree.BlockTag))
if self.in_def:
- name = "render_" + node.name
- args = node.function_decl.get_argument_expressions()
+ name = "render_%s" % node.funcname
+ args = node.get_argument_expressions()
filtered = len(node.filter_args.args) > 0
buffered = eval(node.attributes.get('buffered', 'False'))
cached = eval(node.attributes.get('cached', 'False'))
defs = None
pagetag = None
+ if node.is_block and not node.is_anonymous:
+ args += ['**pageargs']
else:
defs = self.write_toplevel()
pagetag = self.compiler.pagetag
@@ -238,7 +240,7 @@ class _GenerateRenderMethod(object):
self.printer.writeline("context._push_buffer()")
self.identifier_stack.append(self.compiler.identifiers.branch(self.node))
- if not self.in_def and '**pageargs' in args:
+ if (not self.in_def or self.node.is_block) and '**pageargs' in args:
self.identifier_stack[-1].argument_declared.add('pageargs')
if not self.in_def and (
@@ -308,8 +310,19 @@ class _GenerateRenderMethod(object):
self.in_def = True
class NSDefVisitor(object):
def visitDefTag(s, node):
+ s.visitDefOrBase(node)
+
+ def visitBlockTag(s, node):
+ s.visitDefOrBase(node)
+
+ def visitDefOrBase(s, node):
+ if node.is_anonymous:
+ raise exceptions.CompileException(
+ "Can't put anonymous blocks inside <%namespace>",
+ **node.exception_kwargs
+ )
self.write_inline_def(node, identifiers, nested=False)
- export.append(node.name)
+ export.append(node.funcname)
vis = NSDefVisitor()
for n in node.nodes:
n.accept_visitor(vis)
@@ -376,9 +389,8 @@ class _GenerateRenderMethod(object):
top-level, it is fully rendered as a local closure.
"""
-
# collection of all defs available to us in this scope
- comp_idents = dict([(c.name, c) for c in identifiers.defs])
+ comp_idents = dict([(c.funcname, c) for c in identifiers.defs])
to_write = set()
# write "context.get()" for all variables we are going to
@@ -387,7 +399,7 @@ class _GenerateRenderMethod(object):
# write closure functions for closures that we define
# right here
- to_write = to_write.union([c.name for c in identifiers.closuredefs.values()])
+ to_write = to_write.union([c.funcname for c in identifiers.closuredefs.values()])
# remove identifiers that are declared in the argument
# signature of the callable
@@ -420,10 +432,17 @@ class _GenerateRenderMethod(object):
for ident in to_write:
if ident in comp_idents:
comp = comp_idents[ident]
- if comp.is_root():
- self.write_def_decl(comp, identifiers)
+ if comp.is_block:
+ if not comp.is_anonymous:
+ self.write_def_decl(comp, identifiers)
+ else:
+ self.write_inline_def(comp, identifiers, nested=True)
else:
- self.write_inline_def(comp, identifiers, nested=True)
+ if comp.is_root():
+ self.write_def_decl(comp, identifiers)
+ else:
+ self.write_inline_def(comp, identifiers, nested=True)
+
elif ident in self.compiler.namespaces:
self.printer.writeline(
"%s = _mako_get_namespace(context, %r)" %
@@ -472,9 +491,9 @@ class _GenerateRenderMethod(object):
def write_def_decl(self, node, identifiers):
"""write a locally-available callable referencing a top-level def"""
- funcname = node.function_decl.funcname
- namedecls = node.function_decl.get_argument_expressions()
- nameargs = node.function_decl.get_argument_expressions(include_defaults=False)
+ 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
@@ -488,13 +507,13 @@ class _GenerateRenderMethod(object):
def write_inline_def(self, node, identifiers, nested):
"""write a locally-available def callable inside an enclosing def."""
-
- namedecls = node.function_decl.get_argument_expressions()
+
+ 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.name, ",".join(namedecls)))
+ self.printer.writeline("def %s(%s):" % (node.funcname, ",".join(namedecls)))
filtered = len(node.filter_args.args) > 0
buffered = eval(node.attributes.get('buffered', 'False'))
cached = eval(node.attributes.get('cached', 'False'))
@@ -519,7 +538,7 @@ class _GenerateRenderMethod(object):
self.write_def_finish(node, buffered, filtered, cached)
self.printer.writeline(None)
if cached:
- self.write_cache_decorator(node, node.name,
+ self.write_cache_decorator(node, node.funcname,
namedecls, False, identifiers,
inline=True, toplevel=False)
@@ -750,6 +769,18 @@ class _GenerateRenderMethod(object):
def visitDefTag(self, node):
pass
+ def visitBlockTag(self, node):
+ if node.is_anonymous:
+ self.printer.writeline("%s()" % node.funcname)
+ else:
+ nameargs = node.get_argument_expressions(include_defaults=False)
+ nameargs += ['**pageargs']
+ self.printer.writeline("if 'parent' not in context._data or "
+ "not hasattr(context._data['parent'], '%s'):"
+ % node.funcname)
+ self.printer.writeline("context['self'].%s(%s)" % (node.funcname, ",".join(nameargs)))
+ self.printer.writeline("\n")
+
def visitCallNamespaceTag(self, node):
# TODO: we can put namespace-specific checks here, such
# as ensure the given namespace will be imported,
@@ -770,12 +801,19 @@ class _GenerateRenderMethod(object):
self.identifier_stack.append(body_identifiers)
class DefVisitor(object):
def visitDefTag(s, node):
+ s.visitDefOrBase(node)
+
+ def visitBlockTag(s, node):
+ s.visitDefOrBase(node)
+
+ def visitDefOrBase(s, node):
self.write_inline_def(node, callable_identifiers, nested=False)
- export.append(node.name)
+ if not node.is_anonymous:
+ export.append(node.funcname)
# remove defs that are within the <%call> from the "closuredefs" defined
# in the body, so they dont render twice
- if node.name in body_identifiers.closuredefs:
- del body_identifiers.closuredefs[node.name]
+ if node.funcname in body_identifiers.closuredefs:
+ del body_identifiers.closuredefs[node.funcname]
vis = DefVisitor()
for n in node.nodes:
@@ -934,12 +972,23 @@ class _Identifiers(object):
if self.node is node:
for n in node.nodes:
n.accept_visitor(self)
-
+
+ def _check_name_exists(self, collection, node):
+ existing = collection.get(node.funcname)
+ collection[node.funcname] = node
+ if existing is not None and \
+ existing is not node and \
+ (node.is_block or existing.is_block):
+ raise exceptions.CompileException(
+ "%%def or %%block named '%s' already "
+ "exists in this template." %
+ node.funcname, **node.exception_kwargs)
+
def visitDefTag(self, node):
- if node.is_root():
- self.topleveldefs[node.name] = node
+ if node.is_root() and not node.is_anonymous:
+ self._check_name_exists(self.topleveldefs, node)
elif node is not self.node:
- self.closuredefs[node.name] = node
+ self._check_name_exists(self.closuredefs, node)
for ident in node.undeclared_identifiers():
if ident != 'context' and ident not in self.declared.union(self.locally_declared):
@@ -951,7 +1000,30 @@ class _Identifiers(object):
self.argument_declared.add(ident)
for n in node.nodes:
n.accept_visitor(self)
-
+
+ def visitBlockTag(self, node):
+ if node is not self.node and \
+ not node.is_anonymous:
+
+ if isinstance(self.node, parsetree.DefTag):
+ raise exceptions.CompileException(
+ "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"
+ % (node.name, ), **node.exception_kwargs)
+
+ if not node.is_anonymous:
+ self._check_name_exists(self.topleveldefs, node)
+ self.undeclared.add(node.funcname)
+ elif node is not self.node:
+ self._check_name_exists(self.closuredefs, node)
+ for ident in node.declared_identifiers():
+ self.argument_declared.add(ident)
+ for n in node.nodes:
+ n.accept_visitor(self)
+
def visitIncludeTag(self, node):
self.check_declared(node)
diff --git a/mako/parsetree.py b/mako/parsetree.py
index 53a980d..31b9b4f 100644
--- a/mako/parsetree.py
+++ b/mako/parsetree.py
@@ -410,6 +410,16 @@ class DefTag(Tag):
attributes.get('filter', ''),
**self.exception_kwargs)
+ is_anonymous = False
+ is_block = False
+
+ @property
+ def funcname(self):
+ return self.function_decl.funcname
+
+ def get_argument_expressions(self, **kw):
+ return self.function_decl.get_argument_expressions(**kw)
+
def declared_identifiers(self):
return self.function_decl.argnames
@@ -423,6 +433,57 @@ class DefTag(Tag):
difference(filters.DEFAULT_ESCAPES.keys())
)
+class BlockTag(Tag):
+ __keyword__ = 'block'
+
+ def __init__(self, keyword, attributes, **kwargs):
+ super(BlockTag, self).__init__(
+ keyword,
+ attributes,
+ ('buffered', 'cached', 'cache_key', 'cache_timeout',
+ 'cache_type', 'cache_dir', 'cache_url', 'args'),
+ ('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",
+ **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.exception_kwargs)
+
+ self.name = name
+ self.decorator = attributes.get('decorator', '')
+ self.filter_args = ast.ArgumentList(
+ attributes.get('filter', ''),
+ **self.exception_kwargs)
+
+
+ is_block = True
+
+ @property
+ def is_anonymous(self):
+ return self.name is None
+
+ @property
+ def funcname(self):
+ return self.name or "__M_anon_%d" % (self.lineno, )
+
+ def get_argument_expressions(self, **kw):
+ return self.body_decl.get_argument_expressions(**kw)
+
+ def declared_identifiers(self):
+ return self.body_decl.argnames
+
+ def undeclared_identifiers(self):
+ return []
+
class CallTag(Tag):
__keyword__ = 'call'
diff --git a/test/test_block.py b/test/test_block.py
new file mode 100644
index 0000000..e70b79c
--- /dev/null
+++ b/test/test_block.py
@@ -0,0 +1,569 @@
+from mako.template import Template
+from mako.lookup import TemplateLookup
+from mako import exceptions
+from test import TemplateTest, assert_raises, assert_raises_message
+from util import flatten_result, result_lines
+
+
+
+class BlockTest(TemplateTest):
+ def test_anonymous_block_namespace_raises(self):
+ assert_raises_message(
+ exceptions.CompileException,
+ "Can't put anonymous blocks inside <%namespace>",
+ Template, """
+ <%namespace name="foo">
+ <%block>
+ block
+ </%block>
+ </%namespace>
+ """
+ )
+
+ def test_anonymous_block_in_call(self):
+ template = Template("""
+
+ <%self:foo x="5">
+ <%block>
+ this is the block x
+ </%block>
+ </%self:foo>
+
+ <%def name="foo(x)">
+ foo:
+ ${caller.body()}
+ </%def>
+ """)
+ self._do_test(
+ template,
+ ["foo:", "this is the block x"],
+ filters=result_lines
+ )
+
+ def test_named_block_in_call(self):
+ assert_raises_message(
+ exceptions.CompileException,
+ "Named block 'y' not allowed inside of <%call> tag",
+ Template,"""
+
+ <%self:foo x="5">
+ <%block name="y">
+ this is the block
+ </%block>
+ </%self:foo>
+
+ <%def name="foo(x)">
+ foo:
+ ${caller.body()}
+ ${caller.y()}
+ </%def>
+ """)
+
+ def test_name_collision_blocks_toplevel(self):
+ assert_raises_message(
+ exceptions.CompileException,
+ "%def or %block named 'x' already exists in this template",
+ Template,
+ """
+ <%block name="x">
+ block
+ </%block>
+
+ foob
+
+ <%block name="x">
+ block
+ </%block>
+ """
+ )
+
+ def test_name_collision_blocks_nested_block(self):
+ assert_raises_message(
+ exceptions.CompileException,
+ "%def or %block named 'x' already exists in this template",
+ Template,
+ """
+ <%block>
+ <%block name="x">
+ block
+ </%block>
+
+ foob
+
+ <%block name="x">
+ block
+ </%block>
+ </%block>
+ """
+ )
+
+ def test_name_collision_blocks_nested_def(self):
+ assert_raises_message(
+ exceptions.CompileException,
+ "Named block 'x' not allowed inside of def 'foo'",
+ Template,
+ """
+ <%def name="foo()">
+ <%block name="x">
+ block
+ </%block>
+
+ foob
+
+ <%block name="x">
+ block
+ </%block>
+ </%def>
+ """
+ )
+
+ def test_name_collision_block_def_toplevel(self):
+ assert_raises_message(
+ exceptions.CompileException,
+ "%def or %block named 'x' already exists in this template",
+ Template,
+ """
+ <%block name="x">
+ block
+ </%block>
+
+ foob
+
+ <%def name="x()">
+ block
+ </%def>
+ """
+ )
+
+ def test_name_collision_def_block_toplevel(self):
+ assert_raises_message(
+ exceptions.CompileException,
+ "%def or %block named 'x' already exists in this template",
+ Template,
+ """
+ <%def name="x()">
+ block
+ </%def>
+
+ foob
+
+ <%block name="x">
+ block
+ </%block>
+
+ """
+ )
+
+ def test_named_block_renders(self):
+ template = Template("""
+ above
+ <%block name="header">
+ the header
+ </%block>
+ below
+ """)
+ self._do_test(template, ["above", "the header", "below"],
+ filters=result_lines)
+
+ def test_inherited_block_no_render(self):
+ l = TemplateLookup()
+ l.put_string("index",
+ """
+ <%inherit file="base"/>
+ <%block name="header">
+ index header
+ </%block>
+ """
+ )
+ l.put_string("base","""
+ above
+ <%block name="header">
+ the header
+ </%block>
+
+ ${next.body()}
+ below
+ """)
+ self._do_test(l.get_template("index"),
+ ["above", "index header", "below"],
+ filters=result_lines)
+
+ def test_no_named_in_def(self):
+ assert_raises_message(
+ exceptions.CompileException,
+ "Named block 'y' not allowed inside of def 'q'",
+ Template,
+ """
+ <%def name="q()">
+ <%block name="y">
+ </%block>
+ </%def>
+ """)
+
+ def test_inherited_block_nested_both(self):
+ l = TemplateLookup()
+ l.put_string("index",
+ """
+ <%inherit file="base"/>
+ <%block name="title">
+ index title
+ </%block>
+
+ <%block name="header">
+ index header
+ ${parent.header()}
+ </%block>
+ """
+ )
+ l.put_string("base","""
+ above
+ <%block name="header">
+ base header
+ <%block name="title">
+ the title
+ </%block>
+ </%block>
+
+ ${next.body()}
+ below
+ """)
+ self._do_test(l.get_template("index"),
+ ["above", "index header", "base header", "index title", "below"],
+ filters=result_lines)
+
+ def test_inherited_block_nested_inner_only(self):
+ l = TemplateLookup()
+ l.put_string("index",
+ """
+ <%inherit file="base"/>
+ <%block name="title">
+ index title
+ </%block>
+
+ """
+ )
+ l.put_string("base","""
+ above
+ <%block name="header">
+ base header
+ <%block name="title">
+ the title
+ </%block>
+ </%block>
+
+ ${next.body()}
+ below
+ """)
+ self._do_test(l.get_template("index"),
+ ["above", "base header", "index title", "below"],
+ filters=result_lines)
+
+ def test_noninherited_block_no_render(self):
+ l = TemplateLookup()
+ l.put_string("index",
+ """
+ <%inherit file="base"/>
+ <%block name="some_thing">
+ some thing
+ </%block>
+ """
+ )
+ l.put_string("base","""
+ above
+ <%block name="header">
+ the header
+ </%block>
+
+ ${next.body()}
+ below
+ """)
+ self._do_test(l.get_template("index"),
+ ["above", "the header", "some thing", "below"],
+ filters=result_lines)
+
+ def test_no_conflict_nested_one(self):
+ l = TemplateLookup()
+ l.put_string("index",
+ """
+ <%inherit file="base"/>
+ <%block>
+ <%block name="header">
+ inner header
+ </%block>
+ </%block>
+ """
+ )
+ l.put_string("base","""
+ above
+ <%block name="header">
+ the header
+ </%block>
+
+ ${next.body()}
+ below
+ """)
+ self._do_test(l.get_template("index"),
+ ["above", "inner header", "below"],
+ filters=result_lines)
+
+ def test_nested_dupe_names_raise(self):
+ assert_raises_message(
+ exceptions.CompileException,
+ "%def or %block named 'header' already exists in this template.",
+ Template,
+ """
+ <%inherit file="base"/>
+ <%block name="header">
+ <%block name="header">
+ inner header
+ </%block>
+ </%block>
+ """
+ )
+
+ def test_two_levels_one(self):
+ l = TemplateLookup()
+ l.put_string("index",
+ """
+ <%inherit file="middle"/>
+ <%block name="header">
+ index header
+ </%block>
+ <%block>
+ index anon
+ </%block>
+ """
+ )
+ l.put_string("middle", """
+ <%inherit file="base"/>
+ <%block>
+ middle anon
+ </%block>
+ ${next.body()}
+ """)
+ l.put_string("base","""
+ above
+ <%block name="header">
+ the header
+ </%block>
+
+ ${next.body()}
+ below
+ """)
+ self._do_test(l.get_template("index"),
+ ["above", "index header", "middle anon",
+ "index anon", "below"],
+ filters=result_lines)
+
+ def test_filter(self):
+ template = Template("""
+ <%block filter="h">
+ <html>
+ </%block>
+ """)
+ self._do_test(template, [u'&lt;html&gt;'],
+ filters=result_lines)
+
+ def test_anon_in_named(self):
+ template = Template("""
+ <%block name="x">
+ outer above
+ <%block>
+ inner
+ </%block>
+ outer below
+ </%block>
+ """)
+ self._test_block_in_block(template)
+
+ def test_named_in_anon(self):
+ template = Template("""
+ <%block>
+ outer above
+ <%block name="x">
+ inner
+ </%block>
+ outer below
+ </%block>
+ """)
+ self._test_block_in_block(template)
+
+ def test_anon_in_anon(self):
+ template = Template("""
+ <%block>
+ outer above
+ <%block>
+ inner
+ </%block>
+ outer below
+ </%block>
+ """)
+ self._test_block_in_block(template)
+
+ def test_named_in_named(self):
+ template = Template("""
+ <%block name="x">
+ outer above
+ <%block name="y">
+ inner
+ </%block>
+ outer below
+ </%block>
+ """)
+ self._test_block_in_block(template)
+
+ def _test_block_in_block(self, template):
+ self._do_test(template,
+ ["outer above", "inner", "outer below"],
+ filters=result_lines
+ )
+
+ def test_iteration(self):
+ t = Template("""
+ % for i in (1, 2, 3):
+ <%block>${i}</%block>
+ % endfor
+ """)
+ self._do_test(t,
+ ["1", "2", "3"],
+ filters=result_lines
+ )
+
+ def test_conditional(self):
+ t = Template("""
+ % if True:
+ <%block>true</%block>
+ % endif
+
+ % if False:
+ <%block>false</%block>
+ % endif
+ """)
+ self._do_test(t,
+ ["true"],
+ filters=result_lines
+ )
+
+ def test_block_overridden_by_def(self):
+ l = TemplateLookup()
+ l.put_string("index",
+ """
+ <%inherit file="base"/>
+ <%def name="header()">
+ inner header
+ </%def>
+ """
+ )
+ l.put_string("base","""
+ above
+ <%block name="header">
+ the header
+ </%block>
+
+ ${next.body()}
+ below
+ """)
+ self._do_test(l.get_template("index"),
+ ["above", "inner header", "below"],
+ filters=result_lines)
+
+ def test_def_overridden_by_block(self):
+ l = TemplateLookup()
+ l.put_string("index",
+ """
+ <%inherit file="base"/>
+ <%block name="header">
+ inner header
+ </%block>
+ """
+ )
+ l.put_string("base","""
+ above
+ ${self.header()}
+ <%def name="header()">
+ the header
+ </%def>
+
+ ${next.body()}
+ below
+ """)
+ self._do_test(l.get_template("index"),
+ ["above", "inner header", "below"],
+ filters=result_lines)
+
+ def test_block_args(self):
+ l = TemplateLookup()
+ l.put_string("caller", """
+
+ <%include file="callee" args="val1='3', val2='4'"/>
+
+ """)
+ l.put_string("callee", """
+ <%page args="val1, val2"/>
+ <%block name="foob" args="val1, val2">
+ foob, ${val1}, ${val2}
+ </%block>
+ """)
+ self._do_test(
+ l.get_template("caller"),
+ [u'foob, 3, 4'],
+ filters=result_lines
+ )
+
+ def test_block_variables_contextual(self):
+ t = Template("""
+ <%block name="foob" >
+ foob, ${val1}, ${val2}
+ </%block>
+ """)
+ self._do_test(
+ t,
+ [u'foob, 3, 4'],
+ template_args={'val1':3, 'val2':4},
+ filters=result_lines
+ )
+
+ def test_block_args_contextual(self):
+ t = Template("""
+ <%page args="val1"/>
+ <%block name="foob" args="val1">
+ foob, ${val1}, ${val2}
+ </%block>
+ """)
+ self._do_test(
+ t,
+ [u'foob, 3, 4'],
+ template_args={'val1':3, 'val2':4},
+ filters=result_lines
+ )
+
+ def test_block_pageargs_contextual(self):
+ t = Template("""
+ <%block name="foob">
+ foob, ${pageargs['val1']}, ${pageargs['val2']}
+ </%block>
+ """)
+ self._do_test(
+ t,
+ [u'foob, 3, 4'],
+ template_args={'val1':3, 'val2':4},
+ filters=result_lines
+ )
+
+ def test_block_pageargs(self):
+ l = TemplateLookup()
+ l.put_string("caller", """
+
+ <%include file="callee" args="val1='3', val2='4'"/>
+
+ """)
+ l.put_string("callee", """
+ <%block name="foob">
+ foob, ${pageargs['val1']}, ${pageargs['val2']}
+ </%block>
+ """)
+ self._do_test(
+ l.get_template("caller"),
+ [u'foob, 3, 4'],
+ filters=result_lines
+ ) \ No newline at end of file