root/galaxy-central/eggs/Mako-0.2.5-py2.6.egg/mako/codegen.py @ 3

リビジョン 3, 32.4 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1# codegen.py
2# Copyright (C) 2006, 2007, 2008, 2009 Michael Bayer mike_mp@zzzcomputing.com
3#
4# This module is part of Mako and is released under
5# the MIT License: http://www.opensource.org/licenses/mit-license.php
6
7"""provides functionality for rendering a parsetree constructing into module source code."""
8
9import time
10import re
11from mako.pygen import PythonPrinter
12from mako import util, ast, parsetree, filters
13
14MAGIC_NUMBER = 5
15
16
17def 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"""
19
20    buf = util.FastEncodingBuffer(unicode=generate_unicode)
21
22    printer = PythonPrinter(buf)
23    _GenerateRenderMethod(printer, _CompileContext(uri, filename, default_filters, buffer_filters, imports, source_encoding, generate_unicode), node)
24    return buf.getvalue()
25
26class _CompileContext(object):
27    def __init__(self, uri, filename, default_filters, buffer_filters, imports, source_encoding, generate_unicode):
28        self.uri = uri
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
35       
36class _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
42        self.node = node
43        self.identifier_stack = [None]
44       
45        self.in_def = isinstance(node, parsetree.DefTag)
46
47        if self.in_def:
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'))
53            defs = None
54            pagetag = None
55        else:
56            defs = self.write_toplevel()
57            pagetag = self.compiler.pagetag
58            name = "render_body"
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'))
64            else:
65                args = ['**pageargs']
66                cached = False
67            buffered = filtered = False
68        if args is None:
69            args = ['context']
70        else:
71            args = [a for a in ['context'] + args]
72           
73        self.write_render_callable(pagetag or node, name, args, buffered, filtered, cached)
74       
75        if defs is not None:
76            for node in defs:
77                _GenerateRenderMethod(printer, compiler, node)
78   
79    identifiers = property(lambda self:self.identifier_stack[-1])
80   
81    def write_toplevel(self):
82        """traverse a template structure for module-level directives and generate the
83        start of module-level code."""
84        inherit = []
85        namespaces = {}
86        module_code = []
87        encoding =[None]
88
89        self.compiler.pagetag = None
90       
91        class FindTopLevel(object):
92            def visitInheritTag(s, node):
93                inherit.append(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):
99                if node.ismodule:
100                    module_code.append(node)
101           
102        f = FindTopLevel()
103        for n in self.node.nodes:
104            n.accept_visitor(f)
105
106        self.compiler.namespaces = namespaces
107
108        module_ident = util.Set()
109        for n in module_code:
110            module_ident = module_ident.union(n.declared_identifiers())
111
112        module_identifiers = _Identifiers()
113        module_identifiers.declared = module_ident
114       
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)
118           
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:
130            buf = ''
131            for imp in self.compiler.imports:
132                buf += imp + "\n"
133                self.printer.writeline(imp)
134            impcode = ast.PythonCode(buf, source='', lineno=0, pos=0, filename='template defined imports')
135        else:
136            impcode = None
137       
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"]]
141        if impcode:
142            [module_identifiers.declared.add(x) for x in impcode.declared_identifiers]
143           
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")
147
148        if len(module_code):
149            self.write_module_code(module_code)
150
151        if len(inherit):
152            self.write_namespaces(namespaces)
153            self.write_inherit(inherit[-1])
154        elif len(namespaces):
155            self.write_namespaces(namespaces)
156
157        return main_identifiers.topleveldefs.values()
158
159    def write_render_callable(self, node, name, args, buffered, filtered, cached):
160        """write a top-level render callable.
161       
162        this could be the main render() method or that of a top-level def."""
163       
164        if self.in_def:
165            decorator = node.decorator
166            if decorator:
167                self.printer.writeline("@runtime._decorate_toplevel(%s)" % decorator)
168       
169        self.printer.writelines(
170            "def %s(%s):" % (name, ','.join(args)),
171                "context.caller_stack._push_frame()",
172                "try:"
173        )
174        if buffered or filtered or cached:
175            self.printer.writeline("context._push_buffer()")
176       
177        self.identifier_stack.append(self.compiler.identifiers.branch(self.node))
178        if not self.in_def and '**pageargs' in args:
179            self.identifier_stack[-1].argument_declared.add('pageargs')
180
181        if not self.in_def and (len(self.identifiers.locally_assigned) > 0 or len(self.identifiers.argument_declared)>0):
182            self.printer.writeline("__M_locals = __M_dict_builtin(%s)" % ','.join(["%s=%s" % (x, x) for x in self.identifiers.argument_declared]))
183
184        self.write_variable_declares(self.identifiers, toplevel=True)
185
186        for n in self.node.nodes:
187            n.accept_visitor(self)
188
189        self.write_def_finish(self.node, buffered, filtered, cached)
190        self.printer.writeline(None)
191        self.printer.write("\n\n")
192        if cached:
193            self.write_cache_decorator(node, name, args, buffered, self.identifiers, toplevel=True)
194           
195    def write_module_code(self, module_code):
196        """write module-level template code, i.e. that which is enclosed in <%! %> tags
197        in the template."""
198        for n in module_code:
199            self.write_source_comment(n)
200            self.printer.write_indented_block(n.text)
201
202    def write_inherit(self, node):
203        """write the module-level inheritance-determination callable."""
204        self.printer.writelines(
205            "def _mako_inherit(template, context):",
206                "_mako_generate_namespaces(context)",
207                "return runtime._inherit_from(context, %s, _template_uri)" % (node.parsed_attributes['file']),
208                None
209            )
210
211    def write_namespaces(self, namespaces):
212        """write the module-level namespace-generating callable."""
213        self.printer.writelines(
214            "def _mako_get_namespace(context, name):",
215                "try:",
216                    "return context.namespaces[(__name__, name)]",
217                "except KeyError:",
218                    "_mako_generate_namespaces(context)",
219                "return context.namespaces[(__name__, name)]",
220            None,None
221            )
222        self.printer.writeline("def _mako_generate_namespaces(context):")
223        for node in namespaces.values():
224            if node.attributes.has_key('import'):
225                self.compiler.has_ns_imports = True
226            self.write_source_comment(node)
227            if len(node.nodes):
228                self.printer.writeline("def make_namespace():")
229                export = []
230                identifiers = self.compiler.identifiers.branch(node)
231                class NSDefVisitor(object):
232                    def visitDefTag(s, node):
233                        self.write_inline_def(node, identifiers, nested=False)
234                        export.append(node.name)
235                vis = NSDefVisitor()
236                for n in node.nodes:
237                    n.accept_visitor(vis)
238                self.printer.writeline("return [%s]" % (','.join(export)))
239                self.printer.writeline(None)
240                callable_name = "make_namespace()"
241            else:
242                callable_name = "None"
243            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')))
244            if eval(node.attributes.get('inheritable', "False")):
245                self.printer.writeline("context['self'].%s = ns" % (node.name))
246            self.printer.writeline("context.namespaces[(__name__, %s)] = ns" % repr(node.name))
247            self.printer.write("\n")
248        if not len(namespaces):
249            self.printer.writeline("pass")
250        self.printer.writeline(None)
251           
252    def write_variable_declares(self, identifiers, toplevel=False, limit=None):
253        """write variable declarations at the top of a function.
254       
255        the variable declarations are in the form of callable definitions for defs and/or
256        name lookup within the function's context argument.  the names declared are based on the
257        names that are referenced in the function body, which don't otherwise have any explicit
258        assignment operation.  names that are assigned within the body are assumed to be
259        locally-scoped variables and are not separately declared.
260       
261        for def callable definitions, if the def is a top-level callable then a
262        'stub' callable is generated which wraps the current Context into a closure.  if the def
263        is not top-level, it is fully rendered as a local closure."""
264       
265        # collection of all defs available to us in this scope
266        comp_idents = dict([(c.name, c) for c in identifiers.defs])
267        to_write = util.Set()
268       
269        # write "context.get()" for all variables we are going to need that arent in the namespace yet
270        to_write = to_write.union(identifiers.undeclared)
271       
272        # write closure functions for closures that we define right here
273        to_write = to_write.union(util.Set([c.name for c in identifiers.closuredefs.values()]))
274
275        # remove identifiers that are declared in the argument signature of the callable
276        to_write = to_write.difference(identifiers.argument_declared)
277
278        # remove identifiers that we are going to assign to.  in this way we mimic Python's behavior,
279        # i.e. assignment to a variable within a block means that variable is now a "locally declared" var,
280        # which cannot be referenced beforehand. 
281        to_write = to_write.difference(identifiers.locally_declared)
282       
283        # if a limiting set was sent, constraint to those items in that list
284        # (this is used for the caching decorator)
285        if limit is not None:
286            to_write = to_write.intersection(limit)
287       
288        if toplevel and getattr(self.compiler, 'has_ns_imports', False):
289            self.printer.writeline("_import_ns = {}")
290            self.compiler.has_imports = True
291            for ident, ns in self.compiler.namespaces.iteritems():
292                if ns.attributes.has_key('import'):
293                    self.printer.writeline("_mako_get_namespace(context, %s)._populate(_import_ns, %s)" % (repr(ident),  repr(re.split(r'\s*,\s*', ns.attributes['import']))))
294                       
295        for ident in to_write:
296            if ident in comp_idents:
297                comp = comp_idents[ident]
298                if comp.is_root():
299                    self.write_def_decl(comp, identifiers)
300                else:
301                    self.write_inline_def(comp, identifiers, nested=True)
302            elif ident in self.compiler.namespaces:
303                self.printer.writeline("%s = _mako_get_namespace(context, %s)" % (ident, repr(ident)))
304            else:
305                if getattr(self.compiler, 'has_ns_imports', False):
306                    self.printer.writeline("%s = _import_ns.get(%s, context.get(%s, UNDEFINED))" % (ident, repr(ident), repr(ident)))
307                else:
308                    self.printer.writeline("%s = context.get(%s, UNDEFINED)" % (ident, repr(ident)))
309       
310        self.printer.writeline("__M_writer = context.writer()")
311       
312    def write_source_comment(self, node):
313        """write a source comment containing the line number of the corresponding template line."""
314        if self.last_source_line != node.lineno:
315            self.printer.writeline("# SOURCE LINE %d" % node.lineno)
316            self.last_source_line = node.lineno
317
318    def write_def_decl(self, node, identifiers):
319        """write a locally-available callable referencing a top-level def"""
320        funcname = node.function_decl.funcname
321        namedecls = node.function_decl.get_argument_expressions()
322        nameargs = node.function_decl.get_argument_expressions(include_defaults=False)
323        if not self.in_def and (len(self.identifiers.locally_assigned) > 0 or len(self.identifiers.argument_declared) > 0):
324            nameargs.insert(0, 'context.locals_(__M_locals)')
325        else:
326            nameargs.insert(0, 'context')
327        self.printer.writeline("def %s(%s):" % (funcname, ",".join(namedecls)))
328        self.printer.writeline("return render_%s(%s)" % (funcname, ",".join(nameargs)))
329        self.printer.writeline(None)
330       
331    def write_inline_def(self, node, identifiers, nested):
332        """write a locally-available def callable inside an enclosing def."""
333        namedecls = node.function_decl.get_argument_expressions()
334       
335        decorator = node.decorator
336        if decorator:
337            self.printer.writeline("@runtime._decorate_inline(context, %s)" % decorator)
338        self.printer.writeline("def %s(%s):" % (node.name, ",".join(namedecls)))
339        filtered = len(node.filter_args.args) > 0
340        buffered = eval(node.attributes.get('buffered', 'False'))
341        cached = eval(node.attributes.get('cached', 'False'))
342        self.printer.writelines(
343            "context.caller_stack._push_frame()",
344            "try:"
345            )
346        if buffered or filtered or cached:
347            self.printer.writelines(
348                "context._push_buffer()",
349                )
350
351        identifiers = identifiers.branch(node, nested=nested)
352
353        self.write_variable_declares(identifiers)
354       
355        self.identifier_stack.append(identifiers)
356        for n in node.nodes:
357            n.accept_visitor(self)
358        self.identifier_stack.pop()
359       
360        self.write_def_finish(node, buffered, filtered, cached)
361        self.printer.writeline(None)
362        if cached:
363            self.write_cache_decorator(node, node.name, namedecls, False, identifiers, inline=True, toplevel=False)
364           
365    def write_def_finish(self, node, buffered, filtered, cached, callstack=True):
366        """write the end section of a rendering function, either outermost or inline.
367       
368        this takes into account if the rendering function was filtered, buffered, etc.
369        and closes the corresponding try: block if any, and writes code to retrieve captured content,
370        apply filters, send proper return value."""
371        if not buffered and not cached and not filtered:
372            self.printer.writeline("return ''")
373            if callstack:
374                self.printer.writelines(
375                    "finally:",
376                        "context.caller_stack._pop_frame()",
377                    None
378                )
379               
380        if buffered or filtered or cached:
381            if buffered or cached:
382                # in a caching scenario, don't try to get a writer
383                # from the context after popping; assume the caching
384                # implemenation might be using a context with no
385                # extra buffers
386                self.printer.writelines(
387                    "finally:",
388                        "__M_buf = context._pop_buffer()"
389                )
390            else:
391                self.printer.writelines(
392                    "finally:",
393                        "__M_buf, __M_writer = context._pop_buffer_and_writer()"
394                )
395               
396            if callstack:
397                self.printer.writeline("context.caller_stack._pop_frame()")
398               
399            s = "__M_buf.getvalue()"
400            if filtered:
401                s = self.create_filter_callable(node.filter_args.args, s, False)
402            self.printer.writeline(None)
403            if buffered and not cached:
404                s = self.create_filter_callable(self.compiler.buffer_filters, s, False)
405            if buffered or cached:
406                self.printer.writeline("return %s" % s)
407            else:
408                self.printer.writelines(
409                    "__M_writer(%s)" % s,
410                    "return ''"
411                )
412
413    def write_cache_decorator(self, node_or_pagetag, name, args, buffered, identifiers, inline=False, toplevel=False):
414        """write a post-function decorator to replace a rendering callable with a cached version of itself."""
415        self.printer.writeline("__M_%s = %s" % (name, name))
416        cachekey = node_or_pagetag.parsed_attributes.get('cache_key', repr(name))
417        cacheargs = {}
418        for arg in (('cache_type', 'type'), ('cache_dir', 'data_dir'), ('cache_timeout', 'expiretime'), ('cache_url', 'url')):
419            val = node_or_pagetag.parsed_attributes.get(arg[0], None)
420            if val is not None:
421                if arg[1] == 'expiretime':
422                    cacheargs[arg[1]] = int(eval(val))
423                else:
424                    cacheargs[arg[1]] = val
425            else:
426                if self.compiler.pagetag is not None:
427                    val = self.compiler.pagetag.parsed_attributes.get(arg[0], None)
428                    if val is not None:
429                        if arg[1] == 'expiretime':
430                            cacheargs[arg[1]] == int(eval(val))
431                        else:
432                            cacheargs[arg[1]] = val
433       
434        self.printer.writeline("def %s(%s):" % (name, ','.join(args)))
435       
436        # form "arg1, arg2, arg3=arg3, arg4=arg4", etc.
437        pass_args = [ '=' in a and "%s=%s" % ((a.split('=')[0],)*2) or a for a in args]
438
439        self.write_variable_declares(identifiers, toplevel=toplevel, limit=node_or_pagetag.undeclared_identifiers())
440        if buffered:
441            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))
442            # apply buffer_filters
443            s = self.create_filter_callable(self.compiler.buffer_filters, s, False)
444            self.printer.writelines("return " + s,None)
445        else:
446            self.printer.writelines(
447                    "__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)),
448                    "return ''",
449                None
450            )
451
452    def create_filter_callable(self, args, target, is_expression):
453        """write a filter-applying expression based on the filters present in the given
454        filter names, adjusting for the global 'default' filter aliases as needed."""
455        def locate_encode(name):
456            if re.match(r'decode\..+', name):
457                return "filters." + name
458            else:
459                return filters.DEFAULT_ESCAPES.get(name, name)
460       
461        if 'n' not in args:
462            if is_expression:
463                if self.compiler.pagetag:
464                    args = self.compiler.pagetag.filter_args.args + args
465                if self.compiler.default_filters:
466                    args = self.compiler.default_filters + args
467        for e in args:
468            # if filter given as a function, get just the identifier portion
469            if e == 'n':
470                continue
471            m = re.match(r'(.+?)(\(.*\))', e)
472            if m:
473                (ident, fargs) = m.group(1,2)
474                f = locate_encode(ident)
475                e = f + fargs
476            else:
477                x = e
478                e = locate_encode(e)
479                assert e is not None
480            target = "%s(%s)" % (e, target)
481        return target
482       
483    def visitExpression(self, node):
484        self.write_source_comment(node)
485        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):
486            s = self.create_filter_callable(node.escapes_code.args, "%s" % node.text, True)
487            self.printer.writeline("__M_writer(%s)" % s)
488        else:
489            self.printer.writeline("__M_writer(%s)" % node.text)
490           
491    def visitControlLine(self, node):
492        if node.isend:
493            self.printer.writeline(None)
494        else:
495            self.write_source_comment(node)
496            self.printer.writeline(node.text)
497    def visitText(self, node):
498        self.write_source_comment(node)
499        self.printer.writeline("__M_writer(%s)" % repr(node.content))
500    def visitTextTag(self, node):
501        filtered = len(node.filter_args.args) > 0
502        if filtered:
503            self.printer.writelines(
504                "__M_writer = context._push_writer()",
505                "try:",
506            )
507        for n in node.nodes:
508            n.accept_visitor(self)
509        if filtered:
510            self.printer.writelines(
511                "finally:",
512                "__M_buf, __M_writer = context._pop_buffer_and_writer()",
513                "__M_writer(%s)" % self.create_filter_callable(node.filter_args.args, "__M_buf.getvalue()", False),
514                None
515                )
516       
517    def visitCode(self, node):
518        if not node.ismodule:
519            self.write_source_comment(node)
520            self.printer.write_indented_block(node.text)
521
522            if not self.in_def and len(self.identifiers.locally_assigned) > 0:
523                # if we are the "template" def, fudge locally declared/modified variables into the "__M_locals" dictionary,
524                # which is used for def calls within the same template, to simulate "enclosing scope"
525                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()]))
526               
527    def visitIncludeTag(self, node):
528        self.write_source_comment(node)
529        args = node.attributes.get('args')
530        if args:
531            self.printer.writeline("runtime._include_file(context, %s, _template_uri, %s)" % (node.parsed_attributes['file'], args))
532        else:
533            self.printer.writeline("runtime._include_file(context, %s, _template_uri)" % (node.parsed_attributes['file']))
534           
535    def visitNamespaceTag(self, node):
536        pass
537           
538    def visitDefTag(self, node):
539        pass
540
541    def visitCallNamespaceTag(self, node):
542        # TODO: we can put namespace-specific checks here, such
543        # as ensure the given namespace will be imported,
544        # pre-import the namespace, etc.
545        self.visitCallTag(node)
546       
547    def visitCallTag(self, node):
548        self.printer.writeline("def ccall(caller):")
549        export = ['body']
550        callable_identifiers = self.identifiers.branch(node, nested=True)
551        body_identifiers = callable_identifiers.branch(node, nested=False)
552        # we want the 'caller' passed to ccall to be used for the body() function,
553        # but for other non-body() <%def>s within <%call> we want the current caller off the call stack (if any)
554        body_identifiers.add_declared('caller')
555       
556        self.identifier_stack.append(body_identifiers)
557        class DefVisitor(object):
558            def visitDefTag(s, node):
559                self.write_inline_def(node, callable_identifiers, nested=False)
560                export.append(node.name)
561                # remove defs that are within the <%call> from the "closuredefs" defined
562                # in the body, so they dont render twice
563                if node.name in body_identifiers.closuredefs:
564                    del body_identifiers.closuredefs[node.name]
565
566        vis = DefVisitor()
567        for n in node.nodes:
568            n.accept_visitor(vis)
569        self.identifier_stack.pop()
570       
571        bodyargs = node.body_decl.get_argument_expressions()   
572        self.printer.writeline("def body(%s):" % ','.join(bodyargs))
573        # TODO: figure out best way to specify buffering/nonbuffering (at call time would be better)
574        buffered = False
575        if buffered:
576            self.printer.writelines(
577                "context._push_buffer()",
578                "try:"
579            )
580        self.write_variable_declares(body_identifiers)
581        self.identifier_stack.append(body_identifiers)
582       
583        for n in node.nodes:
584            n.accept_visitor(self)
585        self.identifier_stack.pop()
586       
587        self.write_def_finish(node, buffered, False, False, callstack=False)
588        self.printer.writelines(
589            None,
590            "return [%s]" % (','.join(export)),
591            None
592        )
593
594        self.printer.writelines(
595            # get local reference to current caller, if any
596            "caller = context.caller_stack._get_caller()",
597            # push on caller for nested call
598            "context.caller_stack.nextcaller = runtime.Namespace('caller', context, callables=ccall(caller))",
599            "try:")
600        self.write_source_comment(node)
601        self.printer.writelines(
602                "__M_writer(%s)" % self.create_filter_callable([], node.expression, True),
603            "finally:",
604                "context.caller_stack.nextcaller = None",
605            None
606        )
607
608class _Identifiers(object):
609    """tracks the status of identifier names as template code is rendered."""
610    def __init__(self, node=None, parent=None, nested=False):
611        if parent is not None:
612            # things that have already been declared in an enclosing namespace (i.e. names we can just use)
613            self.declared = util.Set(parent.declared).union([c.name for c in parent.closuredefs.values()]).union(parent.locally_declared).union(parent.argument_declared)
614           
615            # if these identifiers correspond to a "nested" scope, it means whatever the
616            # parent identifiers had as undeclared will have been declared by that parent,
617            # and therefore we have them in our scope.
618            if nested:
619                self.declared = self.declared.union(parent.undeclared)
620           
621            # top level defs that are available
622            self.topleveldefs = util.SetLikeDict(**parent.topleveldefs)
623        else:
624            self.declared = util.Set()
625            self.topleveldefs = util.SetLikeDict()
626       
627        # things within this level that are referenced before they are declared (e.g. assigned to)
628        self.undeclared = util.Set()
629       
630        # things that are declared locally.  some of these things could be in the "undeclared"
631        # list as well if they are referenced before declared
632        self.locally_declared = util.Set()
633   
634        # assignments made in explicit python blocks.  these will be propigated to
635        # the context of local def calls.
636        self.locally_assigned = util.Set()
637       
638        # things that are declared in the argument signature of the def callable
639        self.argument_declared = util.Set()
640       
641        # closure defs that are defined in this level
642        self.closuredefs = util.SetLikeDict()
643       
644        self.node = node
645       
646        if node is not None:
647            node.accept_visitor(self)
648       
649    def branch(self, node, **kwargs):
650        """create a new Identifiers for a new Node, with this Identifiers as the parent."""
651        return _Identifiers(node, self, **kwargs)
652   
653    defs = property(lambda self:util.Set(self.topleveldefs.union(self.closuredefs).values()))
654   
655    def __repr__(self):
656        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))
657       
658    def check_declared(self, node):
659        """update the state of this Identifiers with the undeclared and declared identifiers of the given node."""
660        for ident in node.undeclared_identifiers():
661            if ident != 'context' and ident not in self.declared.union(self.locally_declared):
662                self.undeclared.add(ident)
663        for ident in node.declared_identifiers():
664            self.locally_declared.add(ident)
665   
666    def add_declared(self, ident):
667        self.declared.add(ident)
668        if ident in self.undeclared:
669            self.undeclared.remove(ident)
670                       
671    def visitExpression(self, node):
672        self.check_declared(node)
673    def visitControlLine(self, node):
674        self.check_declared(node)
675    def visitCode(self, node):
676        if not node.ismodule:
677            self.check_declared(node)
678            self.locally_assigned = self.locally_assigned.union(node.declared_identifiers())
679    def visitDefTag(self, node):
680        if node.is_root():
681            self.topleveldefs[node.name] = node
682        elif node is not self.node:
683            self.closuredefs[node.name] = node
684        for ident in node.undeclared_identifiers():
685            if ident != 'context' and ident not in self.declared.union(self.locally_declared):
686                self.undeclared.add(ident)
687        # visit defs only one level deep
688        if node is self.node:
689            for ident in node.declared_identifiers():
690                self.argument_declared.add(ident)
691            for n in node.nodes:
692                n.accept_visitor(self)
693    def visitIncludeTag(self, node):
694        self.check_declared(node)
695    def visitPageTag(self, node):
696        for ident in node.declared_identifiers():
697            self.argument_declared.add(ident)
698        self.check_declared(node)
699   
700    def visitCallNamespaceTag(self, node):
701        self.visitCallTag(node)
702       
703    def visitCallTag(self, node):
704        if node is self.node:
705            for ident in node.undeclared_identifiers():
706                if ident != 'context' and ident not in self.declared.union(self.locally_declared):
707                    self.undeclared.add(ident)
708            for ident in node.declared_identifiers():
709                self.argument_declared.add(ident)
710            for n in node.nodes:
711                n.accept_visitor(self)
712        else:
713            for ident in node.undeclared_identifiers():
714                if ident != 'context' and ident not in self.declared.union(self.locally_declared):
715                    self.undeclared.add(ident)
716               
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。