aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES7
-rw-r--r--lib/mako/ast.py2
-rw-r--r--lib/mako/codegen.py30
-rw-r--r--lib/mako/filters.py3
-rw-r--r--lib/mako/lookup.py4
-rw-r--r--lib/mako/template.py8
-rw-r--r--test/ast.py11
-rw-r--r--test/filters.py89
-rw-r--r--test/template.py2
9 files changed, 134 insertions, 22 deletions
diff --git a/CHANGES b/CHANGES
index 254f675..cb727c5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,13 @@
- fix to lexing of <%docs> tag nested in other tags
- fix to context-arguments inside of <%include> tag which broke
during 0.1.4 [ticket:29]
+- added "n" filter, disables *all* filters normally applied to an expression
+via <%page> or default_filters (but not those within the filter)
+- added buffer_filters argument, defines filters applied to the return value
+of buffered/cached/filtered %defs, after all filters defined with the %def
+itself have been applied. allows the creation of default expression filters
+that let the output of return-valued %defs "opt out" of that filtering
+via passing special attributes or objects.
0.1.4
- got defs-within-defs to be cacheable
diff --git a/lib/mako/ast.py b/lib/mako/ast.py
index 5556269..db51a88 100644
--- a/lib/mako/ast.py
+++ b/lib/mako/ast.py
@@ -47,6 +47,8 @@ class PythonCode(object):
def _add_declared(s, name):
if not s.in_function:
self.declared_identifiers.add(name)
+ def visitClass(self, node, *args):
+ self._add_declared(node.name)
def visitAssName(self, node, *args):
self._add_declared(node.name)
def visitAssign(self, node, *args):
diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py
index efdd301..c2a10d3 100644
--- a/lib/mako/codegen.py
+++ b/lib/mako/codegen.py
@@ -14,18 +14,19 @@ from mako import util, ast, parsetree, filters
MAGIC_NUMBER = 1
-def compile(node, uri, filename=None, default_filters=None, imports=None):
+def compile(node, uri, filename=None, default_filters=None, buffer_filters=None, imports=None):
"""generate module source code given a parsetree node, uri, and optional source filename"""
buf = util.FastEncodingBuffer()
printer = PythonPrinter(buf)
- _GenerateRenderMethod(printer, _CompileContext(uri, filename, default_filters, imports), node)
+ _GenerateRenderMethod(printer, _CompileContext(uri, filename, default_filters, buffer_filters, imports), node)
return buf.getvalue()
class _CompileContext(object):
- def __init__(self, uri, filename, default_filters, imports):
+ def __init__(self, uri, filename, default_filters, buffer_filters, imports):
self.uri = uri
self.filename = filename
self.default_filters = default_filters
+ self.buffer_filters = buffer_filters
self.imports = imports
class _GenerateRenderMethod(object):
@@ -345,6 +346,7 @@ class _GenerateRenderMethod(object):
s = "_buf.getvalue()"
if filtered:
s = self.create_filter_callable(node.filter_args.args, s, False)
+ s = self.create_filter_callable(self.compiler.buffer_filters, s, False)
self.printer.writeline(None)
if buffered or cached:
self.printer.writeline("return %s" % s)
@@ -402,20 +404,18 @@ class _GenerateRenderMethod(object):
elif name == 'unicode':
return 'unicode'
else:
- return \
- {'x':'filters.xml_escape',
- 'h':'filters.html_escape',
- 'u':'filters.url_escape',
- 'trim':'filters.trim',
- 'entity':'filters.html_entities_escape',
- }.get(name, name)
-
- if is_expression and self.compiler.pagetag:
- args = self.compiler.pagetag.filter_args.args + args
- if is_expression and self.compiler.default_filters:
- args = self.compiler.default_filters + args
+ return filters.DEFAULT_ESCAPES.get(name, name)
+
+ if 'n' not in args:
+ if is_expression:
+ if self.compiler.pagetag:
+ args = self.compiler.pagetag.filter_args.args + args
+ if self.compiler.default_filters:
+ args = self.compiler.default_filters + args
for e in args:
# if filter given as a function, get just the identifier portion
+ if e == 'n':
+ continue
m = re.match(r'(.+?)(\(.*\))', e)
if m:
(ident, fargs) = m.group(1,2)
diff --git a/lib/mako/filters.py b/lib/mako/filters.py
index d59e974..0f2b87c 100644
--- a/lib/mako/filters.py
+++ b/lib/mako/filters.py
@@ -161,7 +161,8 @@ DEFAULT_ESCAPES = {
'trim':'filters.trim',
'entity':'filters.html_entities_escape',
'unicode':'unicode',
- 'decode':'decode'
+ 'decode':'decode',
+ 'n':'n'
}
diff --git a/lib/mako/lookup.py b/lib/mako/lookup.py
index f43acf8..e5955cc 100644
--- a/lib/mako/lookup.py
+++ b/lib/mako/lookup.py
@@ -37,7 +37,7 @@ class TemplateCollection(object):
return uri
class TemplateLookup(TemplateCollection):
- def __init__(self, directories=None, module_directory=None, filesystem_checks=True, collection_size=-1, format_exceptions=False, error_handler=None, output_encoding=None, cache_type=None, cache_dir=None, cache_url=None, modulename_callable=None, default_filters=['unicode'], imports=None, input_encoding=None, preprocessor=None):
+ def __init__(self, directories=None, module_directory=None, filesystem_checks=True, collection_size=-1, format_exceptions=False, error_handler=None, output_encoding=None, cache_type=None, cache_dir=None, cache_url=None, modulename_callable=None, default_filters=['unicode'], buffer_filters=[], imports=None, input_encoding=None, preprocessor=None):
if isinstance(directories, basestring):
directories = [directories]
self.directories = [posixpath.normpath(d) for d in directories or []]
@@ -45,7 +45,7 @@ class TemplateLookup(TemplateCollection):
self.modulename_callable = modulename_callable
self.filesystem_checks = filesystem_checks
self.collection_size = collection_size
- self.template_args = {'format_exceptions':format_exceptions, 'error_handler':error_handler, 'output_encoding':output_encoding, 'input_encoding':input_encoding, 'module_directory':module_directory, 'cache_type':cache_type, 'cache_dir':cache_dir or module_directory, 'cache_url':cache_url, 'default_filters':default_filters, 'imports':imports, 'preprocessor':preprocessor}
+ self.template_args = {'format_exceptions':format_exceptions, 'error_handler':error_handler, 'output_encoding':output_encoding, 'input_encoding':input_encoding, 'module_directory':module_directory, 'cache_type':cache_type, 'cache_dir':cache_dir or module_directory, 'cache_url':cache_url, 'default_filters':default_filters, 'buffer_filters':buffer_filters, 'imports':imports, 'preprocessor':preprocessor}
if collection_size == -1:
self.__collection = {}
self._uri_cache = {}
diff --git a/lib/mako/template.py b/lib/mako/template.py
index 8e8afb6..07efbd6 100644
--- a/lib/mako/template.py
+++ b/lib/mako/template.py
@@ -16,7 +16,7 @@ import imp, time, weakref, tempfile, shutil, os, stat, sys, re
class Template(object):
"""a compiled template"""
- def __init__(self, text=None, filename=None, uri=None, format_exceptions=False, error_handler=None, lookup=None, output_encoding=None, module_directory=None, cache_type=None, cache_dir=None, cache_url=None, module_filename=None, input_encoding=None, default_filters=['unicode'], imports=None, preprocessor=None):
+ def __init__(self, text=None, filename=None, uri=None, format_exceptions=False, error_handler=None, lookup=None, output_encoding=None, module_directory=None, cache_type=None, cache_dir=None, cache_url=None, module_filename=None, input_encoding=None, default_filters=['unicode'], buffer_filters=[], imports=None, preprocessor=None):
"""construct a new Template instance using either literal template text, or a previously loaded template module
text - textual template source, or None if a module is to be provided
@@ -39,6 +39,7 @@ class Template(object):
self.uri = self.module_id
self.default_filters = default_filters
+ self.buffer_filters = buffer_filters
self.input_encoding = input_encoding
self.imports = imports
self.preprocessor = preprocessor
@@ -129,6 +130,7 @@ class DefTemplate(Template):
self.parent = parent
self.callable_ = callable_
self.default_filters = parent.default_filters
+ self.buffer_filters = parent.buffer_filters
self.input_encoding = parent.input_encoding
self.imports = parent.imports
self.output_encoding = parent.output_encoding
@@ -175,7 +177,7 @@ class ModuleInfo(object):
def _compile_text(template, text, filename):
identifier = template.module_id
node = Lexer(text, filename, input_encoding=template.input_encoding, preprocessor=template.preprocessor).parse()
- source = codegen.compile(node, template.uri, filename, default_filters=template.default_filters, imports=template.imports)
+ source = codegen.compile(node, template.uri, filename, default_filters=template.default_filters, buffer_filters=template.buffer_filters, imports=template.imports)
cid = identifier
module = imp.new_module(cid)
code = compile(source, cid, 'exec')
@@ -186,7 +188,7 @@ def _compile_module_file(template, text, filename, outputpath):
identifier = template.module_id
(dest, name) = tempfile.mkstemp()
node = Lexer(text, filename, input_encoding=template.input_encoding, preprocessor=template.preprocessor).parse()
- source = codegen.compile(node, template.uri, filename, default_filters=template.default_filters, imports=template.imports)
+ source = codegen.compile(node, template.uri, filename, default_filters=template.default_filters, buffer_filters=template.buffer_filters, imports=template.imports)
os.write(dest, source)
os.close(dest)
shutil.move(name, outputpath)
diff --git a/test/ast.py b/test/ast.py
index f19c103..4df03fa 100644
--- a/test/ast.py
+++ b/test/ast.py
@@ -113,6 +113,17 @@ import foo.bar
parsed = ast.PythonCode(code, 0, 0, None)
assert parsed.declared_identifiers == util.Set(['foo'])
assert parsed.undeclared_identifiers == util.Set()
+
+ def test_locate_identifiers_8(self):
+ code = """
+class Hi(object):
+ foo = 7
+ def hoho(self):
+ x = 5
+"""
+ parsed = ast.PythonCode(code, 0, 0, None)
+ assert parsed.declared_identifiers == util.Set(['Hi'])
+ assert parsed.undeclared_identifiers == util.Set()
def test_no_global_imports(self):
code = """
diff --git a/test/filters.py b/test/filters.py
index 8d9447c..aa0eb4e 100644
--- a/test/filters.py
+++ b/test/filters.py
@@ -77,6 +77,95 @@ class FilterTest(unittest.TestCase):
${"<tag>this is html</tag>"}
""")
assert t.render().strip() == "&lt;tag&gt;this is html&lt;/tag&gt;"
+
+ def test_nflag(self):
+ t = Template("""
+ ${"<tag>this is html</tag>" | n}
+ """, default_filters=['h', 'unicode'])
+ assert t.render().strip() == "<tag>this is html</tag>"
+
+ t = Template("""
+ <%page expression_filter="h"/>
+ ${"<tag>this is html</tag>" | n}
+ """)
+ assert t.render().strip() == "<tag>this is html</tag>"
+
+ t = Template("""
+ <%page expression_filter="h"/>
+ ${"<tag>this is html</tag>" | n, h}
+ """)
+ assert t.render().strip() == "&lt;tag&gt;this is html&lt;/tag&gt;"
+
+ def testnonexpression(self):
+ t = Template("""
+ <%!
+ def a(text):
+ return "this is a"
+ def b(text):
+ return "this is b"
+ %>
+
+ ${foo()}
+ <%def name="foo()" buffered="True">
+ this is text
+ </%def>
+ """, buffer_filters=['a'])
+ assert t.render().strip() == "this is a"
+
+ t = Template("""
+ <%!
+ def a(text):
+ return "this is a"
+ def b(text):
+ return "this is b"
+ %>
+
+ ${'hi'}
+ ${foo()}
+ <%def name="foo()" buffered="True">
+ this is text
+ </%def>
+ """, buffer_filters=['a'], default_filters=['b'])
+ assert flatten_result(t.render()) == "this is b this is b"
+
+ t = Template("""
+ <%!
+ class Foo(object):
+ foo = True
+ def __str__(self):
+ return "this is a"
+ def a(text):
+ return Foo()
+ def b(text):
+ if hasattr(text, 'foo'):
+ return str(text)
+ else:
+ return "this is b"
+ %>
+
+ ${'hi'}
+ ${foo()}
+ <%def name="foo()" buffered="True">
+ this is text
+ </%def>
+ """, buffer_filters=['a'], default_filters=['b'])
+ assert flatten_result(t.render()) == "this is b this is a"
+
+ t = Template("""
+ <%!
+ def a(text):
+ return "this is a"
+ def b(text):
+ return "this is b"
+ %>
+
+ ${foo()}
+ <%def name="foo()" filter="b">
+ this is text
+ </%def>
+ """, buffer_filters=['a'])
+ assert flatten_result(t.render()) == "this is a"
+
def test_builtins(self):
t = Template("""
diff --git a/test/template.py b/test/template.py
index 3627dad..6baac1c 100644
--- a/test/template.py
+++ b/test/template.py
@@ -200,7 +200,7 @@ class IncludeTest(unittest.TestCase):
<%page args="a,b,c"/>
this is b. ${a}, ${b}, ${c}
""")
- print lookup.get_template("a").code
+ #print lookup.get_template("a").code
assert flatten_result(lookup.get_template("a").render(a=7,b=8)) == "this is a this is b. 7, 8, 5"
def test_include_withargs(self):