2 # Copyright (C) 2006, 2007, 2008 Michael Bayer mike_mp@zzzcomputing.com
4 # This module is part of Mako and is released under
5 # the MIT License: http://www.opensource.org/licenses/mit-license.php
7 """provides functionality for rendering a parsetree constructing into module source code."""
11 from mako.pygen import PythonPrinter
12 from mako import util, ast, parsetree, filters
17 def compile(node, uri, filename=None, default_filters=None, buffer_filters=None, imports=None, source_encoding=None, generate_unicode=True):
18 """generate module source code given a parsetree node, uri, and optional source filename"""
20 buf = util.FastEncodingBuffer(unicode=generate_unicode)
22 printer = PythonPrinter(buf)
23 _GenerateRenderMethod(printer, _CompileContext(uri, filename, default_filters, buffer_filters, imports, source_encoding, generate_unicode), node)
26 class _CompileContext(object):
27 def __init__(self, uri, filename, default_filters, buffer_filters, imports, source_encoding, generate_unicode):
29 self.filename = filename
30 self.default_filters = default_filters
31 self.buffer_filters = buffer_filters
32 self.imports = imports
33 self.source_encoding = source_encoding
34 self.generate_unicode = generate_unicode
36 class _GenerateRenderMethod(object):
37 """a template visitor object which generates the full module source for a template."""
38 def __init__(self, printer, compiler, node):
39 self.printer = printer
40 self.last_source_line = -1
41 self.compiler = compiler
43 self.identifier_stack = [None]
45 self.in_def = isinstance(node, parsetree.DefTag)
48 name = "render_" + node.name
49 args = node.function_decl.get_argument_expressions()
50 filtered = len(node.filter_args.args) > 0
51 buffered = eval(node.attributes.get('buffered', 'False'))
52 cached = eval(node.attributes.get('cached', 'False'))
56 defs = self.write_toplevel()
57 pagetag = self.compiler.pagetag
59 if pagetag is not None:
60 args = pagetag.body_decl.get_argument_expressions()
61 if not pagetag.body_decl.kwargs:
62 args += ['**pageargs']
63 cached = eval(pagetag.attributes.get('cached', 'False'))
67 buffered = filtered = False
71 args = [a for a in ['context'] + args]
73 self.write_render_callable(pagetag or node, name, args, buffered, filtered, cached)
77 _GenerateRenderMethod(printer, compiler, node)
79 identifiers = property(lambda self:self.identifier_stack[-1])
81 def write_toplevel(self):
82 """traverse a template structure for module-level directives and generate the
83 start of module-level code."""
89 self.compiler.pagetag = None
91 class FindTopLevel(object):
92 def visitInheritTag(s, node):
94 def visitNamespaceTag(s, node):
95 namespaces[node.name] = node
96 def visitPageTag(s, node):
97 self.compiler.pagetag = node
98 def visitCode(s, node):
100 module_code.append(node)
103 for n in self.node.nodes:
106 self.compiler.namespaces = namespaces
108 module_ident = util.Set()
109 for n in module_code:
110 module_ident = module_ident.union(n.declared_identifiers())
112 module_identifiers = _Identifiers()
113 module_identifiers.declared = module_ident
115 # module-level names, python code
116 if not self.compiler.generate_unicode and self.compiler.source_encoding:
117 self.printer.writeline("# -*- encoding:%s -*-" % self.compiler.source_encoding)
119 self.printer.writeline("from mako import runtime, filters, cache")
120 self.printer.writeline("UNDEFINED = runtime.UNDEFINED")
121 self.printer.writeline("__M_dict_builtin = dict")
122 self.printer.writeline("__M_locals_builtin = locals")
123 self.printer.writeline("_magic_number = %s" % repr(MAGIC_NUMBER))
124 self.printer.writeline("_modified_time = %s" % repr(time.time()))
125 self.printer.writeline("_template_filename=%s" % repr(self.compiler.filename))
126 self.printer.writeline("_template_uri=%s" % repr(self.compiler.uri))
127 self.printer.writeline("_template_cache=cache.Cache(__name__, _modified_time)")
128 self.printer.writeline("_source_encoding=%s" % repr(self.compiler.source_encoding))
129 if self.compiler.imports:
131 for imp in self.compiler.imports:
133 self.printer.writeline(imp)
134 impcode = ast.PythonCode(buf, source='', lineno=0, pos=0, filename='template defined imports')
138 main_identifiers = module_identifiers.branch(self.node)
139 module_identifiers.topleveldefs = module_identifiers.topleveldefs.union(main_identifiers.topleveldefs)
140 [module_identifiers.declared.add(x) for x in ["UNDEFINED"]]
142 [module_identifiers.declared.add(x) for x in impcode.declared_identifiers]
144 self.compiler.identifiers = module_identifiers
145 self.printer.writeline("_exports = %s" % repr([n.name for n in main_identifiers.topleveldefs.values()]))
146 self.printer.write("\n\n")
149 self.write_module_code(module_code)
152 self.write_namespaces(namespaces)
153 self.write_inherit(inherit[-1])
154 elif len(namespaces):
155 self.write_namespaces(namespaces)
157 return main_identifiers.topleveldefs.values()
159 def write_render_callable(self, node, name, args, buffered, filtered, cached):
160 """write a top-level render callable.
162 this could be the main render() method or that of a top-level def."""
163 self.printer.writelines(
164 "def %s(%s):" % (name, ','.join(args)),
165 "context.caller_stack._push_frame()",
168 if buffered or filtered or cached:
169 self.printer.writeline("context._push_buffer()")
171 self.identifier_stack.append(self.compiler.identifiers.branch(self.node))
172 if not self.in_def and '**pageargs' in args:
173 self.identifier_stack[-1].argument_declared.add('pageargs')
175 if not self.in_def and (len(self.identifiers.locally_assigned) > 0 or len(self.identifiers.argument_declared)>0):
176 self.printer.writeline("__M_locals = __M_dict_builtin(%s)" % ','.join(["%s=%s" % (x, x) for x in self.identifiers.argument_declared]))
178 self.write_variable_declares(self.identifiers, toplevel=True)
180 for n in self.node.nodes:
181 n.accept_visitor(self)
183 self.write_def_finish(self.node, buffered, filtered, cached)
184 self.printer.writeline(None)
185 self.printer.write("\n\n")
187 self.write_cache_decorator(node, name, args, buffered, self.identifiers, toplevel=True)
189 def write_module_code(self, module_code):
190 """write module-level template code, i.e. that which is enclosed in <%! %> tags
192 for n in module_code:
193 self.write_source_comment(n)
194 self.printer.write_indented_block(n.text)
196 def write_inherit(self, node):
197 """write the module-level inheritance-determination callable."""
198 self.printer.writelines(
199 "def _mako_inherit(template, context):",
200 "_mako_generate_namespaces(context)",
201 "return runtime._inherit_from(context, %s, _template_uri)" % (node.parsed_attributes['file']),
205 def write_namespaces(self, namespaces):
206 """write the module-level namespace-generating callable."""
207 self.printer.writelines(
208 "def _mako_get_namespace(context, name):",
210 "return context.namespaces[(__name__, name)]",
212 "_mako_generate_namespaces(context)",
213 "return context.namespaces[(__name__, name)]",
216 self.printer.writeline("def _mako_generate_namespaces(context):")
217 for node in namespaces.values():
218 if node.attributes.has_key('import'):
219 self.compiler.has_ns_imports = True
220 self.write_source_comment(node)
222 self.printer.writeline("def make_namespace():")
224 identifiers = self.compiler.identifiers.branch(node)
225 class NSDefVisitor(object):
226 def visitDefTag(s, node):
227 self.write_inline_def(node, identifiers, nested=False)
228 export.append(node.name)
231 n.accept_visitor(vis)
232 self.printer.writeline("return [%s]" % (','.join(export)))
233 self.printer.writeline(None)
234 callable_name = "make_namespace()"
236 callable_name = "None"
237 self.printer.writeline("ns = runtime.Namespace(%s, context._clean_inheritance_tokens(), templateuri=%s, callables=%s, calling_uri=_template_uri, module=%s)" % (repr(node.name), node.parsed_attributes.get('file', 'None'), callable_name, node.parsed_attributes.get('module', 'None')))
238 if eval(node.attributes.get('inheritable', "False")):
239 self.printer.writeline("context['self'].%s = ns" % (node.name))
240 self.printer.writeline("context.namespaces[(__name__, %s)] = ns" % repr(node.name))
241 self.printer.write("\n")
242 if not len(namespaces):
243 self.printer.writeline("pass")
244 self.printer.writeline(None)
246 def write_variable_declares(self, identifiers, toplevel=False, limit=None):
247 """write variable declarations at the top of a function.
249 the variable declarations are in the form of callable definitions for defs and/or
250 name lookup within the function's context argument. the names declared are based on the
251 names that are referenced in the function body, which don't otherwise have any explicit
252 assignment operation. names that are assigned within the body are assumed to be
253 locally-scoped variables and are not separately declared.
255 for def callable definitions, if the def is a top-level callable then a
256 'stub' callable is generated which wraps the current Context into a closure. if the def
257 is not top-level, it is fully rendered as a local closure."""
259 # collection of all defs available to us in this scope
260 comp_idents = dict([(c.name, c) for c in identifiers.defs])
261 to_write = util.Set()
263 # write "context.get()" for all variables we are going to need that arent in the namespace yet
264 to_write = to_write.union(identifiers.undeclared)
266 # write closure functions for closures that we define right here
267 to_write = to_write.union(util.Set([c.name for c in identifiers.closuredefs.values()]))
269 # remove identifiers that are declared in the argument signature of the callable
270 to_write = to_write.difference(identifiers.argument_declared)
272 # remove identifiers that we are going to assign to. in this way we mimic Python's behavior,
273 # i.e. assignment to a variable within a block means that variable is now a "locally declared" var,
274 # which cannot be referenced beforehand.
275 to_write = to_write.difference(identifiers.locally_declared)
277 # if a limiting set was sent, constraint to those items in that list
278 # (this is used for the caching decorator)
279 if limit is not None:
280 to_write = to_write.intersection(limit)
282 if toplevel and getattr(self.compiler, 'has_ns_imports', False):
283 self.printer.writeline("_import_ns = {}")
284 self.compiler.has_imports = True
285 for ident, ns in self.compiler.namespaces.iteritems():
286 if ns.attributes.has_key('import'):
287 self.printer.writeline("_mako_get_namespace(context, %s)._populate(_import_ns, %s)" % (repr(ident), repr(re.split(r'\s*,\s*', ns.attributes['import']))))
289 for ident in to_write:
290 if ident in comp_idents:
291 comp = comp_idents[ident]
293 self.write_def_decl(comp, identifiers)
295 self.write_inline_def(comp, identifiers, nested=True)
296 elif ident in self.compiler.namespaces:
297 self.printer.writeline("%s = _mako_get_namespace(context, %s)" % (ident, repr(ident)))
299 if getattr(self.compiler, 'has_ns_imports', False):
300 self.printer.writeline("%s = _import_ns.get(%s, context.get(%s, UNDEFINED))" % (ident, repr(ident), repr(ident)))
302 self.printer.writeline("%s = context.get(%s, UNDEFINED)" % (ident, repr(ident)))
304 self.printer.writeline("__M_writer = context.writer()")
306 def write_source_comment(self, node):
307 """write a source comment containing the line number of the corresponding template line."""
308 if self.last_source_line != node.lineno:
309 self.printer.writeline("# SOURCE LINE %d" % node.lineno)
310 self.last_source_line = node.lineno
312 def write_def_decl(self, node, identifiers):
313 """write a locally-available callable referencing a top-level def"""
314 funcname = node.function_decl.funcname
315 namedecls = node.function_decl.get_argument_expressions()
316 nameargs = node.function_decl.get_argument_expressions(include_defaults=False)
317 if not self.in_def and (len(self.identifiers.locally_assigned) > 0 or len(self.identifiers.argument_declared) > 0):
318 nameargs.insert(0, 'context.locals_(__M_locals)')
320 nameargs.insert(0, 'context')
321 self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls)))
322 self.printer.writeline("return render_%s(%s)" % (funcname, ",".join(nameargs)))
323 self.printer.writeline(None)
325 def write_inline_def(self, node, identifiers, nested):
326 """write a locally-available def callable inside an enclosing def."""
327 namedecls = node.function_decl.get_argument_expressions()
328 self.printer.writeline("def %s(%s):" % (node.name, ",".join(namedecls)))
329 filtered = len(node.filter_args.args) > 0
330 buffered = eval(node.attributes.get('buffered', 'False'))
331 cached = eval(node.attributes.get('cached', 'False'))
332 self.printer.writelines(
333 "context.caller_stack._push_frame()",
336 if buffered or filtered or cached:
337 self.printer.writelines(
338 "context._push_buffer()",
341 identifiers = identifiers.branch(node, nested=nested)
343 self.write_variable_declares(identifiers)
345 self.identifier_stack.append(identifiers)
347 n.accept_visitor(self)
348 self.identifier_stack.pop()
350 self.write_def_finish(node, buffered, filtered, cached)
351 self.printer.writeline(None)
353 self.write_cache_decorator(node, node.name, namedecls, False, identifiers, inline=True, toplevel=False)
355 def write_def_finish(self, node, buffered, filtered, cached, callstack=True):
356 """write the end section of a rendering function, either outermost or inline.
358 this takes into account if the rendering function was filtered, buffered, etc.
359 and closes the corresponding try: block if any, and writes code to retrieve captured content,
360 apply filters, send proper return value."""
361 if not buffered and not cached and not filtered:
362 self.printer.writeline("return ''")
364 self.printer.writelines(
366 "context.caller_stack._pop_frame()",
370 if buffered or filtered or cached:
371 if buffered or cached:
372 # in a caching scenario, don't try to get a writer
373 # from the context after popping; assume the caching
374 # implemenation might be using a context with no
376 self.printer.writelines(
378 "__M_buf = context._pop_buffer()"
381 self.printer.writelines(
383 "__M_buf, __M_writer = context._pop_buffer_and_writer()"
387 self.printer.writeline("context.caller_stack._pop_frame()")
389 s = "__M_buf.getvalue()"
391 s = self.create_filter_callable(node.filter_args.args, s, False)
392 self.printer.writeline(None)
393 if buffered and not cached:
394 s = self.create_filter_callable(self.compiler.buffer_filters, s, False)
395 if buffered or cached:
396 self.printer.writeline("return %s" % s)
398 self.printer.writelines(
399 "__M_writer(%s)" % s,
403 def write_cache_decorator(self, node_or_pagetag, name, args, buffered, identifiers, inline=False, toplevel=False):
404 """write a post-function decorator to replace a rendering callable with a cached version of itself."""
405 self.printer.writeline("__M_%s = %s" % (name, name))
406 cachekey = node_or_pagetag.parsed_attributes.get('cache_key', repr(name))
408 for arg in (('cache_type', 'type'), ('cache_dir', 'data_dir'), ('cache_timeout', 'expiretime'), ('cache_url', 'url')):
409 val = node_or_pagetag.parsed_attributes.get(arg[0], None)
411 if arg[1] == 'expiretime':
412 cacheargs[arg[1]] = int(eval(val))
414 cacheargs[arg[1]] = val
416 if self.compiler.pagetag is not None:
417 val = self.compiler.pagetag.parsed_attributes.get(arg[0], None)
419 if arg[1] == 'expiretime':
420 cacheargs[arg[1]] == int(eval(val))
422 cacheargs[arg[1]] = val
424 self.printer.writeline("def %s(%s):" % (name, ','.join(args)))
426 # form "arg1, arg2, arg3=arg3, arg4=arg4", etc.
427 pass_args = [ '=' in a and "%s=%s" % ((a.split('=')[0],)*2) or a for a in args]
429 self.write_variable_declares(identifiers, toplevel=toplevel, limit=node_or_pagetag.undeclared_identifiers())
431 s = "context.get('local').get_cached(%s, defname=%r, %screatefunc=lambda:__M_%s(%s))" % (cachekey, name, ''.join(["%s=%s, " % (k,v) for k, v in cacheargs.iteritems()]), name, ','.join(pass_args))
432 # apply buffer_filters
433 s = self.create_filter_callable(self.compiler.buffer_filters, s, False)
434 self.printer.writelines("return " + s,None)
436 self.printer.writelines(
437 "__M_writer(context.get('local').get_cached(%s, defname=%r, %screatefunc=lambda:__M_%s(%s)))" % (cachekey, name, ''.join(["%s=%s, " % (k,v) for k, v in cacheargs.iteritems()]), name, ','.join(pass_args)),
442 def create_filter_callable(self, args, target, is_expression):
443 """write a filter-applying expression based on the filters present in the given
444 filter names, adjusting for the global 'default' filter aliases as needed."""
445 def locate_encode(name):
446 if re.match(r'decode\..+', name):
447 return "filters." + name
449 return filters.DEFAULT_ESCAPES.get(name, name)
453 if self.compiler.pagetag:
454 args = self.compiler.pagetag.filter_args.args + args
455 if self.compiler.default_filters:
456 args = self.compiler.default_filters + args
458 # if filter given as a function, get just the identifier portion
461 m = re.match(r'(.+?)(\(.*\))', e)
463 (ident, fargs) = m.group(1,2)
464 f = locate_encode(ident)
470 target = "%s(%s)" % (e, target)
473 def visitExpression(self, node):
474 self.write_source_comment(node)
475 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):
476 s = self.create_filter_callable(node.escapes_code.args, "%s" % node.text, True)
477 self.printer.writeline("__M_writer(%s)" % s)
479 self.printer.writeline("__M_writer(%s)" % node.text)
481 def visitControlLine(self, node):
483 self.printer.writeline(None)
485 self.write_source_comment(node)
486 self.printer.writeline(node.text)
487 def visitText(self, node):
488 self.write_source_comment(node)
489 self.printer.writeline("__M_writer(%s)" % repr(node.content))
490 def visitTextTag(self, node):
491 filtered = len(node.filter_args.args) > 0
493 self.printer.writelines(
494 "__M_writer = context._push_writer()",
498 n.accept_visitor(self)
500 self.printer.writelines(
502 "__M_buf, __M_writer = context._pop_buffer_and_writer()",
503 "__M_writer(%s)" % self.create_filter_callable(node.filter_args.args, "__M_buf.getvalue()", False),
507 def visitCode(self, node):
508 if not node.ismodule:
509 self.write_source_comment(node)
510 self.printer.write_indented_block(node.text)
512 if not self.in_def and len(self.identifiers.locally_assigned) > 0:
513 # if we are the "template" def, fudge locally declared/modified variables into the "__M_locals" dictionary,
514 # which is used for def calls within the same template, to simulate "enclosing scope"
515 self.printer.writeline('__M_locals.update(__M_dict_builtin([(__M_key, __M_locals_builtin()[__M_key]) for __M_key in [%s] if __M_key in __M_locals_builtin()]))' % ','.join([repr(x) for x in node.declared_identifiers()]))
517 def visitIncludeTag(self, node):
518 self.write_source_comment(node)
519 args = node.attributes.get('args')
521 self.printer.writeline("runtime._include_file(context, %s, _template_uri, %s)" % (node.parsed_attributes['file'], args))
523 self.printer.writeline("runtime._include_file(context, %s, _template_uri)" % (node.parsed_attributes['file']))
525 def visitNamespaceTag(self, node):
528 def visitDefTag(self, node):
531 def visitCallNamespaceTag(self, node):
532 # TODO: we can put namespace-specific checks here, such
533 # as ensure the given namespace will be imported,
534 # pre-import the namespace, etc.
535 self.visitCallTag(node)
537 def visitCallTag(self, node):
538 self.printer.writeline("def ccall(caller):")
540 callable_identifiers = self.identifiers.branch(node, nested=True)
541 body_identifiers = callable_identifiers.branch(node, nested=False)
542 # we want the 'caller' passed to ccall to be used for the body() function,
543 # but for other non-body() <%def>s within <%call> we want the current caller off the call stack (if any)
544 body_identifiers.add_declared('caller')
546 self.identifier_stack.append(body_identifiers)
547 class DefVisitor(object):
548 def visitDefTag(s, node):
549 self.write_inline_def(node, callable_identifiers, nested=False)
550 export.append(node.name)
551 # remove defs that are within the <%call> from the "closuredefs" defined
552 # in the body, so they dont render twice
553 if node.name in body_identifiers.closuredefs:
554 del body_identifiers.closuredefs[node.name]
558 n.accept_visitor(vis)
559 self.identifier_stack.pop()
561 bodyargs = node.body_decl.get_argument_expressions()
562 self.printer.writeline("def body(%s):" % ','.join(bodyargs))
563 # TODO: figure out best way to specify buffering/nonbuffering (at call time would be better)
566 self.printer.writelines(
567 "context._push_buffer()",
570 self.write_variable_declares(body_identifiers)
571 self.identifier_stack.append(body_identifiers)
574 n.accept_visitor(self)
575 self.identifier_stack.pop()
577 self.write_def_finish(node, buffered, False, False, callstack=False)
578 self.printer.writelines(
580 "return [%s]" % (','.join(export)),
584 self.printer.writelines(
585 # get local reference to current caller, if any
586 "caller = context.caller_stack._get_caller()",
587 # push on caller for nested call
588 "context.caller_stack.nextcaller = runtime.Namespace('caller', context, callables=ccall(caller))",
590 self.write_source_comment(node)
591 self.printer.writelines(
592 "__M_writer(%s)" % self.create_filter_callable([], node.expression, True),
594 "context.caller_stack.nextcaller = None",
598 class _Identifiers(object):
599 """tracks the status of identifier names as template code is rendered."""
600 def __init__(self, node=None, parent=None, nested=False):
601 if parent is not None:
602 # things that have already been declared in an enclosing namespace (i.e. names we can just use)
603 self.declared = util.Set(parent.declared).union([c.name for c in parent.closuredefs.values()]).union(parent.locally_declared).union(parent.argument_declared)
605 # if these identifiers correspond to a "nested" scope, it means whatever the
606 # parent identifiers had as undeclared will have been declared by that parent,
607 # and therefore we have them in our scope.
609 self.declared = self.declared.union(parent.undeclared)
611 # top level defs that are available
612 self.topleveldefs = util.SetLikeDict(**parent.topleveldefs)
614 self.declared = util.Set()
615 self.topleveldefs = util.SetLikeDict()
617 # things within this level that are referenced before they are declared (e.g. assigned to)
618 self.undeclared = util.Set()
620 # things that are declared locally. some of these things could be in the "undeclared"
621 # list as well if they are referenced before declared
622 self.locally_declared = util.Set()
624 # assignments made in explicit python blocks. these will be propigated to
625 # the context of local def calls.
626 self.locally_assigned = util.Set()
628 # things that are declared in the argument signature of the def callable
629 self.argument_declared = util.Set()
631 # closure defs that are defined in this level
632 self.closuredefs = util.SetLikeDict()
637 node.accept_visitor(self)
639 def branch(self, node, **kwargs):
640 """create a new Identifiers for a new Node, with this Identifiers as the parent."""
641 return _Identifiers(node, self, **kwargs)
643 defs = property(lambda self:util.Set(self.topleveldefs.union(self.closuredefs).values()))
646 return "Identifiers(declared=%s, locally_declared=%s, undeclared=%s, topleveldefs=%s, closuredefs=%s, argumenetdeclared=%s)" % (repr(list(self.declared)), repr(list(self.locally_declared)), repr(list(self.undeclared)), repr([c.name for c in self.topleveldefs.values()]), repr([c.name for c in self.closuredefs.values()]), repr(self.argument_declared))
648 def check_declared(self, node):
649 """update the state of this Identifiers with the undeclared and declared identifiers of the given node."""
650 for ident in node.undeclared_identifiers():
651 if ident != 'context' and ident not in self.declared.union(self.locally_declared):
652 self.undeclared.add(ident)
653 for ident in node.declared_identifiers():
654 self.locally_declared.add(ident)
656 def add_declared(self, ident):
657 self.declared.add(ident)
658 if ident in self.undeclared:
659 self.undeclared.remove(ident)
661 def visitExpression(self, node):
662 self.check_declared(node)
663 def visitControlLine(self, node):
664 self.check_declared(node)
665 def visitCode(self, node):
666 if not node.ismodule:
667 self.check_declared(node)
668 self.locally_assigned = self.locally_assigned.union(node.declared_identifiers())
669 def visitDefTag(self, node):
671 self.topleveldefs[node.name] = node
672 elif node is not self.node:
673 self.closuredefs[node.name] = node
674 for ident in node.undeclared_identifiers():
675 if ident != 'context' and ident not in self.declared.union(self.locally_declared):
676 self.undeclared.add(ident)
677 # visit defs only one level deep
678 if node is self.node:
679 for ident in node.declared_identifiers():
680 self.argument_declared.add(ident)
682 n.accept_visitor(self)
683 def visitIncludeTag(self, node):
684 self.check_declared(node)
685 def visitPageTag(self, node):
686 for ident in node.declared_identifiers():
687 self.argument_declared.add(ident)
688 self.check_declared(node)
690 def visitCallNamespaceTag(self, node):
691 self.visitCallTag(node)
693 def visitCallTag(self, node):
694 if node is self.node:
695 for ident in node.undeclared_identifiers():
696 if ident != 'context' and ident not in self.declared.union(self.locally_declared):
697 self.undeclared.add(ident)
698 for ident in node.declared_identifiers():
699 self.argument_declared.add(ident)
701 n.accept_visitor(self)
703 for ident in node.undeclared_identifiers():
704 if ident != 'context' and ident not in self.declared.union(self.locally_declared):
705 self.undeclared.add(ident)