diff --git a/doc/build/genhtml.py b/doc/build/genhtml.py index d72b93b950bdc380e6759347bd3be8b71818a8f4..2e08c092e0a62da5272ad7bd8505b6570fc71513 100644 --- a/doc/build/genhtml.py +++ b/doc/build/genhtml.py @@ -35,13 +35,10 @@ 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(toc=toc, extension='html')) -try: - for filename in files: - genfile(filename, root) -except exception.Error, e: - sys.stderr.write(e.textformat()) +for filename in files: + genfile(filename, root) diff --git a/doc/build/read_markdown.py b/doc/build/read_markdown.py index a7f5d74354a5502ea1296852f518c1d641a51e30..5bf68688fcabdc19e0e861278fc6465ae81c3bb7 100644 --- a/doc/build/read_markdown.py +++ b/doc/build/read_markdown.py @@ -34,22 +34,15 @@ def dump_tree(elem, stream): def dump_mako_tag(elem, stream): tag = elem.tag[5:] - params = ', '.join(['%s=%s' % i for i in elem.items()]) - pipe = '' - if elem.text or len(elem): - pipe = '|' - comma = '' - if params: - comma = ', ' - stream.write('<&%s%s%s%s&>' % (pipe, tag, comma, params)) - if pipe: - if elem.text: - stream.write(elem.text) - for n in elem: - dump_tree(n, stream) - if n.tail: - stream.write(n.tail) - stream.write("</&>") + params = ','.join(['%s=%s' % i for i in elem.items()]) + stream.write('<%%call expr="%s(%s)">' % (tag, params)) + if elem.text: + stream.write(elem.text) + for n in elem: + dump_tree(n, stream) + if n.tail: + stream.write(n.tail) + stream.write("</%call>") def create_toc(filename, tree, tocroot): title = [None] @@ -81,7 +74,7 @@ def create_toc(filename, tree, tocroot): level[0] = taglevel - tag = et.Element("MAKO:formatting.section", path=literal(current[0].path), toc="toc") + tag = et.Element("MAKO:self.formatting.section", path=repr(current[0].path), toc="toc") tag.text = (node.tail or "") + '\n' tag.tail = '\n' tag[:] = content @@ -92,9 +85,6 @@ def create_toc(filename, tree, tocroot): process(tree) return (title[0], tocroot.get_by_file(filename)) -def literal(s): - return '"%s"' % s - def index(parent, item): for n, i in enumerate(parent): if i is item: @@ -124,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=literal(path), toc="toc", extension="extension") + tag = et.Element("MAKO:self.nav.toclink", path=repr(path), toc="toc", extension="extension") else: - tag = et.Element("MAKO:nav.toclink", path=literal(path), description=literal(text), toc="toc", extension="extension") + tag = et.Element("MAKO:self.nav.toclink", path=repr(path), description=repr(text), toc="toc", extension="extension") a_parent = parent[a] if bold: bold = et.Element('strong') @@ -138,50 +128,30 @@ def process_rel_href(tree): a_parent[index(a_parent, a)] = tag def replace_pre_with_mako(tree): - def splice_code_tag(pre, text, type=None, title=None): - doctest_directives = re.compile(r'#\s*doctest:\s*[+-]\w+(,[+-]\w+)*\s*$', re.M) - text = re.sub(doctest_directives, '', text) - # process '>>>' to have quotes around it, to work with the mako python - # syntax highlighter which uses the tokenize module - text = re.sub(r'>>> ', r'">>>" ', text) - - # indent two spaces. among other things, this helps comment lines "# " from being - # consumed as mako comments. - text = re.compile(r'^(?!<&)', re.M).sub(' ', text) - - use_sliders = True - - opts = {} - if type == 'python': - opts['syntaxtype'] = literal('python') - else: - opts['syntaxtype'] = None - - if title is not None: - opts['title'] = literal(title) - - if use_sliders: - opts['use_sliders'] = True - - tag = et.Element("MAKO:formatting.code", **opts) - tag.text = text + parents = get_parent_map(tree) - pre_parent = parents[pre] + for precode in tree.findall('.//pre/code'): + tag = et.Element("MAKO:self.formatting.code") + tag.text = precode.text + [tag.append(x) for x in precode] + pre = parents[precode] tag.tail = pre.tail + + pre_parent = parents[pre] pre_parent[reverse_parent(pre_parent, pre)] = tag - + +def safety_code(tree): parents = get_parent_map(tree) - - for precode in tree.findall('.//pre/code'): - m = re.match(r'\{(python|code)(?: title="(.*?)"){0,1}\}', precode.text.lstrip()) - if m: - code = m.group(1) - title = m.group(2) - text = precode.text.lstrip() - text = re.sub(r'{(python|code).*?}(\n\s*)?', '', text) - splice_code_tag(parents[precode], text, type=code, title=title) - elif precode.text.lstrip().startswith('>>> '): - splice_code_tag(parents[precode], precode.text) + for code in tree.findall('.//code'): + tag = et.Element('%text') + tag.text = code.text + code.append(tag) + code.text = "" + #code.tail =None + #tag.text = code.text + #code_parent = parents[code] + #code_parent[reverse_parent(code_parent, code)] = tag + def reverse_parent(parent, item): for n, i in enumerate(parent): @@ -215,6 +185,7 @@ def parse_markdown_files(toc, files): html = markdown.markdown(file(infile).read()) tree = et.fromstring("<html>" + html + "</html>") (title, toc_element) = create_toc(inname, tree, toc) + safety_code(tree) replace_pre_with_mako(tree) process_rel_href(tree) outname = 'output/%s.html' % inname diff --git a/doc/build/templates/base.html b/doc/build/templates/base.html index 2163c42859ce7b2d2484fda018e23fa4dbf71f61..c272b1482e23cc1991197dd5a12845184d688b04 100644 --- a/doc/build/templates/base.html +++ b/doc/build/templates/base.html @@ -1,15 +1,13 @@ # base.html - common to all documentation pages. intentionally separate # from autohandler, which can be swapped out for a different one <% - if m.cache_self(key=m.request_component.file): - return # bootstrap TOC structure from request args, or pickled file if not present. import cPickle as pickle import os, time print "base.myt generating from table of contents for file %s" % ("<todo: get the file>") toc = context.get('toc') if toc is None: - filename = os.path.join(os.path.dirname(m.request_component.file), 'table_of_contents.pickle') + filename = os.path.join(os.path.dirname(self.template.filename), 'table_of_contents.pickle') toc = pickle.load(file(filename)) version = toc.version last_updated = toc.last_updated @@ -29,7 +27,7 @@ <div class="">Version: <% version %> Last Updated: <% time.strftime('%x %X', time.localtime(last_updated)) %></div> </div> -% m.call_next(toc=toc, extension=extension) +${next.body(toc=toc)} </div> diff --git a/doc/build/templates/content_layout.html b/doc/build/templates/content_layout.html index 252515559d69a0516b096183e085f65bec7f5118..fa354d23370fb5356aebd2c2bb3e0b4d0a5332f3 100644 --- a/doc/build/templates/content_layout.html +++ b/doc/build/templates/content_layout.html @@ -1,11 +1,11 @@ # defines the default layout for normal documentation pages (not including the index) -<%inherit file="base.html"> +<%inherit file="root.html"/> <% current = toc.get_by_file(self.template.module.filename) %> <A name="<% current.path %>"></a> -<& nav.myt:topnav, item=current, extension=extension &> +${self.nav.topnav(item=current)} <div class="sectioncontent"> -% m.call_next(toc=toc, extension=extension) +${next.body(toc=toc)} </div> diff --git a/doc/build/templates/formatting.html b/doc/build/templates/formatting.html index da1c81df05b265432fc2c485a99c4acf7d32b7a2..d7a42f1e15d76149e1e2c265318fd719721d0593 100644 --- a/doc/build/templates/formatting.html +++ b/doc/build/templates/formatting.html @@ -1,80 +1,65 @@ -<%doc>formatting.myt - Provides section formatting elements, syntax-highlighted code blocks, and other special filters.</%doc> - -<%global> +# formatting.myt - Provides section formatting elements, syntax-highlighted code blocks, and other special filters. +<%! import string, re - import highlight -</%global> - -<%method section> -<%doc>Main section formatting element.</%doc> -<%args> - toc - path - description=None -</%args> -<%init> + #import highlight + + def plainfilter(f): + f = re.sub(r'\n[\s\t]*\n[\s\t]*', '</p>\n<p>', f) + f = "<p>" + f + "</p>" + return f + +%> + +<%def name="section(toc, path, description=None)"> +# Main section formatting element. +<% item = toc.get_by_path(path) if item is None: raise "path: " + path -</%init> +%> -<A name="<% item.path %>"></a> +<A name="${item.path}"></a> -<div class="subsection" style="margin-left:<% repr(item.depth * 10) %>px;"> +<div class="subsection" style="margin-left:${repr(item.depth * 10)}px;"> -<%python> - content = m.content() +<% + content = caller.body() re2 = re.compile(r"'''PYESC(.+?)PYESC'''", re.S) content = re2.sub(lambda m: m.group(1), content) -</%python> +%> % if item.depth > 1: -<h3><% description or item.description %></h3> -% +<h3>${description or item.description}</h3> +% endif <div class="sectiontext"> - <% content %> + ${content} </div> % if item.depth > 1: % if (item.next and item.next.depth >= item.depth): - <a href="#<% item.get_page_root().path %>" class="toclink">back to section top</a> -% + <a href="#${ item.get_page_root().path }" class="toclink">back to section top</a> +% endif % else: - <a href="#<% item.get_page_root().path %>" class="toclink">back to section top</a> - <& nav.myt:pagenav, item=item &> -% + <a href="#${ item.get_page_root().path }" class="toclink">back to section top</a> + ${nav.pagenav(item=item)} +% endif </div> -</%method> - - -<%method formatplain> - <%filter> - import re - f = re.sub(r'\n[\s\t]*\n[\s\t]*', '</p>\n<p>', f) - f = "<p>" + f + "</p>" - return f - </%filter> -<% m.content() | h%> -</%method> - +</%def> +<%def name="formatplain" filter="plainfilter"> +${ caller.body() | h} +</%def> -<%method codeline trim="both"> -<span class="codeline"><% m.content() %></span> -</%method> -<%method code autoflush=False> -<%args> - title = None - syntaxtype = 'python' - html_escape = False - use_sliders = False -</%args> +<%def name="codeline" filter="trim"> +<span class="codeline">${ m.content() }</span> +</%def> -<%init> +<%def name="code(title=None, syntaxtype='python', html_escape=False, use_sliders=False)"> +<% def fix_indent(f): f =string.expandtabs(f, 4) g = '' @@ -98,68 +83,17 @@ p = re.compile(r'<pre>(.*?)</pre>', re.S) def hlight(match): return "<pre>" + highlight.highlight(fix_indent(match.group(1)), html_escape = html_escape, syntaxtype = syntaxtype) + "</pre>" - content = p.sub(hlight, "<pre>" + m.content() + "</pre>") -</%init> -<div class="<% use_sliders and "sliding_code" or "code" %>"> + #content = p.sub(hlight, "<pre>" + capture(caller.body) + "</pre>") + content = "<pre>" + capture(caller.body) + "</pre>" +%> + +<div class="${ use_sliders and "sliding_code" or "code" }"> % if title is not None: - <div class="codetitle"><% title %></div> -% -<% content %></div> -</%method> - - - - -<%method popboxlink trim="both"> - <%args> - name=None - show='show' - hide='hide' - </%args> - <%init> - if name is None: - name = m.attributes.setdefault('popbox_name', 0) - name += 1 - m.attributes['popbox_name'] = name - name = "popbox_" + repr(name) - </%init> -javascript:togglePopbox('<% name %>', '<% show %>', '<% hide %>') -</%method> - -<%method popbox trim="both"> -<%args> - name = None - class_ = None -</%args> -<%init> - if name is None: - name = 'popbox_' + repr(m.attributes['popbox_name']) -</%init> -<div id="<% name %>_div" class="<% class_ %>" style="display:none;"><% m.content().strip() %></div> -</%method> - -<%method poplink trim="both"> - <%args> - link='sql' - </%args> - <%init> - href = m.scomp('SELF:popboxlink') - </%init> - '''PYESC<& nav.myt:link, href=href, text=link, class_="codepoplink" &>PYESC''' -</%method> - -<%method codepopper trim="both"> - <%init> - c = m.content() - c = re.sub(r'\n', '<br/>\n', c.strip()) - </%init> - </pre><&|SELF:popbox, class_="codepop" &><% c %></&><pre> -</%method> - -<%method poppedcode trim="both"> - <%init> - c = m.content() - c = re.sub(r'\n', '<br/>\n', c.strip()) - </%init> - </pre><div class="codepop"><% c %></div><pre> -</%method> + <div class="codetitle">${ title }</div> +% endif +${ content }</div> +</%def> + + + + diff --git a/doc/build/templates/nav.html b/doc/build/templates/nav.html index a045df8ce74b32deb927976a3a8e63f56eb911c5..82c8cf0c16363be34b85844bb50252fd1a0eaecb 100644 --- a/doc/build/templates/nav.html +++ b/doc/build/templates/nav.html @@ -1,106 +1,88 @@ -<%doc>nav.myt - Provides page navigation elements that are derived from toc.TOCElement structures, including -individual hyperlinks as well as navigational toolbars and table-of-content listings.</%doc> - -<%method itemlink trim="both"> - <%args> - item - anchor=True - </%args> - <%args scope="request"> - extension='myt' - </%args> - <a href="<% item.get_link(extension=extension, anchor=anchor) %>"><% item.description %></a> -</%method> - -<%method toclink trim="both"> - <%args> - toc - path - description=None - extension - </%args> - <%init> +# nav.myt - Provides page navigation elements that are derived from toc.TOCElement structures, including +# individual hyperlinks as well as navigational toolbars and table-of-content listings. + +<%namespace name="tocns" file="toc.html"/> + +<%def name="itemlink(item, anchor=True)" filter="trim"> + <a href="${ item.get_link(extension=extension, anchor=anchor) }">${ item.description }</a> +</%def> + +<%def name="toclink(toc, path, description=None)" filter="trim"> + <% item = toc.get_by_path(path) if description is None: if item: description = item.description else: description = path - </%init> + %> % if item: - <a href="<% item.get_link(extension=extension) %>"><% description %></a> + <a href="${ item.get_link(extension=extension) }">${ description }</a> % else: - <b><% description %></b> -% -</%method> - - -<%method link trim="both"> - <%args> - href - text - class_ - </%args> - <a href="<% href %>" <% class_ and (('class=\"%s\"' % class_) or '')%>><% text %></a> -</%method> - -<%method topnav> - <%args> - item - extension - </%args> + <b>${ description }</b> +% endif +</%def> + + +<%def name="link" filter="trim(href, text, class_)"> + <a href="${ href }" ${ class_ and (('class=\"%s\"' % class_) or '')}>${ text }</a> +</%def> + +<%def name="topnav(item)"> <div class="topnav"> <div class="topnavsectionlink"> -<a href="index.<% extension %>">Table of Contents</a> +<a href="index.${ extension }">Table of Contents</a> <div class="prevnext"> % if item.previous is not None: -Previous: <& itemlink, item=item.previous, anchor=False &> -% +Previous: ${itemlink(item=item.previous, anchor=False)} +% endif % if item.previous is not None and item.next is not None: | -% +% endif % if item.next is not None: -Next: <& itemlink, item=item.next, anchor=False &> -% +Next: ${itemlink(item=item.next, anchor=False)} +% endif </div> </div> <div class="topnavmain"> - <div class="topnavheader"><% item.description %></div> + <div class="topnavheader">${ item.description }</div> <div class="topnavitems"> - <& toc.myt:printtoc, root=item, current=None, full=True, extension=extension, anchor_toplevel=True &> + % if tocns is UNDEFINED: + <% + raise "its undefined" + %> + % endif + ${tocns.printtoc(root=item, current=None, full=True, anchor_toplevel=True)} </div> </div> </div> -</%method> +</%def> -<%method pagenav> -<%args> - item -</%args> +<%def name="pagenav(item)"> <div class="sectionnavblock"> <div class="sectionnav"> % if item.previous is not None: - Previous: <& itemlink, item=item.previous &> -% # end if + Previous: ${itemlink(item=item.previous)} +% endif % if item.next is not None: % if item.previous is not None: | -% # end if +% endif - Next: <& itemlink, item=item.next &> -% # end if + Next: ${itemlink(item=item.next)} +% endif </div> </div> -</%method> +</%def> diff --git a/doc/build/templates/root.html b/doc/build/templates/root.html index 1b7db5a57c4217b89a1d924b926ced618334f7fb..e801e0635a41ddac154257300d85c2ecd675ad9e 100644 --- a/doc/build/templates/root.html +++ b/doc/build/templates/root.html @@ -1,3 +1,4 @@ +<%inherit file="base.html"/> <html> <head> <title>${self.title()}</title> @@ -8,7 +9,7 @@ <script src="scripts.js"></script> </head> <body> -${self.next()} +${next.body()} </body> </html> diff --git a/doc/build/templates/toc.html b/doc/build/templates/toc.html index 6580a39c5021807c84d8d82d2bd1e20c4e368396..7fb77194e75413b1098645ef49cf836285321503 100644 --- a/doc/build/templates/toc.html +++ b/doc/build/templates/toc.html @@ -1,12 +1,6 @@ -<%doc>toc.myt - prints full table of contents listings given toc.TOCElement strucures</%doc> +# toc.myt - prints full table of contents listings given toc.TOCElement strucures -<%method toc> - <%args> - toc - extension - </%args> - - +<%def name="toc(toc)"> <div class="maintoc"> <a name="table_of_contents"></a> @@ -16,12 +10,11 @@ <br/><br/> <div style="margin-left:50px;"> - <& printtoc, root = toc, current = None, full = False, children=False, extension=extension, anchor_toplevel=False &> + ${printtoc(root=toc,current=None,full=False,children=False,anchor_toplevel=False)} </div> </div> - <div class="maintoc"> <a name="full_index"></a> <h2>Table of Contents: Full</h2> @@ -30,66 +23,43 @@ <br/><br/> <div style="margin-left:50px;"> - <& printtoc, root = toc, current = None, full = True, children=True, extension=extension, anchor_toplevel=False &> + ${printtoc(root=toc,current=None,full=True,children=True,anchor_toplevel=False)} </div> </div> -</%method> - +</%def> -<%method printtoc> -<%args> - root - current = None - full = False - children = True - extension - anchor_toplevel=False -</%args> -<ul class="toc_list"> -% for i in root.children: - <& printtocelement, item=i, bold = (i == current), full = full, children=children, extension=extension, anchor_toplevel=anchor_toplevel &> -% -</ul> -</%method> +<%def name="printtoc(root,current=None,full=False,children=True,anchor_toplevel=False)"> + <ul class="toc_list"> + % for i in root.children: + ${printtocelement(item=i, bold = (i == current), full = full, children=children, anchor_toplevel=anchor_toplevel)} + % endfor + </ul> +</%def> -<%def printtocelement> -<%doc>prints a TOCElement as a table of contents item and prints its immediate child items</%doc> - <%args> - item - bold = False - full = False - children = True - extension - anchor_toplevel - </%args> - - <li><A style="<% bold and "font-weight:bold;" or "" %>" href="<% item.get_link(extension=extension, anchor=anchor_toplevel) %>"><% item.description %></a></li> +<%def name="printtocelement(item, anchor_toplevel, bold=False, full=False, children=True)"> + # prints a TOCElement as a table of contents item and prints its immediate child items + <li><A style="${bold and "font-weight:bold;" or "" }" href="${item.get_link(extension=extension, anchor=anchor_toplevel) }">${item.description}</a></li> -% if children: - <ul class="small_toc_list"> -% for i in item.children: - <& printsmtocelem, item=i, children=full, extension=extension &> -% - </ul> -% + % if children: + <ul class="small_toc_list"> + % for i in item.children: + ${printsmtocelem(item=i, children=full)} + % endfor + </ul> + % endif </%def> -<%def printsmtocelem> - <%args> - item - children = False - extension - </%args> - <li><A href="<% item.get_link(extension=extension) %>"><% item.description %></a></li> +<%def name="printsmtocelem(item, children=False)"> + <li><A href="${item.get_link(extension=extension)}">${item.description}</a></li> -% if children: - <ul class="small_toc_list"> -% for i in item.children: - <& printsmtocelem, item = i, extension=extension &> -% - </ul> -% + % if children: + <ul class="small_toc_list"> + % for i in item.children: + ${printsmtocelem(item=i)} + % endfor + </ul> + % endif </%def> diff --git a/lib/mako/ast.py b/lib/mako/ast.py index 43a36ec0aaef0eec3392e1baf389e7d8e2b09b0b..489d219a7efe3253fe0352fffe5ba9b2e2931aff 100644 --- a/lib/mako/ast.py +++ b/lib/mako/ast.py @@ -6,14 +6,21 @@ """utilities for analyzing expressions and blocks of Python code, as well as generating Python from AST nodes""" -from compiler import ast, parse, visitor +from compiler import ast, visitor +from compiler import parse as compiler_parse from mako import util, exceptions from StringIO import StringIO import re +def parse(code, mode, lineno, pos, filename): + try: + return compiler_parse(code, mode) + except SyntaxError, e: + raise exceptions.SyntaxException("(%s) %s" % (e.__class__.__name__, str(e)), lineno, pos, filename) + class PythonCode(object): """represents information about a string containing Python code""" - def __init__(self, code, lineno, pos): + def __init__(self, code, lineno, pos, filename): self.code = code # represents all identifiers which are assigned to at some point in the code @@ -29,7 +36,7 @@ class PythonCode(object): # - 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): - expr = parse(code, "exec") + expr = parse(code.lstrip(), "exec", lineno, pos, filename) else: expr = code @@ -72,14 +79,14 @@ class PythonCode(object): self.declared_identifiers.add(alias) else: if mod == '*': - raise exceptions.CompileException("'import *' is not supported, since all identifier names must be explicitly declared. Please use the form 'from <modulename> import <name1>, <name2>, ...' instead.", lineno, pos) + raise exceptions.CompileException("'import *' is not supported, since all identifier names must be explicitly declared. Please use the form 'from <modulename> import <name1>, <name2>, ...' instead.", lineno, pos, filename) self.declared_identifiers.add(mod) f = FindIdentifiers() visitor.walk(expr, f) #, walker=walker()) class ArgumentList(object): """parses a fragment of code as a comma-separated list of expressions""" - def __init__(self, code, lineno, pos): + def __init__(self, code, lineno, pos, filename): self.codeargs = [] self.args = [] self.declared_identifiers = util.Set() @@ -87,7 +94,7 @@ class ArgumentList(object): class FindTuple(object): def visitTuple(s, node, *args): for n in node.nodes: - p = PythonCode(n, lineno, pos) + p = PythonCode(n, lineno, pos, filename) self.codeargs.append(p) self.args.append(ExpressionGenerator(n).value()) self.declared_identifiers = self.declared_identifiers.union(p.declared_identifiers) @@ -97,7 +104,7 @@ class ArgumentList(object): # if theres text and no trailing comma, insure its parsed # as a tuple by adding a trailing comma code += "," - expr = parse(code, "exec") + expr = parse(code, "exec", lineno, pos, filename) else: expr = code @@ -113,10 +120,10 @@ class PythonFragment(PythonCode): except (MyException, e): etc. """ - def __init__(self, code, lineno, pos): - m = re.match(r'^(\w+)(?:\s+(.*?))?:$', code) + def __init__(self, code, lineno, pos, filename): + m = re.match(r'^(\w+)(?:\s+(.*?))?:$', code.strip()) if not m: - raise exceptions.CompileException("Fragment '%s' is not a partial control statement" % code, lineno, pos) + raise exceptions.CompileException("Fragment '%s' is not a partial control statement" % code, lineno, pos, filename) (keyword, expr) = m.group(1,2) if keyword in ['for','if', 'while']: code = code + "pass" @@ -127,8 +134,8 @@ class PythonFragment(PythonCode): elif keyword == 'except': code = "try:pass\n" + code + "pass" else: - raise exceptions.CompileException("Unsupported control keyword: '%s'" % keyword, lineno, pos) - super(PythonFragment, self).__init__(code, lineno, pos) + raise exceptions.CompileException("Unsupported control keyword: '%s'" % keyword, lineno, pos, filename) + super(PythonFragment, self).__init__(code, lineno, pos, filename) class walker(visitor.ASTVisitor): def dispatch(self, node, *args): @@ -138,9 +145,9 @@ class walker(visitor.ASTVisitor): class FunctionDecl(object): """function declaration""" - def __init__(self, code, lineno, pos): + def __init__(self, code, lineno, pos, filename): self.code = code - expr = parse(code, "exec") + expr = parse(code, "exec", lineno, pos, filename) class ParseFunc(object): def visitFunction(s, node, *args): self.funcname = node.name @@ -152,7 +159,7 @@ class FunctionDecl(object): f = ParseFunc() visitor.walk(expr, f) if not hasattr(self, 'funcname'): - raise exceptions.CompileException("Code '%s' is not a function declaration" % code, lineno, pos) + raise exceptions.CompileException("Code '%s' is not a function declaration" % code, lineno, pos, filename) def get_argument_expressions(self, include_defaults=True): """return the argument declarations of this FunctionDecl as a printable list.""" namedecls = [] diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py index 6ce1d9a39ff7d82ed8bfb3788ec167da380ff08c..7095627037c938f2ed86bc9ea80d3e60f8317f01 100644 --- a/lib/mako/codegen.py +++ b/lib/mako/codegen.py @@ -81,9 +81,9 @@ class _GenerateRenderMethod(object): buffered = filtered = False if args is None: - args = ['context'] + args = ['context', '**kwargs'] else: - args = [a for a in ['context'] + args] + args = [a for a in ['context'] + args + ['**kwargs']] if not self.in_def: self._inherit() @@ -97,13 +97,13 @@ class _GenerateRenderMethod(object): if len(self.identifiers.locally_assigned) > 0: printer.writeline("__locals = {}") - self.write_variable_declares(self.identifiers) + self.write_variable_declares(self.identifiers, first="kwargs") for n in node.nodes: n.accept_visitor(self) self.write_def_finish(node, buffered, filtered) - + printer.writeline(None) printer.write("\n\n") def _inherit(self): @@ -196,6 +196,7 @@ class _GenerateRenderMethod(object): n.accept_visitor(self) self.write_def_finish(node, buffered, filtered) + self.printer.writeline(None) def write_def_finish(self, node, buffered, filtered): if not buffered: @@ -210,7 +211,7 @@ class _GenerateRenderMethod(object): self.printer.writeline("return %s" % s) else: self.printer.writeline("context.write(%s)" % s) - self.printer.writeline(None) + self.printer.writeline(None) def create_filter_callable(self, args, target): d = dict([(k, "filters." + v.func_name) for k, v in filters.DEFAULT_ESCAPES.iteritems()]) @@ -241,33 +242,40 @@ class _GenerateRenderMethod(object): self.write_source_comment(node) self.printer.write_indented_block(node.text) - if not self.in_def: + if not self.in_def and len(self.identifiers.locally_assigned) > 0: # if we are the "template" def, fudge locally declared/modified variables into the "__locals" dictionary, # which is used for def calls within the same template, to simulate "enclosing scope" - self.printer.writeline('__locals.update(%s)' % (",".join(["%s=%s" % (x, x) for x in node.declared_identifiers()]))) - + #self.printer.writeline('__locals.update(%s)' % (",".join(["%s=%s" % (x, x) for x in node.declared_identifiers()]))) + self.printer.writeline('__locals.update(dict([(k, v) for k, v in locals().iteritems() if k in [%s]]))' % ','.join([repr(x) for x in node.declared_identifiers()])) + def visitIncludeTag(self, node): self.write_source_comment(node) self.printer.writeline("runtime.include_file(context, %s, import_symbols=%s)" % (node.parsed_attributes['file'], repr(node.attributes.get('import', False)))) def visitNamespaceTag(self, node): self.write_source_comment(node) - self.printer.writeline("def make_namespace():") - export = [] - identifiers = self.identifiers.branch(node) - class NSDefVisitor(object): - def visitDefTag(s, node): - self.write_inline_def(node, identifiers) - export.append(node.name) - vis = NSDefVisitor() - for n in node.nodes: - n.accept_visitor(vis) - self.printer.writeline("return [%s]" % (','.join(export))) - self.printer.writeline(None) - self.printer.writeline("%s = runtime.Namespace(%s, context.clean_inheritance_tokens(), templateuri=%s, callables=make_namespace())" % (node.name, repr(node.name), node.parsed_attributes.get('file', 'None'))) + if len(node.nodes): + self.printer.writeline("def make_namespace():") + export = [] + identifiers = self.identifiers.branch(node) + class NSDefVisitor(object): + def visitDefTag(s, node): + self.write_inline_def(node, identifiers) + export.append(node.name) + vis = NSDefVisitor() + for n in node.nodes: + n.accept_visitor(vis) + self.printer.writeline("return [%s]" % (','.join(export))) + self.printer.writeline(None) + callable_name = "make_namespace()" + else: + callable_name = "None" + self.printer.writeline("%s = runtime.Namespace(%s, context.clean_inheritance_tokens(), templateuri=%s, callables=%s)" % (node.name, repr(node.name), node.parsed_attributes.get('file', 'None'), callable_name)) if eval(node.attributes.get('inheritable', "False")): self.printer.writeline("self.%s = %s" % (node.name, node.name)) - + if not self.in_def: + self.printer.writeline("__locals[%s] = %s" % (repr(node.name), node.name)) + def visitDefTag(self, node): pass @@ -285,10 +293,15 @@ class _GenerateRenderMethod(object): n.accept_visitor(vis) self.printer.writeline("def body(**kwargs):") body_identifiers = identifiers.branch(node, includedefs=False, includenode=False) + # TODO: figure out best way to specify buffering/nonbuffering (at call time would be better) + buffered = False + if buffered: + self.printer.writeline("context.push_buffer()") + self.printer.writeline("try:") self.write_variable_declares(body_identifiers, first="kwargs") for n in node.nodes: n.accept_visitor(self) - self.printer.writeline("return ''") + self.write_def_finish(node, buffered, False) self.printer.writeline(None) self.printer.writeline("return [%s]" % (','.join(export))) self.printer.writeline(None) @@ -384,6 +397,8 @@ class _Identifiers(object): self.check_declared(node) def visitNamespaceTag(self, node): self.check_declared(node) + self.locally_declared.add(node.name) + self.locally_assigned.add(node.name) if node is self.node: for n in node.nodes: n.accept_visitor(self) diff --git a/lib/mako/exceptions.py b/lib/mako/exceptions.py index a2bc255c117d69151e424ba6851550417ce17e4b..b15f7370f63e0982f650783f1c2542dedae0d094 100644 --- a/lib/mako/exceptions.py +++ b/lib/mako/exceptions.py @@ -11,18 +11,25 @@ class MakoException(Exception): class RuntimeException(MakoException): pass + +def _format_filepos(lineno, pos, filename): + if filename is None: + 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, lineno, pos): - MakoException.__init__(self, message + " at line: %d char: %d" % (lineno, pos)) + def __init__(self, message, lineno, pos, filename): + MakoException.__init__(self, message + _format_filepos(lineno, pos, filename)) self.lineno =lineno self.pos = pos - + self.filename = filename class SyntaxException(MakoException): - def __init__(self, message, lineno, pos): - MakoException.__init__(self, message + " at line: %d char: %d" % (lineno, pos)) + def __init__(self, message, lineno, pos, filename): + MakoException.__init__(self, message + _format_filepos(lineno, pos, filename)) self.lineno =lineno self.pos = pos + self.filename = filename class TemplateLookupException(MakoException): pass \ No newline at end of file diff --git a/lib/mako/filters.py b/lib/mako/filters.py index 880cd99afb494f4720f60514b922535f26871226..1369dc5b503736f14fe18f84e542eadf3e960e52 100644 --- a/lib/mako/filters.py +++ b/lib/mako/filters.py @@ -35,11 +35,15 @@ def url_unescape(string): text = text.decode("utf8") return text +def trim(string): + return string.strip() + # TODO: options to make this dynamic per-compilation will be added in a later release DEFAULT_ESCAPES = { 'x':xml_escape, 'h':html_escape, - 'u':url_escape + 'u':url_escape, + 'trim':trim, } _ASCII_re = re.compile(r'\A[\x00-\x7f]*\Z') diff --git a/lib/mako/lexer.py b/lib/mako/lexer.py index 4b59c269e300ad4a4cc945aa669cd642ced8fc08..e487852be5b043ca7b383c21ea2ad2cfe65c4cf9 100644 --- a/lib/mako/lexer.py +++ b/lib/mako/lexer.py @@ -11,9 +11,10 @@ from mako import parsetree, exceptions from mako.pygen import adjust_whitespace class Lexer(object): - def __init__(self, text): + def __init__(self, text, filename=None): self.text = text - self.template = parsetree.TemplateNode() + self.filename = filename + self.template = parsetree.TemplateNode(self.filename) self.matched_lineno = 1 self.matched_charpos = 0 self.lineno = 1 @@ -51,6 +52,7 @@ class Lexer(object): def append_node(self, nodecls, *args, **kwargs): kwargs['lineno'] = self.matched_lineno kwargs['pos'] = self.matched_charpos + kwargs['filename'] = self.filename node = nodecls(*args, **kwargs) if len(self.tag): self.tag[-1].nodes.append(node) @@ -66,7 +68,7 @@ class Lexer(object): elif node.is_primary: self.control_line.append(node) elif len(self.control_line) and not self.control_line[-1].is_ternary(node.keyword): - raise exceptions.SyntaxException("Keyword '%s' not a legal ternary for keyword '%s'" % (node.keyword, self.control_line[-1].keyword), self.matched_lineno, self.matched_charpos) + raise exceptions.SyntaxException("Keyword '%s' not a legal ternary for keyword '%s'" % (node.keyword, self.control_line[-1].keyword), self.matched_lineno, self.matched_charpos, self.filename) def parse(self): length = len(self.text) @@ -94,7 +96,7 @@ class Lexer(object): raise "assertion failed" if len(self.tag): - raise exceptions.SyntaxException("Unclosed tag: <%%%s>" % self.tag[-1].keyword, self.matched_lineno, self.matched_charpos) + raise exceptions.SyntaxException("Unclosed tag: <%%%s>" % self.tag[-1].keyword, self.matched_lineno, self.matched_charpos, self.filename) return self.template def match_tag_start(self): @@ -128,7 +130,7 @@ class Lexer(object): if keyword == 'text': match = self.match(r'.*?(?=\</%text>)', re.S) if not match: - raise exceptions.SyntaxException("Unclosed tag: <%%%s>" % self.tag[-1].keyword, self.matched_lineno, self.matched_charpos) + raise exceptions.SyntaxException("Unclosed tag: <%%%s>" % self.tag[-1].keyword, self.matched_lineno, self.matched_charpos, self.filename) return self.match_tag_end() return True else: @@ -138,9 +140,9 @@ class Lexer(object): match = self.match(r'\</%\s*(.+?)\s*>') if match: if not len(self.tag): - raise exceptions.SyntaxException("Closing tag without opening tag: </%%%s>" % match.group(1), self.matched_lineno, self.matched_charpos) + raise exceptions.SyntaxException("Closing tag without opening tag: </%%%s>" % match.group(1), self.matched_lineno, self.matched_charpos, self.filename) elif self.tag[-1].keyword != match.group(1): - raise exceptions.SyntaxException("Closing tag </%%%s> does not match tag: <%%%s>" % (match.group(1), self.tag[-1].keyword), self.matched_lineno, self.matched_charpos) + raise exceptions.SyntaxException("Closing tag </%%%s> does not match tag: <%%%s>" % (match.group(1), self.tag[-1].keyword), self.matched_lineno, self.matched_charpos, self.filename) self.tag.pop() return True else: @@ -206,15 +208,15 @@ class Lexer(object): if operator == '%': m2 = re.match(r'(end)?(\w+)\s*(.*)', text) if not m2: - raise exceptions.SyntaxException("Invalid control line: '%s'" % text, self.matched_lineno, self.matched_charpos) + raise exceptions.SyntaxException("Invalid control line: '%s'" % text, self.matched_lineno, self.matched_charpos, self.filename) (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), self.matched_lineno, self.matched_charpos) + raise exceptions.SyntaxException("No starting keyword '%s' for '%s'" % (keyword, text), self.matched_lineno, self.matched_charpos, self.filename) elif self.control_line[-1].keyword != keyword: - raise exceptions.SyntaxException("Keyword '%s' doesn't match keyword '%s'" % (text, self.control_line[-1].keyword), self.matched_lineno, self.matched_charpos) + raise exceptions.SyntaxException("Keyword '%s' doesn't match keyword '%s'" % (text, self.control_line[-1].keyword), self.matched_lineno, self.matched_charpos, self.filename) self.append_node(parsetree.ControlLine, keyword, isend, text) else: self.append_node(parsetree.Comment, text) diff --git a/lib/mako/parsetree.py b/lib/mako/parsetree.py index 45e696dff82133f1fd8dd8456d0a5b73b6d9d7f0..f3dfded5a980f700aa7865acfcbd1ff86de84f90 100644 --- a/lib/mako/parsetree.py +++ b/lib/mako/parsetree.py @@ -11,9 +11,10 @@ import re class Node(object): """base class for a Node in the parse tree.""" - def __init__(self, lineno, pos): + def __init__(self, lineno, pos, filename): self.lineno = lineno self.pos = pos + self.filename = filename def get_children(self): return [] def accept_visitor(self, visitor): @@ -25,8 +26,8 @@ class Node(object): class TemplateNode(Node): """a 'container' node that stores the overall collection of nodes.""" - def __init__(self): - super(TemplateNode, self).__init__(0, 0) + def __init__(self, filename): + super(TemplateNode, self).__init__(0, 0, filename) self.nodes = [] self.page_attributes = {} def get_children(self): @@ -51,7 +52,7 @@ class ControlLine(Node): self._declared_identifiers = [] self._undeclared_identifiers = [] else: - code = ast.PythonFragment(text, self.lineno, self.pos) + code = ast.PythonFragment(text, self.lineno, self.pos, self.filename) (self._declared_identifiers, self._undeclared_identifiers) = (code.declared_identifiers, code.undeclared_identifiers) def declared_identifiers(self): return self._declared_identifiers @@ -93,7 +94,7 @@ class Code(Node): super(Code, self).__init__(**kwargs) self.text = text self.ismodule = ismodule - self.code = ast.PythonCode(text, self.lineno, self.pos) + self.code = ast.PythonCode(text, self.lineno, self.pos, self.filename) def declared_identifiers(self): return self.code.declared_identifiers def undeclared_identifiers(self): @@ -123,8 +124,8 @@ class Expression(Node): super(Expression, self).__init__(**kwargs) self.text = text self.escapes = escapes - self.escapes_code = ast.ArgumentList(escapes, self.lineno, self.pos) - self.code = ast.PythonCode(text, self.lineno, self.pos) + self.escapes_code = ast.ArgumentList(escapes, self.lineno, self.pos, self.filename) + self.code = ast.PythonCode(text, self.lineno, self.pos, self.filename) def declared_identifiers(self): return [] def undeclared_identifiers(self): @@ -144,7 +145,7 @@ class _TagMeta(type): try: cls = _TagMeta._classmap[keyword] except KeyError: - raise exceptions.CompileException("No such tag: '%s'" % keyword, kwargs['lineno'], kwargs['pos']) + raise exceptions.CompileException("No such tag: '%s'" % keyword, kwargs['lineno'], kwargs['pos'], kwargs['filename']) return type.__call__(cls, keyword, attributes, **kwargs) class Tag(Node): @@ -178,7 +179,7 @@ class Tag(Node): self._parse_attributes(expressions, nonexpressions) 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]), self.lineno, self.pos) + raise exceptions.CompileException("Missing attribute(s): %s" % ",".join([repr(m) for m in missing]), self.lineno, self.pos, self.filename) self.parent = None self.nodes = [] def is_root(self): @@ -194,7 +195,7 @@ class Tag(Node): for x in re.split(r'(\${.+?})', self.attributes[key]): m = re.match(r'^\${(.+?)}$', x) if m: - code = ast.PythonCode(m.group(1), self.lineno, self.pos) + code = ast.PythonCode(m.group(1), self.lineno, self.pos, self.filename) undeclared_identifiers = undeclared_identifiers.union(code.undeclared_identifiers) expr.append(m.group(1)) else: @@ -202,10 +203,10 @@ class Tag(Node): self.parsed_attributes[key] = " + ".join(expr) elif key in nonexpressions: if re.search(r'${.+?}', self.attributes[key]): - raise exceptions.CompileException("Attibute '%s' in tag '%s' does not allow embedded expressions" %(key, self.keyword), self.lineno, self.pos) + raise exceptions.CompileException("Attibute '%s' in tag '%s' does not allow embedded expressions" %(key, self.keyword), self.lineno, self.pos, self.filename) self.parsed_attributes[key] = repr(self.attributes[key]) else: - raise exceptions.CompileException("Invalid attribute for tag '%s': '%s'" %(self.keyword, key), self.lineno, self.pos) + raise exceptions.CompileException("Invalid attribute for tag '%s': '%s'" %(self.keyword, key), self.lineno, self.pos, self.filename) self.expression_undeclared_identifiers = undeclared_identifiers def declared_identifiers(self): return [] @@ -225,7 +226,7 @@ class NamespaceTag(Tag): super(NamespaceTag, self).__init__(keyword, attributes, ('file',), ('name','inheritable'), ('name',), **kwargs) self.name = attributes['name'] def declared_identifiers(self): - return [self.name] + return [] class TextTag(Tag): __keyword__ = 'text' @@ -239,22 +240,22 @@ class DefTag(Tag): name = attributes['name'] if re.match(r'^[\w_]+$',name): name = name + "()" - self.function_decl = ast.FunctionDecl("def " + name + ":pass", self.lineno, self.pos) + self.function_decl = ast.FunctionDecl("def " + name + ":pass", self.lineno, self.pos, self.filename) self.name = self.function_decl.funcname - self.filter_args = ast.ArgumentList(attributes.get('filter', ''), self.lineno, self.pos) + self.filter_args = ast.ArgumentList(attributes.get('filter', ''), self.lineno, self.pos, self.filename) def declared_identifiers(self): return self.function_decl.argnames def undeclared_identifiers(self): res = [] for c in self.function_decl.defaults: - res += list(ast.PythonCode(c, self.lineno, self.pos).undeclared_identifiers) + res += list(ast.PythonCode(c, self.lineno, self.pos, self.filename).undeclared_identifiers) return res + list(self.filter_args.undeclared_identifiers.difference(util.Set(filters.DEFAULT_ESCAPES.keys()))) class CallTag(Tag): __keyword__ = 'call' def __init__(self, keyword, attributes, **kwargs): super(CallTag, self).__init__(keyword, attributes, (), ('expr',), ('expr',), **kwargs) - self.code = ast.PythonCode(attributes['expr'], self.lineno, self.pos) + self.code = ast.PythonCode(attributes['expr'], self.lineno, self.pos, self.filename) def declared_identifiers(self): return self.code.declared_identifiers def undeclared_identifiers(self): diff --git a/lib/mako/runtime.py b/lib/mako/runtime.py index 1941ba6051218f5833a21c683e14f9e7b550f76f..34975fbb9b4795670e6601fa2e28d74761c9acb1 100644 --- a/lib/mako/runtime.py +++ b/lib/mako/runtime.py @@ -14,7 +14,8 @@ class Context(object): self._buffer_stack = [buffer] self._argstack = [data] self._with_template = None - data['args'] = _AttrFacade(self) + #data['args'] = _AttrFacade(self) + data['capture'] = lambda x, *args, **kwargs: capture(self, x, *args, **kwargs) def keys(self): return self._argstack[-1].keys() def __getitem__(self, key): @@ -44,7 +45,7 @@ class Context(object): x = self._argstack[-1].copy() c._argstack = [x] c._with_template = self._with_template - x['args'] = _AttrFacade(c) + #x['args'] = _AttrFacade(c) return c def locals_(self, d): """create a new Context with a copy of this Context's current state, updated with the given dictionary.""" @@ -123,7 +124,14 @@ class Namespace(object): return getattr(self.inherits, key) raise exceptions.RuntimeException("Namespace '%s' has no member '%s'" % (self.name, key)) - +def capture(context, callable_, *args, **kwargs): + context.push_buffer() + try: + callable_(*args, **kwargs) + finally: + buf = context.pop_buffer() + return buf.getvalue() + def include_file(context, uri, import_symbols): template = _lookup_template(context, uri) (callable_, ctx) = _populate_self_namespace(context.clean_inheritance_tokens(), template) diff --git a/lib/mako/template.py b/lib/mako/template.py index 0d6947ba9dc7bc7d682721593422b529467853d9..b5c06fc32043419a8d0948e0b9ce2dab8d6f0611 100644 --- a/lib/mako/template.py +++ b/lib/mako/template.py @@ -50,6 +50,7 @@ class Template(object): self._code = None self.module = module self.description = description + self.filename = filename self.callable_ = self.module.render self.format_exceptions = format_exceptions self.error_handler = error_handler @@ -93,12 +94,12 @@ class DefTemplate(Template): return self.parent.get_def(name) def _compile_text(text, identifier, filename): - node = Lexer(text).parse() - source = Compiler(node).render() - #print source + node = Lexer(text, filename).parse() + source = Compiler(node, filename).render() + print source cid = identifier module = imp.new_module(cid) - code = compile(source, filename or cid, 'exec') + code = compile(source, cid, 'exec') exec code in module.__dict__, module.__dict__ return (source, module) diff --git a/test/ast.py b/test/ast.py index c4211a984f9c0ba95f8f3b0a542034a4bc675ebc..3059fdd53c1815c482aee5989448e5ea6c9254c4 100644 --- a/test/ast.py +++ b/test/ast.py @@ -23,11 +23,11 @@ for lar in (1,2,3): print "hello world, ", a, b print "Another expr", c """ - parsed = ast.PythonCode(code, 0, 0) + parsed = ast.PythonCode(code, 0, 0, None) assert parsed.declared_identifiers == util.Set(['a','b','c', 'g', 'h', 'i', 'u', 'k', 'j', 'gh', 'lar', 'x']) assert parsed.undeclared_identifiers == util.Set(['x', 'q', 'foo', 'gah', 'blah']) - parsed = ast.PythonCode("x + 5 * (y-z)", 0, 0) + parsed = ast.PythonCode("x + 5 * (y-z)", 0, 0, None) assert parsed.undeclared_identifiers == util.Set(['x', 'y', 'z']) assert parsed.declared_identifiers == util.Set() @@ -41,7 +41,7 @@ data = get_data() for x in data: result.append(x+7) """ - parsed = ast.PythonCode(code, 0, 0) + parsed = ast.PythonCode(code, 0, 0, None) assert parsed.undeclared_identifiers == util.Set(['get_data']) assert parsed.declared_identifiers == util.Set(['result', 'data', 'x', 'hoho', 'foobar', 'foo', 'yaya']) @@ -54,7 +54,7 @@ for y in range(1, y): [z for z in range(1, z)] (q for q in range (1, q)) """ - parsed = ast.PythonCode(code, 0, 0) + parsed = ast.PythonCode(code, 0, 0, None) assert parsed.undeclared_identifiers == util.Set(['x', 'y', 'z', 'q']) def test_locate_identifiers_4(self): @@ -64,7 +64,7 @@ print y def mydef(mydefarg): print "mda is", mydefarg """ - parsed = ast.PythonCode(code, 0, 0) + parsed = ast.PythonCode(code, 0, 0, None) assert parsed.undeclared_identifiers == util.Set(['y']) assert parsed.declared_identifiers == util.Set(['mydef', 'x']) @@ -74,41 +74,41 @@ from foo import * import x as bar """ try: - parsed = ast.PythonCode(code, 0, 0) + parsed = ast.PythonCode(code, 0, 0, None) assert False except exceptions.CompileException, e: assert str(e).startswith("'import *' is not supported") def test_python_fragment(self): - parsed = ast.PythonFragment("for x in foo:", 0, 0) + parsed = ast.PythonFragment("for x in foo:", 0, 0, None) assert parsed.declared_identifiers == util.Set(['x']) assert parsed.undeclared_identifiers == util.Set(['foo']) - parsed = ast.PythonFragment("try:", 0, 0) + parsed = ast.PythonFragment("try:", 0, 0, None) - parsed = ast.PythonFragment("except (MyException, e):", 0, 0) + parsed = ast.PythonFragment("except (MyException, e):", 0, 0, None) assert parsed.declared_identifiers == util.Set(['e']) assert parsed.undeclared_identifiers == util.Set() def test_argument_list(self): - parsed = ast.ArgumentList("3, 5, 'hi', x+5, context.get('lala')", 0, 0) + parsed = ast.ArgumentList("3, 5, 'hi', x+5, context.get('lala')", 0, 0, None) assert parsed.undeclared_identifiers == util.Set(['x', 'context']) assert [x for x in parsed.args] == ["3", "5", "'hi'", "(x + 5)", "context.get('lala')"] - parsed = ast.ArgumentList("h", 0, 0) + parsed = ast.ArgumentList("h", 0, 0, None) assert parsed.args == ["h"] def test_function_decl(self): """test getting the arguments from a function""" code = "def foo(a, b, c=None, d='hi', e=x, f=y+7):pass" - parsed = ast.FunctionDecl(code, 0, 0) + parsed = ast.FunctionDecl(code, 0, 0, None) assert parsed.funcname=='foo' assert parsed.argnames==['a', 'b', 'c', 'd', 'e', 'f'] def test_function_decl_2(self): """test getting the arguments from a function""" code = "def foo(a, b, c=None, *args, **kwargs):pass" - parsed = ast.FunctionDecl(code, 0, 0) + parsed = ast.FunctionDecl(code, 0, 0, None) assert parsed.funcname=='foo' assert parsed.argnames==['a', 'b', 'c', 'args', 'kwargs'] diff --git a/test/filters.py b/test/filters.py index 43e9c5880b7419943f48e5751b5cba5716ece422..016bf84b014693e48b886ad5c2527e61331255e8 100644 --- a/test/filters.py +++ b/test/filters.py @@ -58,7 +58,15 @@ class BufferTest(unittest.TestCase): """) assert flatten_result(t.render()) == "this is foo hi-><-hi" - + def test_capture(self): + t = Template(""" + <%def name="foo" buffered="False"> + this is foo + </%def> + ${"hi->" + capture(foo) + "<-hi"} +""") + assert flatten_result(t.render()) == "hi-> this is foo <-hi" + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/test/inheritance.py b/test/inheritance.py index 7836dce6a750214edbb29527eb10baa206f2053e..3dc9f5d9b576f821dcd9ce5c5cbb855421fc3516 100644 --- a/test/inheritance.py +++ b/test/inheritance.py @@ -99,7 +99,6 @@ ${next.body()} 'self:main self:layout self:general self:base' ] - def test_includes(self): """test that an included template also has its full hierarchy invoked.""" collection = lookup.TemplateLookup() diff --git a/test/namespace.py b/test/namespace.py index d06d2f59cb12841bb692b2dee7fe163b784329ea..282212bddf46ff68bbc6c33d72f775f2c12d949b 100644 --- a/test/namespace.py +++ b/test/namespace.py @@ -68,6 +68,25 @@ class NamespaceTest(unittest.TestCase): """) assert flatten_result(collection.get_template('main.html').render()) == "this is main. overridden def1 hi, there def2: there" + + def test_in_def(self): + collection = lookup.TemplateLookup() + collection.put_string("main.html", """ + <%namespace name="foo" file="ns.html"/> + + this is main. ${bar()} + <%def name="bar"> + this is bar, foo is ${foo.bar()} + </%def> + """) + + collection.put_string("ns.html", """ + <%def name="bar"> + this is ns.html->bar + </%def> + """) + + print collection.get_template("main.html").render() if __name__ == '__main__': unittest.main()