diff options
| -rw-r--r-- | CHANGES | 7 | ||||
| -rw-r--r-- | lib/mako/ast.py | 2 | ||||
| -rw-r--r-- | lib/mako/codegen.py | 30 | ||||
| -rw-r--r-- | lib/mako/filters.py | 3 | ||||
| -rw-r--r-- | lib/mako/lookup.py | 4 | ||||
| -rw-r--r-- | lib/mako/template.py | 8 | ||||
| -rw-r--r-- | test/ast.py | 11 | ||||
| -rw-r--r-- | test/filters.py | 89 | ||||
| -rw-r--r-- | test/template.py | 2 |
9 files changed, 134 insertions, 22 deletions
@@ -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() == "<tag>this is html</tag>" + + 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() == "<tag>this is html</tag>" + + 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): |
