summaryrefslogtreecommitdiff
path: root/mako/codegen.py
diff options
context:
space:
mode:
Diffstat (limited to 'mako/codegen.py')
-rw-r--r--mako/codegen.py202
1 files changed, 101 insertions, 101 deletions
diff --git a/mako/codegen.py b/mako/codegen.py
index 1a64944..9c577d5 100644
--- a/mako/codegen.py
+++ b/mako/codegen.py
@@ -23,7 +23,7 @@ def compile(node,
generate_magic_comment=True,
disable_unicode=False,
strict_undefined=False):
-
+
"""Generate module source code given a parsetree node,
uri, and optional source filename"""
@@ -33,8 +33,8 @@ def compile(node,
# result into a unicode object, in "disable_unicode" mode
if not util.py3k and isinstance(source_encoding, unicode):
source_encoding = source_encoding.encode(source_encoding)
-
-
+
+
buf = util.FastEncodingBuffer()
printer = PythonPrinter(buf)
@@ -71,11 +71,11 @@ class _CompileContext(object):
self.generate_magic_comment = generate_magic_comment
self.disable_unicode = disable_unicode
self.strict_undefined = strict_undefined
-
+
class _GenerateRenderMethod(object):
"""A template visitor object which generates the
full module source for a template.
-
+
"""
def __init__(self, printer, compiler, node):
self.printer = printer
@@ -83,7 +83,7 @@ class _GenerateRenderMethod(object):
self.compiler = compiler
self.node = node
self.identifier_stack = [None]
-
+
self.in_def = isinstance(node, parsetree.DefTag)
if self.in_def:
@@ -111,24 +111,24 @@ class _GenerateRenderMethod(object):
args = ['context']
else:
args = [a for a in ['context'] + args]
-
+
self.write_render_callable(
pagetag or node,
name, args,
buffered, filtered, cached)
-
+
if defs is not None:
for node in defs:
_GenerateRenderMethod(printer, compiler, node)
-
+
@property
def identifiers(self):
return self.identifier_stack[-1]
-
+
def write_toplevel(self):
"""Traverse a template structure for module-level directives and
generate the start of module-level code.
-
+
"""
inherit = []
namespaces = {}
@@ -136,7 +136,7 @@ class _GenerateRenderMethod(object):
encoding =[None]
self.compiler.pagetag = None
-
+
class FindTopLevel(object):
def visitInheritTag(s, node):
inherit.append(node)
@@ -147,7 +147,7 @@ class _GenerateRenderMethod(object):
def visitCode(s, node):
if node.ismodule:
module_code.append(node)
-
+
f = FindTopLevel()
for n in self.node.nodes:
n.accept_visitor(f)
@@ -160,13 +160,13 @@ class _GenerateRenderMethod(object):
module_identifiers = _Identifiers()
module_identifiers.declared = module_ident
-
+
# module-level names, python code
if self.compiler.generate_magic_comment and \
self.compiler.source_encoding:
self.printer.writeline("# -*- encoding:%s -*-" %
self.compiler.source_encoding)
-
+
self.printer.writeline("from mako import runtime, filters, cache")
self.printer.writeline("UNDEFINED = runtime.UNDEFINED")
self.printer.writeline("__M_dict_builtin = dict")
@@ -192,7 +192,7 @@ class _GenerateRenderMethod(object):
filename='template defined imports')
else:
impcode = None
-
+
main_identifiers = module_identifiers.branch(self.node)
module_identifiers.topleveldefs = \
module_identifiers.topleveldefs.\
@@ -200,7 +200,7 @@ class _GenerateRenderMethod(object):
module_identifiers.declared.add("UNDEFINED")
if impcode:
module_identifiers.declared.update(impcode.declared_identifiers)
-
+
self.compiler.identifiers = module_identifiers
self.printer.writeline("_exports = %r" %
[n.name for n in
@@ -221,14 +221,14 @@ class _GenerateRenderMethod(object):
def write_render_callable(self, node, name, args, buffered, filtered, cached):
"""write a top-level render callable.
-
+
this could be the main render() method or that of a top-level def."""
-
+
if self.in_def:
decorator = node.decorator
if decorator:
self.printer.writeline("@runtime._decorate_toplevel(%s)" % decorator)
-
+
self.printer.writelines(
"def %s(%s):" % (name, ','.join(args)),
"context.caller_stack._push_frame()",
@@ -236,7 +236,7 @@ class _GenerateRenderMethod(object):
)
if buffered or filtered or cached:
self.printer.writeline("context._push_buffer()")
-
+
self.identifier_stack.append(self.compiler.identifiers.branch(self.node))
if not self.in_def and '**pageargs' in args:
self.identifier_stack[-1].argument_declared.add('pageargs')
@@ -264,7 +264,7 @@ class _GenerateRenderMethod(object):
node, name,
args, buffered,
self.identifiers, toplevel=True)
-
+
def write_module_code(self, module_code):
"""write module-level template code, i.e. that which
is enclosed in <%! %> tags in the template."""
@@ -274,7 +274,7 @@ class _GenerateRenderMethod(object):
def write_inherit(self, node):
"""write the module-level inheritance-determination callable."""
-
+
self.printer.writelines(
"def _mako_inherit(template, context):",
"_mako_generate_namespaces(context)",
@@ -296,7 +296,7 @@ class _GenerateRenderMethod(object):
)
self.printer.writeline("def _mako_generate_namespaces(context):")
-
+
for node in namespaces.values():
if node.attributes.has_key('import'):
self.compiler.has_ns_imports = True
@@ -330,16 +330,16 @@ class _GenerateRenderMethod(object):
)
if eval(node.attributes.get('inheritable', "False")):
self.printer.writeline("context['self'].%s = ns" % (node.name))
-
+
self.printer.writeline("context.namespaces[(__name__, %s)] = ns" % repr(node.name))
self.printer.write("\n")
if not len(namespaces):
self.printer.writeline("pass")
self.printer.writeline(None)
-
+
def write_variable_declares(self, identifiers, toplevel=False, limit=None):
"""write variable declarations at the top of a function.
-
+
the variable declarations are in the form of callable
definitions for defs and/or name lookup within the
function's context argument. the names declared are based
@@ -348,22 +348,22 @@ class _GenerateRenderMethod(object):
operation. names that are assigned within the body are
assumed to be locally-scoped variables and are not
separately declared.
-
+
for def callable definitions, if the def is a top-level
callable then a 'stub' callable is generated which wraps
the current Context into a closure. if the def is not
top-level, it is fully rendered as a local closure.
-
+
"""
-
+
# collection of all defs available to us in this scope
comp_idents = dict([(c.name, c) for c in identifiers.defs])
to_write = set()
-
+
# write "context.get()" for all variables we are going to
# need that arent in the namespace yet
to_write = to_write.union(identifiers.undeclared)
-
+
# write closure functions for closures that we define
# right here
to_write = to_write.union([c.name for c in identifiers.closuredefs.values()])
@@ -372,18 +372,18 @@ class _GenerateRenderMethod(object):
# signature of the callable
to_write = to_write.difference(identifiers.argument_declared)
- # remove identifiers that we are going to assign to.
+ # remove identifiers that we are going to assign to.
# in this way we mimic Python's behavior,
# i.e. assignment to a variable within a block
# means that variable is now a "locally declared" var,
- # which cannot be referenced beforehand.
+ # which cannot be referenced beforehand.
to_write = to_write.difference(identifiers.locally_declared)
-
+
# if a limiting set was sent, constraint to those items in that list
# (this is used for the caching decorator)
if limit is not None:
to_write = to_write.intersection(limit)
-
+
if toplevel and getattr(self.compiler, 'has_ns_imports', False):
self.printer.writeline("_import_ns = {}")
self.compiler.has_imports = True
@@ -395,7 +395,7 @@ class _GenerateRenderMethod(object):
ident,
re.split(r'\s*,\s*', ns.attributes['import'])
))
-
+
for ident in to_write:
if ident in comp_idents:
comp = comp_idents[ident]
@@ -440,9 +440,9 @@ class _GenerateRenderMethod(object):
self.printer.writeline(
"%s = context.get(%r, UNDEFINED)" % (ident, ident)
)
-
+
self.printer.writeline("__M_writer = context.writer()")
-
+
def write_source_comment(self, node):
"""write a source comment containing the line number of the corresponding template line."""
if self.last_source_line != node.lineno:
@@ -454,7 +454,7 @@ class _GenerateRenderMethod(object):
funcname = node.function_decl.funcname
namedecls = node.function_decl.get_argument_expressions()
nameargs = node.function_decl.get_argument_expressions(include_defaults=False)
-
+
if not self.in_def and (
len(self.identifiers.locally_assigned) > 0 or
len(self.identifiers.argument_declared) > 0):
@@ -464,12 +464,12 @@ class _GenerateRenderMethod(object):
self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls)))
self.printer.writeline("return render_%s(%s)" % (funcname, ",".join(nameargs)))
self.printer.writeline(None)
-
+
def write_inline_def(self, node, identifiers, nested):
"""write a locally-available def callable inside an enclosing def."""
-
+
namedecls = node.function_decl.get_argument_expressions()
-
+
decorator = node.decorator
if decorator:
self.printer.writeline("@runtime._decorate_inline(context, %s)" % decorator)
@@ -489,26 +489,26 @@ class _GenerateRenderMethod(object):
identifiers = identifiers.branch(node, nested=nested)
self.write_variable_declares(identifiers)
-
+
self.identifier_stack.append(identifiers)
for n in node.nodes:
n.accept_visitor(self)
self.identifier_stack.pop()
-
+
self.write_def_finish(node, buffered, filtered, cached)
self.printer.writeline(None)
if cached:
self.write_cache_decorator(node, node.name,
namedecls, False, identifiers,
inline=True, toplevel=False)
-
+
def write_def_finish(self, node, buffered, filtered, cached, callstack=True):
"""write the end section of a rendering function, either outermost or inline.
-
+
this takes into account if the rendering function was filtered, buffered, etc.
and closes the corresponding try: block if any, and writes code to retrieve
captured content, apply filters, send proper return value."""
-
+
if not buffered and not cached and not filtered:
self.printer.writeline("return ''")
if callstack:
@@ -517,7 +517,7 @@ class _GenerateRenderMethod(object):
"context.caller_stack._pop_frame()",
None
)
-
+
if buffered or filtered or cached:
if buffered or cached:
# in a caching scenario, don't try to get a writer
@@ -533,10 +533,10 @@ class _GenerateRenderMethod(object):
"finally:",
"__M_buf, __M_writer = context._pop_buffer_and_writer()"
)
-
+
if callstack:
self.printer.writeline("context.caller_stack._pop_frame()")
-
+
s = "__M_buf.getvalue()"
if filtered:
s = self.create_filter_callable(node.filter_args.args, s, False)
@@ -556,7 +556,7 @@ class _GenerateRenderMethod(object):
inline=False, toplevel=False):
"""write a post-function decorator to replace a rendering
callable with a cached version of itself."""
-
+
self.printer.writeline("__M_%s = %s" % (name, name))
cachekey = node_or_pagetag.parsed_attributes.get('cache_key', repr(name))
cacheargs = {}
@@ -577,9 +577,9 @@ class _GenerateRenderMethod(object):
cacheargs[arg[1]] == int(eval(val))
else:
cacheargs[arg[1]] = val
-
+
self.printer.writeline("def %s(%s):" % (name, ','.join(args)))
-
+
# form "arg1, arg2, arg3=arg3, arg4=arg4", etc.
pass_args = [
'=' in a and "%s=%s" % ((a.split('=')[0],)*2) or a
@@ -615,7 +615,7 @@ class _GenerateRenderMethod(object):
"""write a filter-applying expression based on the filters
present in the given filter names, adjusting for the global
'default' filter aliases as needed."""
-
+
def locate_encode(name):
if re.match(r'decode\..+', name):
return "filters." + name
@@ -623,7 +623,7 @@ class _GenerateRenderMethod(object):
return filters.NON_UNICODE_ESCAPES.get(name, name)
else:
return filters.DEFAULT_ESCAPES.get(name, name)
-
+
if 'n' not in args:
if is_expression:
if self.compiler.pagetag:
@@ -645,7 +645,7 @@ class _GenerateRenderMethod(object):
assert e is not None
target = "%s(%s)" % (e, target)
return target
-
+
def visitExpression(self, node):
self.write_source_comment(node)
if len(node.escapes) or \
@@ -654,12 +654,12 @@ class _GenerateRenderMethod(object):
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("__M_writer(%s)" % s)
else:
self.printer.writeline("__M_writer(%s)" % node.text)
-
+
def visitControlLine(self, node):
if node.isend:
if not node.get_children():
@@ -668,11 +668,11 @@ class _GenerateRenderMethod(object):
else:
self.write_source_comment(node)
self.printer.writeline(node.text)
-
+
def visitText(self, node):
self.write_source_comment(node)
self.printer.writeline("__M_writer(%s)" % repr(node.content))
-
+
def visitTextTag(self, node):
filtered = len(node.filter_args.args) > 0
if filtered:
@@ -693,7 +693,7 @@ class _GenerateRenderMethod(object):
False),
None
)
-
+
def visitCode(self, node):
if not node.ismodule:
self.write_source_comment(node)
@@ -722,10 +722,10 @@ class _GenerateRenderMethod(object):
self.printer.writeline(
"runtime._include_file(context, %s, _template_uri)" %
(node.parsed_attributes['file']))
-
+
def visitNamespaceTag(self, node):
pass
-
+
def visitDefTag(self, node):
pass
@@ -734,7 +734,7 @@ class _GenerateRenderMethod(object):
# as ensure the given namespace will be imported,
# pre-import the namespace, etc.
self.visitCallTag(node)
-
+
def visitCallTag(self, node):
self.printer.writeline("def ccall(caller):")
export = ['body']
@@ -745,7 +745,7 @@ class _GenerateRenderMethod(object):
# <%def>s within <%call> we want the current caller
# off the call stack (if any)
body_identifiers.add_declared('caller')
-
+
self.identifier_stack.append(body_identifiers)
class DefVisitor(object):
def visitDefTag(s, node):
@@ -760,10 +760,10 @@ class _GenerateRenderMethod(object):
for n in node.nodes:
n.accept_visitor(vis)
self.identifier_stack.pop()
-
- bodyargs = node.body_decl.get_argument_expressions()
+
+ bodyargs = node.body_decl.get_argument_expressions()
self.printer.writeline("def body(%s):" % ','.join(bodyargs))
-
+
# TODO: figure out best way to specify
# buffering/nonbuffering (at call time would be better)
buffered = False
@@ -774,11 +774,11 @@ class _GenerateRenderMethod(object):
)
self.write_variable_declares(body_identifiers)
self.identifier_stack.append(body_identifiers)
-
+
for n in node.nodes:
n.accept_visitor(self)
self.identifier_stack.pop()
-
+
self.write_def_finish(node, buffered, False, False, callstack=False)
self.printer.writelines(
None,
@@ -803,9 +803,9 @@ class _GenerateRenderMethod(object):
class _Identifiers(object):
"""tracks the status of identifier names as template code is rendered."""
-
+
def __init__(self, node=None, parent=None, nested=False):
-
+
if parent is not None:
# if we are the branch created in write_namespaces(),
# we don't share any context from the main body().
@@ -819,56 +819,56 @@ class _Identifiers(object):
union([c.name for c in parent.closuredefs.values()]).\
union(parent.locally_declared).\
union(parent.argument_declared)
-
+
# if these identifiers correspond to a "nested"
# scope, it means whatever the parent identifiers
# had as undeclared will have been declared by that parent,
# and therefore we have them in our scope.
if nested:
self.declared = self.declared.union(parent.undeclared)
-
+
# top level defs that are available
self.topleveldefs = util.SetLikeDict(**parent.topleveldefs)
else:
self.declared = set()
self.topleveldefs = util.SetLikeDict()
-
+
# things within this level that are referenced before they
# are declared (e.g. assigned to)
self.undeclared = set()
-
+
# things that are declared locally. some of these things
# could be in the "undeclared" list as well if they are
# referenced before declared
self.locally_declared = set()
-
- # assignments made in explicit python blocks.
+
+ # assignments made in explicit python blocks.
# these will be propagated to
# the context of local def calls.
self.locally_assigned = set()
-
+
# things that are declared in the argument
# signature of the def callable
self.argument_declared = set()
-
+
# closure defs that are defined in this level
self.closuredefs = util.SetLikeDict()
-
+
self.node = node
-
+
if node is not None:
node.accept_visitor(self)
-
+
def branch(self, node, **kwargs):
"""create a new Identifiers for a new Node, with
this Identifiers as the parent."""
-
+
return _Identifiers(node, self, **kwargs)
-
+
@property
def defs(self):
return set(self.topleveldefs.union(self.closuredefs).values())
-
+
def __repr__(self):
return "Identifiers(declared=%r, locally_declared=%r, "\
"undeclared=%r, topleveldefs=%r, closuredefs=%r, argumentdeclared=%r)" %\
@@ -879,33 +879,33 @@ class _Identifiers(object):
[c.name for c in self.topleveldefs.values()],
[c.name for c in self.closuredefs.values()],
self.argument_declared)
-
+
def check_declared(self, node):
"""update the state of this Identifiers with the undeclared
and declared identifiers of the given node."""
-
+
for ident in node.undeclared_identifiers():
if ident != 'context' and ident not in self.declared.union(self.locally_declared):
self.undeclared.add(ident)
for ident in node.declared_identifiers():
self.locally_declared.add(ident)
-
+
def add_declared(self, ident):
self.declared.add(ident)
if ident in self.undeclared:
self.undeclared.remove(ident)
-
+
def visitExpression(self, node):
self.check_declared(node)
-
+
def visitControlLine(self, node):
self.check_declared(node)
-
+
def visitCode(self, node):
if not node.ismodule:
self.check_declared(node)
self.locally_assigned = self.locally_assigned.union(node.declared_identifiers())
-
+
def visitNamespaceTag(self, node):
# only traverse into the sub-elements of a
# <%namespace> tag if we are the branch created in
@@ -913,7 +913,7 @@ class _Identifiers(object):
if self.node is node:
for n in node.nodes:
n.accept_visitor(self)
-
+
def visitDefTag(self, node):
if node.is_root():
self.topleveldefs[node.name] = node
@@ -923,25 +923,25 @@ class _Identifiers(object):
for ident in node.undeclared_identifiers():
if ident != 'context' and ident not in self.declared.union(self.locally_declared):
self.undeclared.add(ident)
-
+
# visit defs only one level deep
if node is self.node:
for ident in node.declared_identifiers():
self.argument_declared.add(ident)
for n in node.nodes:
n.accept_visitor(self)
-
+
def visitIncludeTag(self, node):
self.check_declared(node)
-
+
def visitPageTag(self, node):
for ident in node.declared_identifiers():
self.argument_declared.add(ident)
self.check_declared(node)
-
+
def visitCallNamespaceTag(self, node):
self.visitCallTag(node)
-
+
def visitCallTag(self, node):
if node is self.node:
for ident in node.undeclared_identifiers():
@@ -955,4 +955,4 @@ class _Identifiers(object):
for ident in node.undeclared_identifiers():
if ident != 'context' and ident not in self.declared.union(self.locally_declared):
self.undeclared.add(ident)
-
+