import json
import re
import time
from mako import ast
from mako import exceptions
from mako import filters
from mako import parsetree
from mako import util
from mako.pygen import PythonPrinter
MAGIC_NUMBER = 10
TOPLEVEL_DECLARED = {"UNDEFINED", "STOP_RENDERING"}
RESERVED_NAMES = {"context", "loop"}.union(TOPLEVEL_DECLARED)
def compile( node,
uri,
filename=None,
default_filters=None,
buffer_filters=None,
imports=None,
future_imports=None,
source_encoding=None,
generate_magic_comment=True,
strict_undefined=False,
enable_loop=True,
reserved_names=frozenset(),
):
buf = util.FastEncodingBuffer()
printer = PythonPrinter(buf)
_GenerateRenderMethod(
printer,
_CompileContext(
uri,
filename,
default_filters,
buffer_filters,
imports,
future_imports,
source_encoding,
generate_magic_comment,
strict_undefined,
enable_loop,
reserved_names,
),
node,
)
return buf.getvalue()
class _CompileContext:
def __init__(
self,
uri,
filename,
default_filters,
buffer_filters,
imports,
future_imports,
source_encoding,
generate_magic_comment,
strict_undefined,
enable_loop,
reserved_names,
):
self.uri = uri
self.filename = filename
self.default_filters = default_filters
self.buffer_filters = buffer_filters
self.imports = imports
self.future_imports = future_imports
self.source_encoding = source_encoding
self.generate_magic_comment = generate_magic_comment
self.strict_undefined = strict_undefined
self.enable_loop = enable_loop
self.reserved_names = reserved_names
class _GenerateRenderMethod:
def __init__(self, printer, compiler, node):
self.printer = printer
self.compiler = compiler
self.node = node
self.identifier_stack = [None]
self.in_def = isinstance(node, (parsetree.DefTag, parsetree.BlockTag))
if self.in_def:
name = "render_%s" % node.funcname
args = node.get_argument_expressions()
filtered = len(node.filter_args.args) > 0
buffered = eval(node.attributes.get("buffered", "False"))
cached = eval(node.attributes.get("cached", "False"))
defs = None
pagetag = None
if node.is_block and not node.is_anonymous:
args += ["**pageargs"]
else:
defs = self.write_toplevel()
pagetag = self.compiler.pagetag
name = "render_body"
if pagetag is not None:
args = pagetag.body_decl.get_argument_expressions()
if not pagetag.body_decl.kwargs:
args += ["**pageargs"]
cached = eval(pagetag.attributes.get("cached", "False"))
self.compiler.enable_loop = self.compiler.enable_loop or eval(
pagetag.attributes.get("enable_loop", "False")
)
else:
args = ["**pageargs"]
cached = False
buffered = filtered = False
if args is None:
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)
if not self.in_def:
self.write_metadata_struct()
def write_metadata_struct(self):
self.printer.source_map[self.printer.lineno] = max(
self.printer.source_map
)
struct = {
"filename": self.compiler.filename,
"uri": self.compiler.uri,
"source_encoding": self.compiler.source_encoding,
"line_map": self.printer.source_map,
}
self.printer.writelines(
'"""',
"__M_BEGIN_METADATA",
json.dumps(struct),
"__M_END_METADATA\n" '"""',
)
@property
def identifiers(self):
return self.identifier_stack[-1]
def write_toplevel(self):
inherit = []
namespaces = {}
module_code = []
self.compiler.pagetag = None
class FindTopLevel:
def visitInheritTag(s, node):
inherit.append(node)
def visitNamespaceTag(s, node):
namespaces[node.name] = node
def visitPageTag(s, node):
self.compiler.pagetag = node
def visitCode(s, node):
if node.ismodule:
module_code.append(node)
f = FindTopLevel()
for n in self.node.nodes:
n.accept_visitor(f)
self.compiler.namespaces = namespaces
module_ident = set()
for n in module_code:
module_ident = module_ident.union(n.declared_identifiers())
module_identifiers = _Identifiers(self.compiler)
module_identifiers.declared = module_ident
if (
self.compiler.generate_magic_comment
and self.compiler.source_encoding
):
self.printer.writeline(
"# -*- coding:%s -*-" % self.compiler.source_encoding
)
if self.compiler.future_imports:
self.printer.writeline(
"from __future__ import %s"
% (", ".join(self.compiler.future_imports),)
)
self.printer.writeline("from mako import runtime, filters, cache")
self.printer.writeline("UNDEFINED = runtime.UNDEFINED")
self.printer.writeline("STOP_RENDERING = runtime.STOP_RENDERING")
self.printer.writeline("__M_dict_builtin = dict")
self.printer.writeline("__M_locals_builtin = locals")
self.printer.writeline("_magic_number = %r" % MAGIC_NUMBER)
self.printer.writeline("_modified_time = %r" % time.time())
self.printer.writeline("_enable_loop = %r" % self.compiler.enable_loop)
self.printer.writeline(
"_template_filename = %r" % self.compiler.filename
)
self.printer.writeline("_template_uri = %r" % self.compiler.uri)
self.printer.writeline(
"_source_encoding = %r" % self.compiler.source_encoding
)
if self.compiler.imports:
buf = ""
for imp in self.compiler.imports:
buf += imp + "\n"
self.printer.writeline(imp)
impcode = ast.PythonCode(
buf,
source="",
lineno=0,
pos=0,
filename="template defined imports",
)
else:
impcode = None
main_identifiers = module_identifiers.branch(self.node)
mit = module_identifiers.topleveldefs
module_identifiers.topleveldefs = mit.union(
main_identifiers.topleveldefs
)
module_identifiers.declared.update(TOPLEVEL_DECLARED)
if impcode:
module_identifiers.declared.update(impcode.declared_identifiers)
self.compiler.identifiers = module_identifiers
self.printer.writeline(
"_exports = %r"
% [n.name for n in main_identifiers.topleveldefs.values()]
)
self.printer.write_blanks(2)
if len(module_code):
self.write_module_code(module_code)
if len(inherit):
self.write_namespaces(namespaces)
self.write_inherit(inherit[-1])
elif len(namespaces):
self.write_namespaces(namespaces)
return list(main_identifiers.topleveldefs.values())
def write_render_callable(
self, node, name, args, buffered, filtered, cached
):
if self.in_def:
decorator = node.decorator
if decorator:
self.printer.writeline(
"@runtime._decorate_toplevel(%s)" % decorator
)
self.printer.start_source(node.lineno)
self.printer.writelines(
"def %s(%s):" % (name, ",".join(args)),
"__M_caller = context.caller_stack._push_frame()",
"try:",
)
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 or self.node.is_block) and "**pageargs" in args:
self.identifier_stack[-1].argument_declared.add("pageargs")
if not self.in_def and (
len(self.identifiers.locally_assigned) > 0
or len(self.identifiers.argument_declared) > 0
):
self.printer.writeline(
"__M_locals = __M_dict_builtin(%s)"
% ",".join(
[
"%s=%s" % (x, x)
for x in self.identifiers.argument_declared
]
)
)
self.write_variable_declares(self.identifiers, toplevel=True)
for n in self.node.nodes:
n.accept_visitor(self)
self.write_def_finish(self.node, buffered, filtered, cached)
self.printer.writeline(None)
self.printer.write_blanks(2)
if cached:
self.write_cache_decorator(
node, name, args, buffered, self.identifiers, toplevel=True
)
def write_module_code(self, module_code):
for n in module_code:
self.printer.write_indented_block(n.text, starting_lineno=n.lineno)
def write_inherit(self, node):
self.printer.writelines(
"def _mako_inherit(template, context):",
"_mako_generate_namespaces(context)",
"return runtime._inherit_from(context, %s, _template_uri)"
% (node.parsed_attributes["file"]),
None,
)
def write_namespaces(self, namespaces):
self.printer.writelines(
"def _mako_get_namespace(context, name):",
"try:",
"return context.namespaces[(__name__, name)]",
"except KeyError:",
"_mako_generate_namespaces(context)",
"return context.namespaces[(__name__, name)]",
None,
None,
)
self.printer.writeline("def _mako_generate_namespaces(context):")
for node in namespaces.values():
if "import" in node.attributes:
self.compiler.has_ns_imports = True
self.printer.start_source(node.lineno)
if len(node.nodes):
self.printer.writeline("def make_namespace():")
export = []
identifiers = self.compiler.identifiers.branch(node)
self.in_def = True
class NSDefVisitor:
def visitDefTag(s, node):
s.visitDefOrBase(node)
def visitBlockTag(s, node):
s.visitDefOrBase(node)
def visitDefOrBase(s, node):
if node.is_anonymous:
raise exceptions.CompileException(
"Can't put anonymous blocks inside "
"<%namespace>",
**node.exception_kwargs,
)
self.write_inline_def(node, identifiers, nested=False)
export.append(node.funcname)
vis = NSDefVisitor()
for n in node.nodes:
n.accept_visitor(vis)
self.printer.writeline("return [%s]" % (",".join(export)))
self.printer.writeline(None)
self.in_def = False
callable_name = "make_namespace()"
else:
callable_name = "None"
if "file" in node.parsed_attributes:
self.printer.writeline(
"ns = runtime.TemplateNamespace(%r,"
" context._clean_inheritance_tokens(),"
" templateuri=%s, callables=%s, "
" calling_uri=_template_uri)"
% (
node.name,
node.parsed_attributes.get("file", "None"),
callable_name,
)
)
elif "module" in node.parsed_attributes:
self.printer.writeline(
"ns = runtime.ModuleNamespace(%r,"
" context._clean_inheritance_tokens(),"
" callables=%s, calling_uri=_template_uri,"
" module=%s)"
% (
node.name,
callable_name,
node.parsed_attributes.get("module", "None"),
)
)
else:
self.printer.writeline(
"ns = runtime.Namespace(%r,"
" context._clean_inheritance_tokens(),"
" callables=%s, calling_uri=_template_uri)"
% (node.name, callable_name)
)
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_blanks(1)
if not len(namespaces):
self.printer.writeline("pass")
self.printer.writeline(None)
def write_variable_declares(self, identifiers, toplevel=False, limit=None):
comp_idents = {c.funcname: c for c in identifiers.defs}
to_write = set()
to_write = to_write.union(identifiers.undeclared)
to_write = to_write.union(
[c.funcname for c in identifiers.closuredefs.values()]
)
to_write = to_write.difference(identifiers.argument_declared)
to_write = to_write.difference(identifiers.locally_declared)
if self.compiler.enable_loop:
has_loop = "loop" in to_write
to_write.discard("loop")
else:
has_loop = False
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
for ident, ns in self.compiler.namespaces.items():
if "import" in ns.attributes:
self.printer.writeline(
"_mako_get_namespace(context, %r)."
"_populate(_import_ns, %r)"
% (
ident,
re.split(r"\s*,\s*", ns.attributes["import"]),
)
)
if has_loop:
self.printer.writeline("loop = __M_loop = runtime.LoopStack()")
for ident in to_write:
if ident in comp_idents:
comp = comp_idents[ident]
if comp.is_block:
if not comp.is_anonymous:
self.write_def_decl(comp, identifiers)
else:
self.write_inline_def(comp, identifiers, nested=True)
else:
if comp.is_root():
self.write_def_decl(comp, identifiers)
else:
self.write_inline_def(comp, identifiers, nested=True)
elif ident in self.compiler.namespaces:
self.printer.writeline(
"%s = _mako_get_namespace(context, %r)" % (ident, ident)
)
else:
if getattr(self.compiler, "has_ns_imports", False):
if self.compiler.strict_undefined:
self.printer.writelines(
"%s = _import_ns.get(%r, UNDEFINED)"
% (ident, ident),
"if %s is UNDEFINED:" % ident,
"try:",
"%s = context[%r]" % (ident, ident),
"except KeyError:",
"raise NameError(\"'%s' is not defined\")" % ident,
None,
None,
)
else:
self.printer.writeline(
"%s = _import_ns.get"
"(%r, context.get(%r, UNDEFINED))"
% (ident, ident, ident)
)
else:
if self.compiler.strict_undefined:
self.printer.writelines(
"try:",
"%s = context[%r]" % (ident, ident),
"except KeyError:",
"raise NameError(\"'%s' is not defined\")" % ident,
None,
)
else:
self.printer.writeline(
"%s = context.get(%r, UNDEFINED)" % (ident, ident)
)
self.printer.writeline("__M_writer = context.writer()")
def write_def_decl(self, node, identifiers):
funcname = node.funcname
namedecls = node.get_argument_expressions()
nameargs = node.get_argument_expressions(as_call=True)
if not self.in_def and (
len(self.identifiers.locally_assigned) > 0
or len(self.identifiers.argument_declared) > 0
):
nameargs.insert(0, "context._locals(__M_locals)")
else:
nameargs.insert(0, "context")
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):
namedecls = node.get_argument_expressions()
decorator = node.decorator
if decorator:
self.printer.writeline(
"@runtime._decorate_inline(context, %s)" % decorator
)
self.printer.writeline(
"def %s(%s):" % (node.funcname, ",".join(namedecls))
)
filtered = len(node.filter_args.args) > 0
buffered = eval(node.attributes.get("buffered", "False"))
cached = eval(node.attributes.get("cached", "False"))
self.printer.writelines(
"__M_caller = context.caller_stack._push_frame()",
"try:",
)
if buffered or filtered or cached:
self.printer.writelines("context._push_buffer()")
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.funcname,
namedecls,
False,
identifiers,
inline=True,
toplevel=False,
)
def write_def_finish(
self, node, buffered, filtered, cached, callstack=True
):
if not buffered and not cached and not filtered:
self.printer.writeline("return ''")
if callstack:
self.printer.writelines(
"finally:", "context.caller_stack._pop_frame()", None
)
if buffered or filtered or cached:
if buffered or cached:
self.printer.writelines(
"finally:", "__M_buf = context._pop_buffer()"
)
else:
self.printer.writelines(
"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
)
self.printer.writeline(None)
if buffered and not cached:
s = self.create_filter_callable(
self.compiler.buffer_filters, s, False
)
if buffered or cached:
self.printer.writeline("return %s" % s)
else:
self.printer.writelines("__M_writer(%s)" % s, "return ''")
def write_cache_decorator(
self,
node_or_pagetag,
name,
args,
buffered,
identifiers,
inline=False,
toplevel=False,
):
self.printer.writeline("__M_%s = %s" % (name, name))
cachekey = node_or_pagetag.parsed_attributes.get(
"cache_key", repr(name)
)
cache_args = {}
if self.compiler.pagetag is not None:
cache_args.update(
(pa[6:], self.compiler.pagetag.parsed_attributes[pa])
for pa in self.compiler.pagetag.parsed_attributes
if pa.startswith("cache_") and pa != "cache_key"
)
cache_args.update(
(pa[6:], node_or_pagetag.parsed_attributes[pa])
for pa in node_or_pagetag.parsed_attributes
if pa.startswith("cache_") and pa != "cache_key"
)
if "timeout" in cache_args:
cache_args["timeout"] = int(eval(cache_args["timeout"]))
self.printer.writeline("def %s(%s):" % (name, ",".join(args)))
pass_args = [
"%s=%s" % ((a.split("=")[0],) * 2) if "=" in a else a for a in args
]
self.write_variable_declares(
identifiers,
toplevel=toplevel,
limit=node_or_pagetag.undeclared_identifiers(),
)
if buffered:
s = (
"context.get('local')."
"cache._ctx_get_or_create("
"%s, lambda:__M_%s(%s), context, %s__M_defname=%r)"
% (
cachekey,
name,
",".join(pass_args),
"".join(
["%s=%s, " % (k, v) for k, v in cache_args.items()]
),
name,
)
)
s = self.create_filter_callable(
self.compiler.buffer_filters, s, False
)
self.printer.writelines("return " + s, None)
else:
self.printer.writelines(
"__M_writer(context.get('local')."
"cache._ctx_get_or_create("
"%s, lambda:__M_%s(%s), context, %s__M_defname=%r))"
% (
cachekey,
name,
",".join(pass_args),
"".join(
["%s=%s, " % (k, v) for k, v in cache_args.items()]
),
name,
),
"return ''",
None,
)
def create_filter_callable(self, args, target, is_expression):
def locate_encode(name):
if re.match(r"decode\..+", name):
return "filters." + name
else:
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 and "n" not in args:
args = self.compiler.default_filters + args
for e in args:
if e == "n":
continue
m = re.match(r"(.+?)(\(.*\))", e)
if m:
ident, fargs = m.group(1, 2)
f = locate_encode(ident)
e = f + fargs
else:
e = locate_encode(e)
assert e is not None
target = "%s(%s)" % (e, target)
return target
def visitExpression(self, node):
self.printer.start_source(node.lineno)
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("__M_writer(%s)" % s)
else:
self.printer.writeline("__M_writer(%s)" % node.text)
def visitControlLine(self, node):
if node.isend:
self.printer.writeline(None)
if node.has_loop_context:
self.printer.writeline("finally:")
self.printer.writeline("loop = __M_loop._exit()")
self.printer.writeline(None)
else:
self.printer.start_source(node.lineno)
if self.compiler.enable_loop and node.keyword == "for":
text = mangle_mako_loop(node, self.printer)
else:
text = node.text
self.printer.writeline(text)
children = node.get_children()
if not children or (
all(
isinstance(c, (parsetree.Comment, parsetree.ControlLine))
for c in children
)
and all(
(node.is_ternary(c.keyword) or c.isend)
for c in children
if isinstance(c, parsetree.ControlLine)
)
):
self.printer.writeline("pass")
def visitText(self, node):
self.printer.start_source(node.lineno)
self.printer.writeline("__M_writer(%s)" % repr(node.content))
def visitTextTag(self, node):
filtered = len(node.filter_args.args) > 0
if filtered:
self.printer.writelines(
"__M_writer = context._push_writer()", "try:"
)
for n in node.nodes:
n.accept_visitor(self)
if filtered:
self.printer.writelines(
"finally:",
"__M_buf, __M_writer = context._pop_buffer_and_writer()",
"__M_writer(%s)"
% self.create_filter_callable(
node.filter_args.args, "__M_buf.getvalue()", False
),
None,
)
def visitCode(self, node):
if not node.ismodule:
self.printer.write_indented_block(
node.text, starting_lineno=node.lineno
)
if not self.in_def and len(self.identifiers.locally_assigned) > 0:
self.printer.writeline(
"__M_locals_builtin_stored = __M_locals_builtin()"
)
self.printer.writeline(
"__M_locals.update(__M_dict_builtin([(__M_key,"
" __M_locals_builtin_stored[__M_key]) for __M_key in"
" [%s] if __M_key in __M_locals_builtin_stored]))"
% ",".join([repr(x) for x in node.declared_identifiers()])
)
def visitIncludeTag(self, node):
self.printer.start_source(node.lineno)
args = node.attributes.get("args")
if args:
self.printer.writeline(
"runtime._include_file(context, %s, _template_uri, %s)"
% (node.parsed_attributes["file"], args)
)
else:
self.printer.writeline(
"runtime._include_file(context, %s, _template_uri)"
% (node.parsed_attributes["file"])
)
def visitNamespaceTag(self, node):
pass
def visitDefTag(self, node):
pass
def visitBlockTag(self, node):
if node.is_anonymous:
self.printer.writeline("%s()" % node.funcname)
else:
nameargs = node.get_argument_expressions(as_call=True)
nameargs += ["**pageargs"]
self.printer.writeline(
"if 'parent' not in context._data or "
"not hasattr(context._data['parent'], '%s'):" % node.funcname
)
self.printer.writeline(
"context['self'].%s(%s)" % (node.funcname, ",".join(nameargs))
)
self.printer.writeline("\n")
def visitCallNamespaceTag(self, node):
self.visitCallTag(node)
def visitCallTag(self, node):
self.printer.writeline("def ccall(caller):")
export = ["body"]
callable_identifiers = self.identifiers.branch(node, nested=True)
body_identifiers = callable_identifiers.branch(node, nested=False)
body_identifiers.add_declared("caller")
self.identifier_stack.append(body_identifiers)
class DefVisitor:
def visitDefTag(s, node):
s.visitDefOrBase(node)
def visitBlockTag(s, node):
s.visitDefOrBase(node)
def visitDefOrBase(s, node):
self.write_inline_def(node, callable_identifiers, nested=False)
if not node.is_anonymous:
export.append(node.funcname)
if node.funcname in body_identifiers.closuredefs:
del body_identifiers.closuredefs[node.funcname]
vis = DefVisitor()
for n in node.nodes:
n.accept_visitor(vis)
self.identifier_stack.pop()
bodyargs = node.body_decl.get_argument_expressions()
self.printer.writeline("def body(%s):" % ",".join(bodyargs))
buffered = False
if buffered:
self.printer.writelines("context._push_buffer()", "try:")
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, "return [%s]" % (",".join(export)), None)
self.printer.writelines(
"context.caller_stack.nextcaller = "
"runtime.Namespace('caller', context, "
"callables=ccall(__M_caller))",
"try:",
)
self.printer.start_source(node.lineno)
self.printer.writelines(
"__M_writer(%s)"
% self.create_filter_callable([], node.expression, True),
"finally:",
"context.caller_stack.nextcaller = None",
None,
)
class _Identifiers:
def __init__(self, compiler, node=None, parent=None, nested=False):
if parent is not None:
if isinstance(node, parsetree.NamespaceTag):
self.declared = set()
self.topleveldefs = util.SetLikeDict()
else:
self.declared = (
set(parent.declared)
.union([c.name for c in parent.closuredefs.values()])
.union(parent.locally_declared)
.union(parent.argument_declared)
)
if nested:
self.declared = self.declared.union(parent.undeclared)
self.topleveldefs = util.SetLikeDict(**parent.topleveldefs)
else:
self.declared = set()
self.topleveldefs = util.SetLikeDict()
self.compiler = compiler
self.undeclared = set()
self.locally_declared = set()
self.locally_assigned = set()
self.argument_declared = set()
self.closuredefs = util.SetLikeDict()
self.node = node
if node is not None:
node.accept_visitor(self)
illegal_names = self.compiler.reserved_names.intersection(
self.locally_declared
)
if illegal_names:
raise exceptions.NameConflictError(
"Reserved words declared in template: %s"
% ", ".join(illegal_names)
)
def branch(self, node, **kwargs):
return _Identifiers(self.compiler, 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)"
% (
list(self.declared),
list(self.locally_declared),
list(self.undeclared),
[c.name for c in self.topleveldefs.values()],
[c.name for c in self.closuredefs.values()],
self.argument_declared,
)
)
def check_declared(self, 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):
if self.node is node:
for n in node.nodes:
n.accept_visitor(self)
def _check_name_exists(self, collection, node):
existing = collection.get(node.funcname)
collection[node.funcname] = node
if (
existing is not None
and existing is not node
and (node.is_block or existing.is_block)
):
raise exceptions.CompileException(
"%%def or %%block named '%s' already "
"exists in this template." % node.funcname,
**node.exception_kwargs,
)
def visitDefTag(self, node):
if node.is_root() and not node.is_anonymous:
self._check_name_exists(self.topleveldefs, node)
elif node is not self.node:
self._check_name_exists(self.closuredefs, node)
for ident in node.undeclared_identifiers():
if ident != "context" and ident not in self.declared.union(
self.locally_declared
):
self.undeclared.add(ident)
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 visitBlockTag(self, node):
if node is not self.node and not node.is_anonymous:
if isinstance(self.node, parsetree.DefTag):
raise exceptions.CompileException(
"Named block '%s' not allowed inside of def '%s'"
% (node.name, self.node.name),
**node.exception_kwargs,
)
elif isinstance(
self.node, (parsetree.CallTag, parsetree.CallNamespaceTag)
):
raise exceptions.CompileException(
"Named block '%s' not allowed inside of <%%call> tag"
% (node.name,),
**node.exception_kwargs,
)
for ident in node.undeclared_identifiers():
if ident != "context" and ident not in self.declared.union(
self.locally_declared
):
self.undeclared.add(ident)
if not node.is_anonymous:
self._check_name_exists(self.topleveldefs, node)
self.undeclared.add(node.funcname)
elif node is not self.node:
self._check_name_exists(self.closuredefs, node)
for ident in node.declared_identifiers():
self.argument_declared.add(ident)
for n in node.nodes:
n.accept_visitor(self)
def visitTextTag(self, node):
for ident in node.undeclared_identifiers():
if ident != "context" and ident not in self.declared.union(
self.locally_declared
):
self.undeclared.add(ident)
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():
if ident != "context" and ident not in self.declared.union(
self.locally_declared
):
self.undeclared.add(ident)
for ident in node.declared_identifiers():
self.argument_declared.add(ident)
for n in node.nodes:
n.accept_visitor(self)
else:
for ident in node.undeclared_identifiers():
if ident != "context" and ident not in self.declared.union(
self.locally_declared
):
self.undeclared.add(ident)
_FOR_LOOP = re.compile(
r"^for\s+((?:\(?)\s*[A-Za-z_][A-Za-z_0-9]*"
r"(?:\s*,\s*(?:[A-Za-z_][A-Za-z0-9_]*),??)*\s*(?:\)?))\s+in\s+(.*):"
)
def mangle_mako_loop(node, printer):
loop_variable = LoopVariable()
node.accept_visitor(loop_variable)
if loop_variable.detected:
node.nodes[-1].has_loop_context = True
match = _FOR_LOOP.match(node.text)
if match:
printer.writelines(
"loop = __M_loop._enter(%s)" % match.group(2),
"try:"
)
text = "for %s in loop:" % match.group(1)
else:
raise SyntaxError("Couldn't apply loop context: %s" % node.text)
else:
text = node.text
return text
class LoopVariable:
def __init__(self):
self.detected = False
def _loop_reference_detected(self, node):
if "loop" in node.undeclared_identifiers():
self.detected = True
else:
for n in node.get_children():
n.accept_visitor(self)
def visitControlLine(self, node):
self._loop_reference_detected(node)
def visitCode(self, node):
self._loop_reference_detected(node)
def visitExpression(self, node):
self._loop_reference_detected(node)