From 58de0fc6967c3428178a7b5dc840b8b3963fb443 Mon Sep 17 00:00:00 2001
From: Mike Bayer <mike_mp@zzzcomputing.com>
Date: Fri, 15 Dec 2006 19:01:09 +0000
Subject: [PATCH] - fixes and tests involving exceptions propigating from
 buffered/ccall sections - more docs

---
 doc/build/content/documentation.html    |  8 +++---
 doc/build/content/index.html            |  2 +-
 doc/build/content/syntax.txt            | 32 +++++++++++++++++++++
 doc/build/content/usage.txt             | 12 ++++----
 doc/build/genhtml.py                    |  3 +-
 doc/build/read_markdown.py              |  6 ++--
 doc/build/templates/base.html           | 10 +++----
 doc/build/templates/content_layout.html |  4 +--
 doc/build/templates/formatting.html     |  6 ++--
 doc/build/templates/nav.html            | 29 +++++++++++--------
 doc/build/templates/toc.html            |  8 +++---
 doc/docs.css                            |  4 +--
 lib/mako/codegen.py                     |  2 +-
 lib/mako/runtime.py                     |  5 +++-
 test/def.py                             |  1 +
 test/filters.py                         | 38 ++++++++++++++++++++++++-
 16 files changed, 126 insertions(+), 44 deletions(-)
 create mode 100644 doc/build/content/syntax.txt

diff --git a/doc/build/content/documentation.html b/doc/build/content/documentation.html
index 250c511..3fbdfec 100644
--- a/doc/build/content/documentation.html
+++ b/doc/build/content/documentation.html
@@ -2,7 +2,7 @@
 <%namespace name="tocns" file="toc.html"/>
 <%namespace name="nav" file="nav.html"/>
 
-${tocns.toc(toc=toc, extension=extension, paged=False)}
+${tocns.toc()}
 
 <%def name="title()">
     Mako Documentation
@@ -10,15 +10,15 @@ ${tocns.toc(toc=toc, extension=extension, paged=False)}
 
 % for file in toc.filenames:
     <%
-        item = toc.get_by_file(file)
+        item = requestattr['toc'].get_by_file(file)
     %>
     
     <A name="${item.path}"></a>
 
     <div class="prevnext">
-        ${nav.pagenav(item=item, paged=False)}
+        ${nav.pagenav(item=item)}
     </div>
 
-    ${self.get_namespace(file + '.html').body(paged=False, toc=toc, extension=extension)}
+    ${self.get_namespace(file + '.html').body()}
 % endfor
 
diff --git a/doc/build/content/index.html b/doc/build/content/index.html
index d650a5a..f7157be 100644
--- a/doc/build/content/index.html
+++ b/doc/build/content/index.html
@@ -1,4 +1,4 @@
 <%inherit file="base.html"/>
 <%namespace name="tocns" file="toc.html"/>
 
-${tocns.toc(toc=toc, extension=extension)}
+${tocns.toc()}
diff --git a/doc/build/content/syntax.txt b/doc/build/content/syntax.txt
new file mode 100644
index 0000000..5612a23
--- /dev/null
+++ b/doc/build/content/syntax.txt
@@ -0,0 +1,32 @@
+Mako Syntax {@name=syntax}
+==============================
+
+A Mako template is parsed from a text stream containing any kind of content, XML, HTML, email text, etc.  The template can further contain Mako-specific directives which represent variable and/or expression substitutions, control structures (i.e. conditionals and loops), server-side comments, full blocks of Python code, as well as various tags that offer additional functionality.  Note that all of these constructs compile into real Python code.  This means that you can leverage the full power of Python in almost every aspect of a Mako template.
+
+### Expression Substiution
+
+The simplest expression is just a variable substitution.  The syntax for this is the `${}` construct, which is inspired by Perl, Genshi, JSP EL, and many others:
+
+    this is x: ${x}
+
+Above, the string representation of `x` is applied to the template's output stream.  If you're wondering where `x` comes from, it usually would have been supplied to the template's context when the template is rendered.  If `x` was not supplied to the template and was not otherwise assigned locally, it evaluates to a special value `UNDEFINED`.  More on that later.
+    
+The contents within the `${}` tag are evaluated by Python directly, so full expressions are OK:
+
+    pythagorean theorem:  ${pow(x,2) + pow(y,2)}
+    
+The results of the expression are evaluated into a string result in all cases before being rendered to the output stream, so our above example which produces a numeric result is OK.
+
+#### Expression Escaping
+
+Mako includes a number of built-in escaping mechanisms, including HTML, URI and XML escaping, as well as a "trim" function.  These escapes can be added to an expression substituion using the `|` operator:
+
+    ${"this is some text" | u}
+
+The above expression applies URL escaping to the expression, and produces `this+is+some+text`.  the `u` name indicates URL escaping, whereas `h` represents HTML escaping, `x` represents XML escaping, and `trim` applies a trim function:
+
+    ${"  <tag>some value</tag> " | h,trim}
+
+The above produces `&lt;tag&gt;some value&lt;/tag&gt;`.  The HTML escaping function is applied first, the "trim" function second, which trims the leading and trailing whitespace from the expression.
+
+Naturally, you can make your own expression filters - that's described in [filtering](rel:filtering).
\ No newline at end of file
diff --git a/doc/build/content/usage.txt b/doc/build/content/usage.txt
index 5808a4c..bd4c7b4 100644
--- a/doc/build/content/usage.txt
+++ b/doc/build/content/usage.txt
@@ -1,6 +1,8 @@
 Basic Usage {@name=usage}
 ==========================
 
