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

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

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

行番号 
1# -*- coding: utf-8 -*-
2"""
3    ast
4    ~~~
5
6    The `ast` module helps Python applications to process trees of the Python
7    abstract syntax grammar.  The abstract syntax itself might change with
8    each Python release; this module helps to find out programmatically what
9    the current grammar looks like and allows modifications of it.
10
11    An abstract syntax tree can be generated by passing `ast.PyCF_ONLY_AST` as
12    a flag to the `compile()` builtin function or by using the `parse()`
13    function from this module.  The result will be a tree of objects whose
14    classes all inherit from `ast.AST`.
15
16    A modified abstract syntax tree can be compiled into a Python code object
17    using the built-in `compile()` function.
18
19    Additionally various helper functions are provided that make working with
20    the trees simpler.  The main intention of the helper functions and this
21    module in general is to provide an easy to use interface for libraries
22    that work tightly with the python syntax (template engines for example).
23
24
25    :copyright: Copyright 2008 by Armin Ronacher.
26    :license: Python License.
27"""
28from _ast import *
29
30
31BOOLOP_SYMBOLS = {
32    And:        'and',
33    Or:         'or'
34}
35
36BINOP_SYMBOLS = {
37    Add:        '+',
38    Sub:        '-',
39    Mult:       '*',
40    Div:        '/',
41    FloorDiv:   '//',
42    Mod:        '%',
43    LShift:     '<<',
44    RShift:     '>>',
45    BitOr:      '|',
46    BitAnd:     '&',
47    BitXor:     '^'
48}
49
50CMPOP_SYMBOLS = {
51    Eq:         '==',
52    Gt:         '>',
53    GtE:        '>=',
54    In:         'in',
55    Is:         'is',
56    IsNot:      'is not',
57    Lt:         '<',
58    LtE:        '<=',
59    NotEq:      '!=',
60    NotIn:      'not in'
61}
62
63UNARYOP_SYMBOLS = {
64    Invert:     '~',
65    Not:        'not',
66    UAdd:       '+',
67    USub:       '-'
68}
69
70ALL_SYMBOLS = {}
71ALL_SYMBOLS.update(BOOLOP_SYMBOLS)
72ALL_SYMBOLS.update(BINOP_SYMBOLS)
73ALL_SYMBOLS.update(CMPOP_SYMBOLS)
74ALL_SYMBOLS.update(UNARYOP_SYMBOLS)
75
76
77def parse(expr, filename='<unknown>', mode='exec'):
78    """Parse an expression into an AST node."""
79    return compile(expr, filename, mode, PyCF_ONLY_AST)
80
81
82def to_source(node, indent_with=' ' * 4):
83    """
84    This function can convert a node tree back into python sourcecode.  This
85    is useful for debugging purposes, especially if you're dealing with custom
86    asts not generated by python itself.
87
88    It could be that the sourcecode is evaluable when the AST itself is not
89    compilable / evaluable.  The reason for this is that the AST contains some
90    more data than regular sourcecode does, which is dropped during
91    conversion.
92
93    Each level of indentation is replaced with `indent_with`.  Per default this
94    parameter is equal to four spaces as suggested by PEP 8, but it might be
95    adjusted to match the application's styleguide.
96    """
97    generator = SourceGenerator(indent_with)
98    generator.visit(node)
99    return ''.join(generator.result)
100
101
102def dump(node):
103    """
104    A very verbose representation of the node passed.  This is useful for
105    debugging purposes.
106    """
107    def _format(node):
108        if isinstance(node, AST):
109            return '%s(%s)' % (node.__class__.__name__,
110                               ', '.join('%s=%s' % (a, _format(b))
111                                         for a, b in iter_fields(node)))
112        elif isinstance(node, list):
113            return '[%s]' % ', '.join(_format(x) for x in node)
114        return repr(node)
115    if not isinstance(node, AST):
116        raise TypeError('expected AST, got %r' % node.__class__.__name__)
117    return _format(node)
118
119
120def copy_location(new_node, old_node):
121    """
122    Copy the source location hint (`lineno` and `col_offset`) from the
123    old to the new node if possible and return the new one.
124    """
125    for attr in 'lineno', 'col_offset':
126        if attr in old_node._attributes and attr in new_node._attributes \
127           and hasattr(old_node, attr):
128            setattr(new_node, attr, getattr(old_node, attr))
129    return new_node
130
131
132def fix_missing_locations(node):
133    """
134    Some nodes require a line number and the column offset.  Without that
135    information the compiler will abort the compilation.  Because it can be
136    a dull task to add appropriate line numbers and column offsets when
137    adding new nodes this function can help.  It copies the line number and
138    column offset of the parent node to the child nodes without this
139    information.
140
141    Unlike `copy_location` this works recursive and won't touch nodes that
142    already have a location information.
143    """
144    def _fix(node, lineno, col_offset):
145        if 'lineno' in node._attributes:
146            if not hasattr(node, 'lineno'):
147                node.lineno = lineno
148            else:
149                lineno = node.lineno
150        if 'col_offset' in node._attributes:
151            if not hasattr(node, 'col_offset'):
152                node.col_offset = col_offset
153            else:
154                col_offset = node.col_offset
155        for child in iter_child_nodes(node):
156            _fix(child, lineno, col_offset)
157    _fix(node, 1, 0)
158    return node
159
160
161def increment_lineno(node, n=1):
162    """
163    Increment the line numbers of all nodes by `n` if they have line number
164    attributes.  This is useful to "move code" to a different location in a
165    file.
166    """
167    for node in zip((node,), walk(node)):
168        if 'lineno' in node._attributes:
169            node.lineno = getattr(node, 'lineno', 0) + n
170
171
172def iter_fields(node):
173    """Iterate over all fields of a node, only yielding existing fields."""
174    # CPython 2.5 compat
175    if not hasattr(node, '_fields') or not node._fields:
176        return
177    for field in node._fields:
178        try:
179            yield field, getattr(node, field)
180        except AttributeError:
181            pass
182
183
184def get_fields(node):
185    """Like `iter_fiels` but returns a dict."""
186    return dict(iter_fields(node))
187
188
189def iter_child_nodes(node):
190    """Iterate over all child nodes or a node."""
191    for name, field in iter_fields(node):
192        if isinstance(field, AST):
193            yield field
194        elif isinstance(field, list):
195            for item in field:
196                if isinstance(item, AST):
197                    yield item
198
199
200def get_child_nodes(node):
201    """Like `iter_child_nodes` but returns a list."""
202    return list(iter_child_nodes(node))
203
204
205def get_compile_mode(node):
206    """
207    Get the mode for `compile` of a given node.  If the node is not a `mod`
208    node (`Expression`, `Module` etc.) a `TypeError` is thrown.
209    """
210    if not isinstance(node, mod):
211        raise TypeError('expected mod node, got %r' % node.__class__.__name__)
212    return {
213        Expression:     'eval',
214        Interactive:    'single'
215    }.get(node.__class__, 'expr')
216
217
218def get_docstring(node):
219    """
220    Return the docstring for the given node or `None` if no docstring can be
221    found.  If the node provided does not accept docstrings a `TypeError`
222    will be raised.
223    """
224    if not isinstance(node, (FunctionDef, ClassDef, Module)):
225        raise TypeError("%r can't have docstrings" % node.__class__.__name__)
226    if node.body and isinstance(node.body[0], Str):
227        return node.body[0].s
228
229
230def walk(node):
231    """
232    Iterate over all nodes.  This is useful if you only want to modify nodes in
233    place and don't care about the context or the order the nodes are returned.
234    """
235    from collections import deque
236    todo = deque([node])
237    while todo:
238        node = todo.popleft()
239        todo.extend(iter_child_nodes(node))
240        yield node
241
242
243class NodeVisitor(object):
244    """
245    Walks the abstract syntax tree and call visitor functions for every node
246    found.  The visitor functions may return values which will be forwarded
247    by the `visit` method.
248
249    Per default the visitor functions for the nodes are ``'visit_'`` +
250    class name of the node.  So a `TryFinally` node visit function would
251    be `visit_TryFinally`.  This behavior can be changed by overriding
252    the `get_visitor` function.  If no visitor function exists for a node
253    (return value `None`) the `generic_visit` visitor is used instead.
254
255    Don't use the `NodeVisitor` if you want to apply changes to nodes during
256    traversing.  For this a special visitor exists (`NodeTransformer`) that
257    allows modifications.
258    """
259
260    def get_visitor(self, node):
261        """
262        Return the visitor function for this node or `None` if no visitor
263        exists for this node.  In that case the generic visit function is
264        used instead.
265        """
266        method = 'visit_' + node.__class__.__name__
267        return getattr(self, method, None)
268
269    def visit(self, node):
270        """Visit a node."""
271        f = self.get_visitor(node)
272        if f is not None:
273            return f(node)
274        return self.generic_visit(node)
275
276    def generic_visit(self, node):
277        """Called if no explicit visitor function exists for a node."""
278        for field, value in iter_fields(node):
279            if isinstance(value, list):
280                for item in value:
281                    if isinstance(item, AST):
282                        self.visit(item)
283            elif isinstance(value, AST):
284                self.visit(value)
285
286
287class NodeTransformer(NodeVisitor):
288    """
289    Walks the abstract syntax tree and allows modifications of nodes.
290
291    The `NodeTransformer` will walk the AST and use the return value of the
292    visitor functions to replace or remove the old node.  If the return
293    value of the visitor function is `None` the node will be removed
294    from the previous location otherwise it's replaced with the return
295    value.  The return value may be the original node in which case no
296    replacement takes place.
297
298    Here an example transformer that rewrites all `foo` to `data['foo']`::
299
300        class RewriteName(NodeTransformer):
301
302            def visit_Name(self, node):
303                return copy_location(Subscript(
304                    value=Name(id='data', ctx=Load()),
305                    slice=Index(value=Str(s=node.id)),
306                    ctx=node.ctx
307                ), node)
308
309    Keep in mind that if the node you're operating on has child nodes
310    you must either transform the child nodes yourself or call the generic
311    visit function for the node first.
312
313    Nodes that were part of a collection of statements (that applies to
314    all statement nodes) may also return a list of nodes rather than just
315    a single node.
316
317    Usually you use the transformer like this::
318
319        node = YourTransformer().visit(node)
320    """
321
322    def generic_visit(self, node):
323        for field, old_value in iter_fields(node):
324            old_value = getattr(node, field, None)
325            if isinstance(old_value, list):
326                new_values = []
327                for value in old_value:
328                    if isinstance(value, AST):
329                        value = self.visit(value)
330                        if value is None:
331                            continue
332                        elif not isinstance(value, AST):
333                            new_values.extend(value)
334                            continue
335                    new_values.append(value)
336                old_value[:] = new_values
337            elif isinstance(old_value, AST):
338                new_node = self.visit(old_value)
339                if new_node is None:
340                    delattr(node, field)
341                else:
342                    setattr(node, field, new_node)
343        return node
344
345
346class SourceGenerator(NodeVisitor):
347    """
348    This visitor is able to transform a well formed syntax tree into python
349    sourcecode.  For more details have a look at the docstring of the
350    `node_to_source` function.
351    """
352
353    def __init__(self, indent_with):
354        self.result = []
355        self.indent_with = indent_with
356        self.indentation = 0
357        self.new_lines = 0
358
359    def write(self, x):
360        if self.new_lines:
361            if self.result:
362                self.result.append('\n' * self.new_lines)
363            self.result.append(self.indent_with * self.indentation)
364            self.new_lines = 0
365        self.result.append(x)
366
367    def newline(self, n=1):
368        self.new_lines = max(self.new_lines, n)
369
370    def body(self, statements):
371        self.new_line = True
372        self.indentation += 1
373        for stmt in statements:
374            self.visit(stmt)
375        self.indentation -= 1
376
377    def body_or_else(self, node):
378        self.body(node.body)
379        if node.orelse:
380            self.newline()
381            self.write('else:')
382            self.body(node.orelse)
383
384    def signature(self, node):
385        want_comma = []
386        def write_comma():
387            if want_comma:
388                self.write(', ')
389            else:
390                want_comma.append(True)
391
392        padding = [None] * (len(node.args) - len(node.defaults))
393        for arg, default in zip(node.args, padding + node.defaults):
394            write_comma()
395            self.visit(arg)
396            if default is not None:
397                self.write('=')
398                self.visit(default)
399        if node.vararg is not None:
400            write_comma()
401            self.write('*' + node.vararg)
402        if node.kwarg is not None:
403            write_comma()
404            self.write('**' + node.kwarg)
405
406    def decorators(self, node):
407        for decorator in node.decorator_list:
408            self.newline()
409            self.write('@')
410            self.visit(decorator)
411
412    # Statements
413
414    def visit_Assign(self, node):
415        self.newline()
416        for idx, target in enumerate(node.targets):
417            if idx:
418                self.write(', ')
419            self.visit(target)
420        self.write(' = ')
421        self.visit(node.value)
422
423    def visit_AugAssign(self, node):
424        self.newline()
425        self.visit(node.target)
426        self.write(BINOP_SYMBOLS[type(node.op)] + '=')
427        self.visit(node.value)
428
429    def visit_ImportFrom(self, node):
430        self.newline()
431        self.write('from %s%s import ' % ('.' * node.level, node.module))
432        for idx, item in enumerate(node.names):
433            if idx:
434                self.write(', ')
435            self.write(item)
436
437    def visit_Import(self, node):
438        self.newline()
439        for item in node.names:
440            self.write('import ')
441            self.visit(item)
442
443    def visit_Expr(self, node):
444        self.newline()
445        self.generic_visit(node)
446
447    def visit_FunctionDef(self, node):
448        self.newline(n=2)
449        self.decorators(node)
450        self.newline()
451        self.write('def %s(' % node.name)
452        self.signature(node.args)
453        self.write('):')
454        self.body(node.body)
455
456    def visit_ClassDef(self, node):
457        have_args = []
458        def paren_or_comma():
459            if have_args:
460                self.write(', ')
461            else:
462                have_args.append(True)
463                self.write('(')
464
465        self.newline(n=3)
466        self.decorators(node)
467        self.newline()
468        self.write('class %s' % node.name)
469        for base in node.bases:
470            paren_or_comma()
471            self.visit(base)
472        # XXX: the if here is used to keep this module compatible
473        #      with python 2.6.
474        if hasattr(node, 'keywords'):
475            for keyword in node.keywords:
476                paren_or_comma()
477                self.write(keyword.arg + '=')
478                self.visit(keyword.value)
479            if node.starargs is not None:
480                paren_or_comma()
481                self.write('*')
482                self.visit(node.starargs)
483            if node.kwargs is not None:
484                paren_or_comma()
485                self.write('**')
486                self.visit(node.kwargs)
487        self.write(have_args and '):' or ':')
488        self.body(node.body)
489
490    def visit_If(self, node):
491        self.newline()
492        self.write('if ')
493        self.visit(node.test)
494        self.write(':')
495        self.body(node.body)
496        while True:
497            else_ = node.orelse
498            if len(else_) == 1 and isinstance(else_[0], If):
499                node = else_[0]
500                self.newline()
501                self.write('elif ')
502                self.visit(node.test)
503                self.write(':')
504                self.body(node.body)
505            else:
506                self.newline()
507                self.write('else:')
508                self.body(else_)
509                break
510
511    def visit_For(self, node):
512        self.newline()
513        self.write('for ')
514        self.visit(node.target)
515        self.write(' in ')
516        self.visit(node.iter)
517        self.write(':')
518        self.body_or_else(node)
519
520    def visit_While(self, node):
521        self.newline()
522        self.write('while ')
523        self.visit(node.test)
524        self.write(':')
525        self.body_or_else(node)
526
527    def visit_With(self, node):
528        self.newline()
529        self.write('with ')
530        self.visit(node.context_expr)
531        if node.optional_vars is not None:
532            self.write(' as ')
533            self.visit(node.optional_vars)
534        self.write(':')
535        self.body(node.body)
536
537    def visit_Pass(self, node):
538        self.newline()
539        self.write('pass')
540
541    def visit_Print(self, node):
542        # XXX: python 2.6 only
543        self.newline()
544        self.write('print ')
545        want_comma = False
546        if node.dest is not None:
547            self.write(' >> ')
548            self.visit(node.dest)
549            want_comma = True
550        for value in node.values:
551            if want_comma:
552                self.write(', ')
553            self.visit(value)
554            want_comma = True
555        if not node.nl:
556            self.write(',')
557
558    def visit_Delete(self, node):
559        self.newline()
560        self.write('del ')
561        for idx, target in enumerate(node):
562            if idx:
563                self.write(', ')
564            self.visit(target)
565
566    def visit_TryExcept(self, node):
567        self.newline()
568        self.write('try:')
569        self.body(node.body)
570        for handler in node.handlers:
571            self.visit(handler)
572
573    def visit_TryFinally(self, node):
574        self.newline()
575        self.write('try:')
576        self.body(node.body)
577        self.newline()
578        self.write('finally:')
579        self.body(node.finalbody)
580
581    def visit_Global(self, node):
582        self.newline()
583        self.write('global ' + ', '.join(node.names))
584
585    def visit_Nonlocal(self, node):
586        self.newline()
587        self.write('nonlocal ' + ', '.join(node.names))
588
589    def visit_Return(self, node):
590        self.newline()
591        self.write('return ')
592        self.visit(node.value)
593
594    def visit_Break(self, node):
595        self.newline()
596        self.write('break')
597
598    def visit_Continue(self, node):
599        self.newline()
600        self.write('continue')
601
602    def visit_Raise(self, node):
603        # XXX: Python 2.6 / 3.0 compatibility
604        self.newline()
605        self.write('raise')
606        if hasattr(node, 'exc') and node.exc is not None:
607            self.write(' ')
608            self.visit(node.exc)
609            if node.cause is not None:
610                self.write(' from ')
611                self.visit(node.cause)
612        elif hasattr(node, 'type') and node.type is not None:
613            self.visit(node.type)
614            if node.inst is not None:
615                self.write(', ')
616                self.visit(node.inst)
617            if node.tback is not None:
618                self.write(', ')
619                self.visit(node.tback)
620
621    # Expressions
622
623    def visit_Attribute(self, node):
624        self.visit(node.value)
625        self.write('.' + node.attr)
626
627    def visit_Call(self, node):
628        want_comma = []
629        def write_comma():
630            if want_comma:
631                self.write(', ')
632            else:
633                want_comma.append(True)
634
635        self.visit(node.func)
636        self.write('(')
637        for arg in node.args:
638            write_comma()
639            self.visit(arg)
640        for keyword in node.keywords:
641            write_comma()
642            self.write(keyword.arg + '=')
643            self.visit(keyword.value)
644        if node.starargs is not None:
645            write_comma()
646            self.write('*')
647            self.visit(node.starargs)
648        if node.kwargs is not None:
649            write_comma()
650            self.write('**')
651            self.visit(node.kwargs)
652        self.write(')')
653
654    def visit_Name(self, node):
655        self.write(node.id)
656
657    def visit_Str(self, node):
658        self.write(repr(node.s))
659
660    def visit_Bytes(self, node):
661        self.write(repr(node.s))
662
663    def visit_Num(self, node):
664        self.write(repr(node.n))
665
666    def visit_Tuple(self, node):
667        self.write('(')
668        idx = -1
669        for idx, item in enumerate(node.elts):
670            if idx:
671                self.write(', ')
672            self.visit(item)
673        self.write(idx and ')' or ',)')
674
675    def sequence_visit(left, right):
676        def visit(self, node):
677            self.write(left)
678            for idx, item in enumerate(node.elts):
679                if idx:
680                    self.write(', ')
681                self.visit(item)
682            self.write(right)
683        return visit
684
685    visit_List = sequence_visit('[', ']')
686    visit_Set = sequence_visit('{', '}')
687    del sequence_visit
688
689    def visit_Dict(self, node):
690        self.write('{')
691        for idx, (key, value) in enumerate(zip(node.keys, node.values)):
692            if idx:
693                self.write(', ')
694            self.visit(key)
695            self.write(': ')
696            self.visit(value)
697        self.write('}')
698
699    def visit_BinOp(self, node):
700        self.write('(')
701        self.visit(node.left)
702        self.write(' %s ' % BINOP_SYMBOLS[type(node.op)])
703        self.visit(node.right)
704        self.write(')')
705
706    def visit_BoolOp(self, node):
707        self.write('(')
708        for idx, value in enumerate(node.values):
709            if idx:
710                self.write(' %s ' % BOOLOP_SYMBOLS[type(node.op)])
711            self.visit(value)
712        self.write(')')
713
714    def visit_Compare(self, node):
715        self.write('(')
716        self.visit(node.left)
717        for op, right in zip(node.ops, node.comparators):
718            self.write(' %s ' % CMPOP_SYMBOLS[type(op)])
719            self.visit(right)
720        self.write(')')
721
722    def visit_UnaryOp(self, node):
723        self.write('(')
724        op = UNARYOP_SYMBOLS[type(node.op)]
725        self.write(op)
726        if op == 'not':
727            self.write(' ')
728        self.visit(node.operand)
729        self.write(')')
730
731    def visit_Subscript(self, node):
732        self.visit(node.value)
733        self.write('[')
734        self.visit(node.slice)
735        self.write(']')
736
737    def visit_Slice(self, node):
738        if node.lower is not None:
739            self.visit(node.lower)
740        self.write(':')
741        if node.upper is not None:
742            self.visit(node.upper)
743        if node.step is not None:
744            self.write(':')
745            if not (isinstance(node.step, Name) and node.step.id == 'None'):
746                self.visit(node.step)
747
748    def visit_ExtSlice(self, node):
749        for idx, item in node.dims:
750            if idx:
751                self.write(', ')
752            self.visit(item)
753
754    def visit_Yield(self, node):
755        self.write('yield ')
756        self.visit(node.value)
757
758    def visit_Lambda(self, node):
759        self.write('lambda ')
760        self.signature(node.args)
761        self.write(': ')
762        self.visit(node.body)
763
764    def visit_Ellipsis(self, node):
765        self.write('Ellipsis')
766
767    def generator_visit(left, right):
768        def visit(self, node):
769            self.write(left)
770            self.visit(node.elt)
771            for comprehension in node.generators:
772                self.visit(comprehension)
773            self.write(right)
774        return visit
775
776    visit_ListComp = generator_visit('[', ']')
777    visit_GeneratorExp = generator_visit('(', ')')
778    visit_SetComp = generator_visit('{', '}')
779    del generator_visit
780
781    def visit_DictComp(self, node):
782        self.write('{')
783        self.visit(node.key)
784        self.write(': ')
785        self.visit(node.value)
786        for comprehension in node.generators:
787            self.visit(comprehension)
788        self.write('}')
789
790    def visit_IfExp(self, node):
791        self.visit(node.body)
792        self.write(' if ')
793        self.visit(node.test)
794        self.write(' else ')
795        self.visit(node.orelse)
796
797    def visit_Starred(self, node):
798        self.write('*')
799        self.visit(node.value)
800
801    def visit_Repr(self, node):
802        # XXX: python 2.6 only
803        self.write('`')
804        self.visit(node.value)
805        self.write('`')
806
807    # Helper Nodes
808
809    def visit_alias(self, node):
810        self.write(node.name)
811        if node.asname is not None:
812            self.write(' as ' + node.asname)
813
814    def visit_comprehension(self, node):
815        self.write(' for ')
816        self.visit(node.target)
817        self.write(' in ')
818        self.visit(node.iter)
819        if node.ifs:
820            for if_ in node.ifs:
821                self.write(' if ')
822                self.visit(if_)
823
824    def visit_excepthandler(self, node):
825        self.newline()
826        self.write('except')
827        if node.type is not None:
828            self.write(' ')
829            self.visit(node.type)
830            if node.name is not None:
831                self.write(' as ')
832                self.visit(node.name)
833        self.write(':')
834        self.body(node.body)
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。