aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2007-01-24 00:30:46 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2007-01-24 00:30:46 +0000
commitad3ddbb71c826844e4efef6f1181bee43422cab2 (patch)
tree2a2bf739aa1bef264601f8e7643b416e2df94729
parent64c7ba7bad1c49ab2985760357144f2ef4d5d378 (diff)
downloadexternal_python_mako-ad3ddbb71c826844e4efef6f1181bee43422cab2.tar.gz
external_python_mako-ad3ddbb71c826844e4efef6f1181bee43422cab2.tar.bz2
external_python_mako-ad3ddbb71c826844e4efef6f1181bee43422cab2.zip
- "expression_filter" argument in <%page> applies only to expressions
- added "default_filters" argument to Template, TemplateLookup. applies only to expressions, gets prepended to "expression_filter" arg from <%page>. defaults to ["unicode"], so that all expressions get stringified into u'' by default (this is what Mako already does). By setting to [], expressions are passed through raw. - added "imports" argument to Template, TemplateLookup. so you can predefine a list of import statements at the top of the template. can be used in conjunction with default_filters.
-rw-r--r--CHANGES8
-rw-r--r--lib/mako/codegen.py39
-rw-r--r--lib/mako/filters.py1
-rw-r--r--lib/mako/lookup.py4
-rw-r--r--lib/mako/template.py28
-rw-r--r--test/filters.py18
-rw-r--r--test/template.py12
7 files changed, 83 insertions, 27 deletions
diff --git a/CHANGES b/CHANGES
index 53727dc..5403c80 100644
--- a/CHANGES
+++ b/CHANGES
@@ -8,6 +8,14 @@ file
(takes filename, uri arguments). used for [ticket:14]
- added optional input_encoding flag to Template, to allow sending a unicode() object with no
magic encoding comment
+- "expression_filter" argument in <%page> applies only to expressions
+- added "default_filters" argument to Template, TemplateLookup. applies only to expressions,
+gets prepended to "expression_filter" arg from <%page>. defaults to ["unicode"], so that
+all expressions get stringified into u'' by default (this is what Mako already does).
+By setting to [], expressions are passed through raw.
+- added "imports" argument to Template, TemplateLookup. so you can predefine a list of
+import statements at the top of the template. can be used in conjunction with
+default_filters.
0.1.1
- buffet plugin supports string-based templates, allows ToscaWidgets to work [ticket:8]
diff --git a/lib/mako/codegen.py b/lib/mako/codegen.py
index 95e1ba2..ba83f1b 100644
--- a/lib/mako/codegen.py
+++ b/lib/mako/codegen.py
@@ -14,17 +14,19 @@ from mako import util, ast, parsetree, filters
MAGIC_NUMBER = 1
-def compile(node, uri, filename=None):
+def compile(node, uri, filename=None, default_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), node)
+ _GenerateRenderMethod(printer, _CompileContext(uri, filename, default_filters, imports), node)
return buf.getvalue()
class _CompileContext(object):
- def __init__(self, uri, filename):
+ def __init__(self, uri, filename, default_filters, imports):
self.uri = uri
self.filename = filename
+ self.default_filters = default_filters
+ self.imports = imports
class _GenerateRenderMethod(object):
"""a template visitor object which generates the full module source for a template."""
@@ -113,10 +115,21 @@ class _GenerateRenderMethod(object):
self.printer.writeline("_template_filename=%s" % repr(self.compiler.filename))
self.printer.writeline("_template_uri=%s" % repr(self.compiler.uri))
self.printer.writeline("_template_cache=cache.Cache(__name__, _modified_time)")
+ if self.compiler.imports:
+ buf = ''
+ for imp in self.compiler.imports:
+ buf += imp + "\n"
+ self.printer.writeline(imp)
+ impcode = ast.PythonCode(buf, 0, 0, 'template defined imports')
+ else:
+ impcode = None
main_identifiers = module_identifiers.branch(self.node)
module_identifiers.topleveldefs = module_identifiers.topleveldefs.union(main_identifiers.topleveldefs)
[module_identifiers.declared.add(x) for x in ["UNDEFINED"]]
+ if impcode:
+ [module_identifiers.declared.add(x) for x in impcode.declared_identifiers]
+
self.compiler.identifiers = module_identifiers
self.printer.writeline("_exports = %s" % repr([n.name for n in main_identifiers.topleveldefs.values()]))
self.printer.write("\n\n")
@@ -331,7 +344,7 @@ class _GenerateRenderMethod(object):
self.printer.writeline("_buf = context.pop_buffer()")
s = "_buf.getvalue()"
if filtered:
- s = self.create_filter_callable(node.filter_args.args, s)
+ s = self.create_filter_callable(node.filter_args.args, s, False)
self.printer.writeline(None)
if buffered or cached:
self.printer.writeline("return %s" % s)
@@ -376,13 +389,15 @@ class _GenerateRenderMethod(object):
None
)
- def create_filter_callable(self, args, target):
+ def create_filter_callable(self, args, target, is_expression):
"""write a filter-applying expression based on the filters present in the given
filter names, adjusting for the global 'default' filter aliases as needed."""
- d = dict([(k, "filters." + v.func_name) for k, v in filters.DEFAULT_ESCAPES.iteritems()])
+ d = dict([(k, (v is unicode and 'unicode' or "filters." + v.func_name)) for k, v in filters.DEFAULT_ESCAPES.iteritems()])
- if self.compiler.pagetag:
- args += self.compiler.pagetag.filter_args.args
+ 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
for e in args:
# if filter given as a function, get just the identifier portion
m = re.match(r'(.+?)(\(.*\))', e)
@@ -397,11 +412,11 @@ class _GenerateRenderMethod(object):
def visitExpression(self, node):
self.write_source_comment(node)
- if len(node.escapes) or (self.compiler.pagetag is not None and len(self.compiler.pagetag.filter_args.args)):
- s = self.create_filter_callable(node.escapes_code.args, "unicode(%s)" % node.text)
+ if len(node.escapes) or (self.compiler.pagetag is not None and len(self.compiler.pagetag.filter_args.args)) or len(self.compiler.default_filters):
+ s = self.create_filter_callable(node.escapes_code.args, "%s" % node.text, True)
self.printer.writeline("context.write(%s)" % s)
else:
- self.printer.writeline("context.write(unicode(%s))" % node.text)
+ self.printer.writeline("context.write(%s)" % node.text)
def visitControlLine(self, node):
if node.isend:
@@ -425,7 +440,7 @@ class _GenerateRenderMethod(object):
self.printer.writelines(
"finally:",
"_buf = context.pop_buffer()",
- "context.write(%s)" % self.create_filter_callable(node.filter_args.args, "_buf.getvalue()"),
+ "context.write(%s)" % self.create_filter_callable(node.filter_args.args, "_buf.getvalue()", False),
None
)
diff --git a/lib/mako/filters.py b/lib/mako/filters.py
index 5e35e34..5d782ee 100644
--- a/lib/mako/filters.py
+++ b/lib/mako/filters.py
@@ -147,6 +147,7 @@ DEFAULT_ESCAPES = {
'u':url_escape,
'trim':trim,
'entity':html_entities_escape,
+ 'unicode':unicode
}
diff --git a/lib/mako/lookup.py b/lib/mako/lookup.py
index 1c910cf..0f6730a 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, modulename_callable=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, modulename_callable=None, default_filters=['unicode'], imports=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, 'module_directory':module_directory, 'cache_type':cache_type, 'cache_dir':cache_dir or module_directory}
+ self.template_args = {'format_exceptions':format_exceptions, 'error_handler':error_handler, 'output_encoding':output_encoding, 'module_directory':module_directory, 'cache_type':cache_type, 'cache_dir':cache_dir or module_directory, 'default_filters':default_filters, 'imports':imports}
if collection_size == -1:
self.__collection = {}
self._uri_cache = {}
diff --git a/lib/mako/template.py b/lib/mako/template.py
index a3c4ea4..40a0872 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, module_filename=None, input_encoding=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, module_filename=None, input_encoding=None, default_filters=['unicode'], imports=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
@@ -38,9 +38,13 @@ class Template(object):
self.module_id = "memory:" + hex(id(self))
self.uri = self.module_id
+ self.default_filters = default_filters
+ self.input_encoding = input_encoding
+ self.imports = imports
+
# if plain text, compile code in memory only
if text is not None:
- (code, module) = _compile_text(text, self.module_id, filename, self.uri, input_encoding)
+ (code, module) = _compile_text(self, text, filename)
self._code = code
self._source = text
ModuleInfo(module, None, self, filename, code, text)
@@ -60,18 +64,18 @@ class Template(object):
util.verify_directory(os.path.dirname(path))
filemtime = os.stat(filename)[stat.ST_MTIME]
if not os.access(path, os.F_OK) or os.stat(path)[stat.ST_MTIME] < filemtime:
- _compile_module_file(file(filename).read(), self.module_id, filename, path, self.uri, input_encoding)
+ _compile_module_file(self, file(filename).read(), filename, path)
module = imp.load_source(self.module_id, path, file(path))
del sys.modules[self.module_id]
if module._magic_number != codegen.MAGIC_NUMBER:
- _compile_module_file(file(filename).read(), self.module_id, filename, path, self.uri, input_encoding)
+ _compile_module_file(self, file(filename).read(), filename, path)
module = imp.load_source(self.module_id, path, file(path))
del sys.modules[self.module_id]
ModuleInfo(module, path, self, filename, None, None)
else:
# template filename and no module directory, compile code
# in memory
- (code, module) = _compile_text(file(filename).read(), self.module_id, filename, self.uri, input_encoding)
+ (code, module) = _compile_text(self, file(filename).read(), filename)
self._source = None
self._code = code
ModuleInfo(module, None, self, filename, code, None)
@@ -153,19 +157,21 @@ class ModuleInfo(object):
return file(self.template_filename).read()
source = property(_get_source)
-def _compile_text(text, identifier, filename, uri, input_encoding):
- node = Lexer(text, filename, input_encoding=input_encoding).parse()
- source = codegen.compile(node, uri, filename)
+def _compile_text(template, text, filename):
+ identifier = template.module_id
+ node = Lexer(text, filename, input_encoding=template.input_encoding).parse()
+ source = codegen.compile(node, template.uri, filename, default_filters=template.default_filters, imports=template.imports)
cid = identifier
module = imp.new_module(cid)
code = compile(source, cid, 'exec')
exec code in module.__dict__, module.__dict__
return (source, module)
-def _compile_module_file(text, identifier, filename, outputpath, uri, input_encoding):
+def _compile_module_file(template, text, filename, outputpath):
+ identifier = template.module_id
(dest, name) = tempfile.mkstemp()
- node = Lexer(text, filename, input_encoding=input_encoding).parse()
- source = codegen.compile(node, uri, filename)
+ node = Lexer(text, filename, input_encoding=template.input_encoding).parse()
+ source = codegen.compile(node, template.uri, filename, default_filters=template.default_filters, imports=template.imports)
os.write(dest, source)
os.close(dest)
shutil.move(name, outputpath)
diff --git a/test/filters.py b/test/filters.py
index 8033ee4..d9ecb25 100644
--- a/test/filters.py
+++ b/test/filters.py
@@ -46,6 +46,24 @@ class FilterTest(unittest.TestCase):
assert t.render().strip()=="trim this string: some string to trim continue"
+ def test_import_2(self):
+ t = Template("""
+ trim this string: ${" some string to trim " | filters.trim} continue\
+ """, imports=["from mako import filters"])
+ print t.code
+ assert t.render().strip()=="trim this string: some string to trim continue"
+
+ def test_custom_default(self):
+ t = Template("""
+ <%!
+ def myfilter(x):
+ return "->" + x + "<-"
+ %>
+
+ hi ${'there'}
+ """, default_filters=['myfilter'])
+ assert t.render().strip()=="hi ->there<-"
+
def test_global(self):
t = Template("""
<%page expression_filter="h"/>
diff --git a/test/template.py b/test/template.py
index aca42ed..88f61e3 100644
--- a/test/template.py
+++ b/test/template.py
@@ -67,7 +67,15 @@ class EncodingTest(unittest.TestCase):
res = Template(s2).render_unicode(f=lambda x:x)
assert res == u"hello śląsk"
-
+ def test_raw_strings(self):
+ """test that raw strings go straight thru with default_filters turned off"""
+ g = 'śląsk'
+ s = u"# -*- coding: utf-8 -*-\nhello ${x}"
+ t = Template(s, default_filters=[])
+ y = t.render(x=g)
+ print t.code
+ assert y == "hello śląsk"
+
def test_encoding(self):
val = u"""Alors vous imaginez ma surprise, au lever du jour, quand une drôle de petit voix m’a réveillé. Elle disait: « S’il vous plaît… dessine-moi un mouton! »"""
template = Template(val, output_encoding='utf-8')
@@ -101,7 +109,7 @@ class PageArgsTest(unittest.TestCase):
this is page, ${x}, ${y}, ${z}, ${w}
""")
-
+ print template.code
assert flatten_result(template.render(x=5, y=10, w=17)) == "this is page, 5, 10, 7, 17"
def test_overrides_builtins(self):