+This section describes the Python API for Mako templates.  If you are using Mako within a web framework such as Pylons, the work of integrating Mako's API is already done for you, in which case you can skip to the next section, [syntax](rel:syntax).
+
 The most basic way to create a template and render it is through the `Template` class:
 
     from mako.template import Template
@@ -8,16 +10,16 @@ The most basic way to create a template and render it is through the `Template`
     mytemplate = Template("hello world!")
     print mytemplate.render()
     
-Above, the text argument to `Template` is **compiled** into a Python module representation internally, which contains a function called `render_body()`.  When you then call `mytemplate.render()`, Mako sets up a runtime environment for the template and calls this method, capturing the output into a buffer and returning it's string contents.
+Above, the text argument to `Template` is **compiled** into a Python module representation.  This module contains a function called `render_body()`, which when executed produces the output of the template.  When you call `mytemplate.render()`, Mako sets up a runtime environment for the template and calls the `render_body()` method, capturing the output into a buffer and returning it's string contents.
 
-When a template is rendering, the code inside the template's generated `render_body()` method has access to a namespace of variables.  You can specify these variables by sending them as additional keyword arguments to the `render()` method:
+The code inside the `render_body()` method has access to a namespace of variables.  You can specify these variables by sending them as additional keyword arguments to the `render()` method:
 
     from mako.template import Template
     
     mytemplate = Template("hello, ${name}!")
     print mytemplate.render(name="jack")
     
-Internally, the `render()` method calls upon Mako to create a `Context` object, which stores all the variable names accessible to the template and also defines a buffer used to capture output.  You can create this `Context` yourself and have the template render with it, using the `render_context` method:
+The `template.render()` method calls upon Mako to create a `Context` object, which stores all the variable names accessible to the template and also defines a buffer used to capture output.  You can create this `Context` yourself and have the template render with it, using the `render_context` method:
 
     from mako.template import Template
     from mako.runtime import Context
@@ -38,7 +40,7 @@ A `Template` can also load its template source code from a file, using the `file
     mytemplate = Template(filename='/docs/mytmpl.txt')
     print mytemplate.render()
     
-When the `Template` is created, it generates Python code which is assembled into a Python module stored in memory.  This module is accessible off the `Template` instance as `sometemplate.module`.    For improved performance, a `Template` which is loaded from a file can also cache its module on the filesystem as a regular Python module file (i.e. a .py file).  To do this, just add the `module_directory` argument to the template:
+For improved performance, a `Template` which is loaded from a file can also cache the source code to its generated module on the filesystem as a regular Python module file (i.e. a .py file).  To do this, just add the `module_directory` argument to the template:
 
     from mako.template import Template
 
@@ -70,7 +72,7 @@ Usually, an application will store most or all of its templates as text files on
         mytemplate = mylookup.get_template(templatename)
         print mytemplate.render(**kwargs)
 
-In the example above, we create a `TemplateLookup` which will look for templates in the `/docs` directory, and when it creates new `Template` objects will specify the module directory of `/tmp/mako_modules`.  The lookup locates templates by appending the given URI to each of its search directories; so if you gave it a URI of `/etc/beans/info.txt`, it would search for the file `/docs/etc/beans/info.txt`, else raise a `TopLevelNotFound` exception, which is a custom Mako exception.
+In the example above, we create a `TemplateLookup` which will look for templates in the `/docs` directory, and will store generated module files in the `/tmp/mako_modules` directory.  The lookup locates templates by appending the given URI to each of its search directories; so if you gave it a URI of `/etc/beans/info.txt`, it would search for the file `/docs/etc/beans/info.txt`, else raise a `TopLevelNotFound` exception, which is a custom Mako exception.
 
 The `TemplateLookup` also serves the important need of caching a fixed set of templates in memory at a given time, so that successive URI-lookups do not result in full filesystem lookups each time.  By default, the `TemplateLookup` size is unbounded.  You can specify a fixed size using the `collection_size` argument:
 
diff --git a/doc/build/genhtml.py b/doc/build/genhtml.py
index bac3ed3..5e1af62 100644
--- a/doc/build/genhtml.py
+++ b/doc/build/genhtml.py
@@ -13,6 +13,7 @@ files = [
     'index',
     'documentation',
     'usage',
+    'syntax',
     'defs',
     'namespaces',
     'caching',
@@ -40,7 +41,7 @@ def genfile(name, toc):
     outname = os.path.join(os.getcwd(), '../', name + ".html")
     outfile = file(outname, 'w')
     print infile, '->', outname
-    outfile.write(lookup.get_template(infile).render(toc=toc, extension='html'))
+    outfile.write(lookup.get_template(infile).render(requestattr={}, toc=toc, extension='html'))
     
 for filename in files:
     try:
diff --git a/doc/build/read_markdown.py b/doc/build/read_markdown.py
index f8bd18a..49ebdf6 100644
--- a/doc/build/read_markdown.py
+++ b/doc/build/read_markdown.py
@@ -74,7 +74,7 @@ def create_toc(filename, tree, tocroot):
 
             level[0] = taglevel
 
-            tag = et.Element("MAKO:formatting.section", path=repr(current[0].path), toc="toc", paged="paged")
+            tag = et.Element("MAKO:formatting.section", path=repr(current[0].path))
             tag.text = (node.tail or "") + '\n'
             tag.tail = '\n'
             tag[:] = content
@@ -114,9 +114,9 @@ def process_rel_href(tree):
             (bold, path) = m.group(1,2)
             text = a.text
             if text == path:
-                tag = et.Element("MAKO:nav.toclink", path=repr(path), toc="toc", extension="extension")
+                tag = et.Element("MAKO:nav.toclink", path=repr(path))
             else:
-                tag = et.Element("MAKO:nav.toclink", path=repr(path), description=repr(text), toc="toc", extension="extension")
+                tag = et.Element("MAKO:nav.toclink", path=repr(path), description=repr(text))
             a_parent = parent[a]
             if bold:
                 bold = et.Element('strong')
diff --git a/doc/build/templates/base.html b/doc/build/templates/base.html
index 476f866..109a111 100644
--- a/doc/build/templates/base.html
+++ b/doc/build/templates/base.html
@@ -24,10 +24,10 @@
     last_updated = toc.last_updated
 
     extension = context.get('extension', 'html')
-    if paged is UNDEFINED:
-        ispaged = True
-    else:
-        ispaged = paged
+    paged = context.get('paged', True)
+    requestattr['extension'] = extension
+    requestattr['paged'] = paged
+    requestattr['toc'] = toc
 %>
 
 <div id="topanchor"><a name="top"></a>&nbsp;</div>
@@ -38,7 +38,7 @@
 
 <div class="versionheader">Version: ${version}   Last Updated: ${time.strftime('%x %X', time.localtime(last_updated))}</div>
 
-${next.body(toc=toc, paged=ispaged)}
+${next.body()}
 
 
 
diff --git a/doc/build/templates/content_layout.html b/doc/build/templates/content_layout.html
index 5816866..b82deb2 100644
--- a/doc/build/templates/content_layout.html
+++ b/doc/build/templates/content_layout.html
@@ -9,6 +9,6 @@
 
 <A name="<% current.path %>"></a>
 
-${topnav(item=current, paged=paged)}
+${topnav(item=current)}
 
-${next.body(toc=toc, paged=paged)}
+${next.body()}
diff --git a/doc/build/templates/formatting.html b/doc/build/templates/formatting.html
index 96ca698..fdb2139 100644
--- a/doc/build/templates/formatting.html
+++ b/doc/build/templates/formatting.html
@@ -13,9 +13,10 @@
 
 <%namespace name="nav" file="nav.html"/>
 
-<%def name="section(toc, path, description=None, paged=True)">
+<%def name="section(path, description=None)">
     # Main section formatting element.
     <%
+        toc = requestattr['toc']
         item = toc.get_by_path(path)
         subsection = item.depth > 1
     %>
@@ -23,6 +24,7 @@
     
     <div class="${subsection and 'subsection' or 'section'}">
     <%
+        caller.body()
         content = capture(caller.body)
         re2 = re.compile(r"'''PYESC(.+?)PYESC'''", re.S)
         content = re2.sub(lambda m: m.group(1), content)
@@ -39,7 +41,7 @@
     % else:
         % if paged:
         <div class="prevnext">
-            ${nav.pagenav(item=item, paged=paged)}
+            ${nav.pagenav(item=item)}
         </div>
         % endif
         <a href="#${ item.get_page_root().path }">back to section top</a>
diff --git a/doc/build/templates/nav.html b/doc/build/templates/nav.html
index 25bc898..a4b6044 100644
--- a/doc/build/templates/nav.html
+++ b/doc/build/templates/nav.html
@@ -2,21 +2,26 @@
 # individual hyperlinks as well as navigational toolbars and table-of-content listings.
 <%namespace name="tocns" file="toc.html"/>
 
-<%def name="itemlink(item, anchor=True, extension='html', paged=True)" filter="trim">
-    <a href="${ item.get_link(extension=extension, anchor=anchor, usefilename=paged) }">${ item.description }</a>
+<%def name="itemlink(item, anchor=True)" filter="trim">
+    <a href="${ item.get_link(anchor=anchor, usefilename=requestattr['paged']) }">${ item.description }</a>
 </%def>
 
-<%def name="toclink(toc, path, description=None)" filter="trim">
+<%def name="toclink(path, description=None)" filter="trim">
     <%
+        toc = requestattr['toc']
         item = toc.get_by_path(path)
         if description is None:
             if item:
                 description = item.description
             else:
                 description = path
+        if item:
+            anchor = not requestattr['paged'] or item.depth > 1
+        else: 
+            anchor = False
     %>
     % if item:
-        <a href="${ item.get_link(extension=extension) }">${ description }</a>
+        <a href="${ item.get_link(extension=requestattr['extension'], anchor=anchor, usefilename=requestattr['paged']) }">${ description }</a>
     % else:
         <b>${ description }</b>
     % endif
@@ -27,28 +32,28 @@
     <a href="${ href }" ${ class_ and (('class=\"%s\"' % class_) or '')}>${ text }</a>
 </%def>
 
-<%def name="topnav(item, extension='html', paged=True)">
+<%def name="topnav(item)">
     <div class="topnav">
 
     <div class="prevnext">
-        ${pagenav(item, paged=paged)}
+        ${pagenav(item)}
     </div>
 
-    <h3><a href="index.${ extension }">Table of Contents</a></h3>
+    <h3><a href="index.${ requestattr['extension'] }">Table of Contents</a></h3>
     <br/>
-	${itemlink(item=item, paged=paged, extension=extension, anchor=True)}
+	${itemlink(item=item, anchor=True)}
 	
-	${tocns.printtoc(root=item, current=None, full=True, anchor_toplevel=True, paged=paged)}
+	${tocns.printtoc(root=item, current=None, full=True, anchor_toplevel=True)}
 	</div>
 </%def>
 
-<%def name="pagenav(item, paged=True)">
+<%def name="pagenav(item)">
     % if item.previous is not None:
-        Previous: ${itemlink(item=item.previous, paged=paged, anchor=not paged)}
+        Previous: ${itemlink(item=item.previous, anchor=not requestattr['paged'])}
     % endif
 
     % if item.next is not None:
         ${item.previous is not None and "|" or ""}
-        Next: ${itemlink(item=item.next, paged=paged, anchor=not paged)}
+        Next: ${itemlink(item=item.next, anchor=not requestattr['paged'])}
     % endif
 </%def>
diff --git a/doc/build/templates/toc.html b/doc/build/templates/toc.html
index 8f0eafc..9048a99 100644
--- a/doc/build/templates/toc.html
+++ b/doc/build/templates/toc.html
@@ -1,24 +1,24 @@
 # toc.myt - prints full table of contents listings given toc.TOCElement strucures
 
-<%def name="toc(toc, paged=True, extension='html')">
+<%def name="toc()">
 	<div class="topnav">
 
 	<a name="full_index"></a>
 	<h3>Table of Contents</h3>
 	
-	${printtoc(root=toc,current=None,full=True,children=True,anchor_toplevel=False, paged=paged)}
+	${printtoc(root=requestattr['toc'],current=None,full=True,children=True,anchor_toplevel=False)}
 
 	</div>
 </%def>
 
 
-<%def name="printtoc(root,current=None,full=False,children=True,anchor_toplevel=False,extension='html',paged=True)">
+<%def name="printtoc(root,current=None,full=False,children=True,anchor_toplevel=False)">
     <ul>
     % for item in root.children:
         <li><A style="${item is current and "font-weight:bold;" or "" }" href="${item.get_link(extension=extension,anchor=anchor_toplevel, usefilename=paged) }">${item.description}</a></li>
         
         % if children:
-            ${printtoc(item, current=current, full=full,children=True,anchor_toplevel=True, paged=paged)}
+            ${printtoc(item, current=current, full=full,children=True,anchor_toplevel=True)}
         % endif
     % endfor
     </ul>
diff --git a/doc/docs.css b/doc/docs.css
index 34462dc..844c88c 100644
--- a/doc/docs.css
+++ b/doc/docs.css
@@ -58,7 +58,7 @@ h2 {
     font-size:.80em;
 }
 code {
-    font-size:1.1em;
+    font-size:1.2em;
 }
 .code {
     font-family:monospace;
@@ -77,7 +77,7 @@ td {
 pre { 
     margin: 1.5em; 
     padding: .5em; 
-    font-size: .95em; 
+    font-size: 1em; 
     line-height:1em;
     background-color: #eee;
     border: 1px solid #ccc;
diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py
index 072ce54..e744ee7 100644
--- a/lib/mako/codegen.py
+++ b/lib/mako/codegen.py
@@ -327,12 +327,12 @@ class _GenerateRenderMethod(object):
             s = "_buf.getvalue()"
             if filtered:
                 s = self.create_filter_callable(node.filter_args.args, s)
+            self.printer.writeline(None)
             if buffered or cached:
                 self.printer.writeline("return %s" % s)
             else:
                 self.printer.writeline("context.write(%s)" % s)
                 self.printer.writeline("return ''")
-            self.printer.writeline(None)
 
     def write_cache_decorator(self, node_or_pagetag, name, buffered, identifiers):
         """write a post-function decorator to replace a rendering callable with a cached version of itself."""
diff --git a/lib/mako/runtime.py b/lib/mako/runtime.py
index 42fb036..254ddee 100644
--- a/lib/mako/runtime.py
+++ b/lib/mako/runtime.py
@@ -154,6 +154,9 @@ class Namespace(object):
                     yield (k, getattr(self.module, k))
                             
     def __getattr__(self, key):
+        return self._get_callable(key)
+        
+    def _get_callable(self, key):
         if self.callables is not None:
             try:
                 return self.callables[key]
@@ -184,7 +187,7 @@ def capture(context, callable_, *args, **kwargs):
         callable_(*args, **kwargs)
     finally:
         buf = context.pop_buffer()
-        return buf.getvalue()
+    return buf.getvalue()
         
 def _include_file(context, uri, calling_uri):
     """locate the template from the given uri and include it in the current output."""
diff --git a/test/def.py b/test/def.py
index 52afeae..4214fc7 100644
--- a/test/def.py
+++ b/test/def.py
@@ -25,6 +25,7 @@ class DefTest(unittest.TestCase):
         ${mycomp()}""")
         assert template.render(variable='hi').strip() == """hello mycomp hi"""
 
+        
     def test_def_args(self):
         template = Template("""
         <%def name="mycomp(a, b)">
diff --git a/test/filters.py b/test/filters.py
index 2503310..b844f3a 100644
--- a/test/filters.py
+++ b/test/filters.py
@@ -66,7 +66,43 @@ class BufferTest(unittest.TestCase):
             ${"hi->" + capture(foo) + "<-hi"}
 """)
         assert flatten_result(t.render()) == "hi-> this is foo <-hi"
-        
+
+    def test_capture_exception(self):
+        template = Template("""
+            <%def name="a()">
+                this is a
+                <% 
+                    raise TypeError("hi")
+                %>
+            </%def>
+            <%
+                c = capture(a)
+            %>
+            a->${c}<-a
+        """)
+        try:
+            template.render()
+            assert False
+        except TypeError:
+            assert True
+    
+    def test_buffered_exception(self):
+        template = Template("""
+            <%def name="a()" buffered="True">
+                <%
+                    raise TypeError("hi")
+                %>
+            </%def>
+            
+            ${a()}
+            
+""") 
+        try:
+            print template.render()
+            assert False
+        except TypeError:
+            assert True
+            
     def test_capture_ccall(self):
         t = Template("""
             <%def name="foo">
-- 
GitLab