Edgewall Software

source: trunk/genshi/template/ast24.py

Last change on this file was 1078, checked in by cmlenz, 14 years ago

More 2to3 diff size reduction.

  • Property svn:eol-style set to native
File size: 17.3 KB
RevLine 
[945]1# -*- coding: utf-8 -*-
2#
[1077]3# Copyright (C) 2008-2009 Edgewall Software
[945]4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution. The terms
8# are also available at http://genshi.edgewall.org/wiki/License.
9#
10# This software consists of voluntary contributions made by many
11# individuals. For the exact contribution history, see the revision
12# history and logs, available at http://genshi.edgewall.org/log/.
13
[979]14"""Emulation of the proper abstract syntax tree API for Python 2.4."""
[945]15
[975]16import compiler
17import compiler.ast
[973]18
[975]19from genshi.template import _ast24 as _ast
[973]20
[982]21__all__ = ['_ast', 'parse']
22__docformat__ = 'restructuredtext en'
[973]23
[982]24
[975]25def _new(cls, *args, **kwargs):
26    ret = cls()
27    if ret._fields:
28        for attr, value in zip(ret._fields, args):
29            if attr in kwargs:
[1078]30                raise ValueError('Field set both in args and kwargs')
[975]31            setattr(ret, attr, value)
32    for attr in kwargs:
33        if (getattr(ret, '_fields', None) and attr in ret._fields) \
34                or (getattr(ret, '_attributes', None) and 
35                        attr in ret._attributes):
36            setattr(ret, attr, kwargs[attr])
37    return ret
[973]38
[944]39
[975]40class ASTUpgrader(object):
41    """Transformer changing structure of Python 2.4 ASTs to
42    Python 2.5 ones.
[944]43
[975]44    Transforms ``compiler.ast`` Abstract Syntax Tree to builtin ``_ast``.
[979]45    It can use fake`` _ast`` classes and this way allow ``_ast`` emulation
46    in Python 2.4.
47    """
[973]48
[975]49    def __init__(self):
50        self.out_flags = None
51        self.lines = [-1]
[973]52
[975]53    def _new(self, *args, **kwargs):
54        return _new(lineno = self.lines[-1], *args, **kwargs)
[944]55
[975]56    def visit(self, node):
57        if node is None:
58            return None
59        if type(node) is tuple:
60            return tuple([self.visit(n) for n in node])
61        lno = getattr(node, 'lineno', None)
62        if lno is not None:
63            self.lines.append(lno)
[979]64        visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None)
65        if visitor is None:
66            raise Exception('Unhandled node type %r' % type(node))
[973]67
[975]68        retval = visitor(node)
69        if lno is not None:
70            self.lines.pop()
71        return retval
[944]72
[979]73    def visit_Module(self, node):
[975]74        body = self.visit(node.node)
75        if node.doc:
76            body = [self._new(_ast.Expr, self._new(_ast.Str, node.doc))] + body
77        return self._new(_ast.Module, body)
[944]78
[979]79    def visit_Expression(self, node):
[975]80        return self._new(_ast.Expression, self.visit(node.node))
[944]81
[975]82    def _extract_args(self, node):
83        tab = node.argnames[:]
84        if node.flags & compiler.ast.CO_VARKEYWORDS:
85            kwarg = tab[-1]
86            tab = tab[:-1]
87        else:
88            kwarg = None
[945]89
[975]90        if node.flags & compiler.ast.CO_VARARGS:
91            vararg = tab[-1]
92            tab = tab[:-1]
93        else:
94            vararg = None
[944]95
[975]96        def _tup(t):
97            if isinstance(t, str):
98                return self._new(_ast.Name, t, _ast.Store())
99            elif isinstance(t, tuple):
100                elts = [_tup(x) for x in t]
101                return self._new(_ast.Tuple, elts, _ast.Store())
[944]102            else:
[975]103                raise NotImplemented
[1078]104
[975]105        args = []
106        for arg in tab:
107            if isinstance(arg, str):
108                args.append(self._new(_ast.Name, arg, _ast.Param()))
109            elif isinstance(arg, tuple):
110                args.append(_tup(arg))
[944]111            else:
[975]112                assert False, node.__class__
[944]113
[1078]114        defaults = [self.visit(d) for d in node.defaults]
[975]115        return self._new(_ast.arguments, args, vararg, kwarg, defaults)
[944]116
117
[979]118    def visit_Function(self, node):
[975]119        if getattr(node, 'decorators', ()):
120            decorators = [self.visit(d) for d in node.decorators.nodes]
121        else:
122            decorators = []
[944]123
[975]124        args = self._extract_args(node)
125        body = self.visit(node.code)
126        if node.doc:
127            body = [self._new(_ast.Expr, self._new(_ast.Str, node.doc))] + body
128        return self._new(_ast.FunctionDef, node.name, args, body, decorators)
[944]129
[979]130    def visit_Class(self, node):
[975]131        #self.name_types.append(_ast.Load)
132        bases = [self.visit(b) for b in node.bases]
133        #self.name_types.pop()
134        body = self.visit(node.code)
135        if node.doc:
136            body = [self._new(_ast.Expr, self._new(_ast.Str, node.doc))] + body
137        return self._new(_ast.ClassDef, node.name, bases, body)
[944]138
[979]139    def visit_Return(self, node):
[975]140        return self._new(_ast.Return, self.visit(node.value))
[944]141
[979]142    def visit_Assign(self, node):
[975]143        #self.name_types.append(_ast.Store)
144        targets = [self.visit(t) for t in node.nodes]
145        #self.name_types.pop()
146        return self._new(_ast.Assign, targets, self.visit(node.expr))
[944]147
[975]148    aug_operators = {
[977]149        '+=': _ast.Add,
150        '/=': _ast.Div,
151        '//=': _ast.FloorDiv,
152        '<<=': _ast.LShift,
153        '%=': _ast.Mod,
154        '*=': _ast.Mult,
155        '**=': _ast.Pow,
156        '>>=': _ast.RShift,
157        '-=': _ast.Sub,
158    }
[944]159
[979]160    def visit_AugAssign(self, node):
[975]161        target = self.visit(node.node)
[944]162
[975]163        # Because it's AugAssign target can't be list nor tuple
164        # so we only have to change context of one node
165        target.ctx = _ast.Store()
166        op = self.aug_operators[node.op]()
167        return self._new(_ast.AugAssign, target, op, self.visit(node.expr))
[944]168
[979]169    def _visit_Print(nl):
[975]170        def _visit(self, node):
171            values = [self.visit(v) for v in node.nodes]
172            return self._new(_ast.Print, self.visit(node.dest), values, nl)
173        return _visit
[944]174
[979]175    visit_Print = _visit_Print(False)
176    visit_Printnl = _visit_Print(True)
177    del _visit_Print
[944]178
[979]179    def visit_For(self, node):
[975]180        return self._new(_ast.For, self.visit(node.assign), self.visit(node.list),
181                        self.visit(node.body), self.visit(node.else_))
[944]182
[979]183    def visit_While(self, node):
[975]184        return self._new(_ast.While, self.visit(node.test), self.visit(node.body),
185                        self.visit(node.else_))
[944]186
[979]187    def visit_If(self, node):
[975]188        def _level(tests, else_):
189            test = self.visit(tests[0][0])
190            body = self.visit(tests[0][1])
191            if len(tests) == 1:
192                orelse = self.visit(else_)
193            else:
194                orelse = [_level(tests[1:], else_)]
195            return self._new(_ast.If, test, body, orelse)
196        return _level(node.tests, node.else_)
[944]197
[979]198    def visit_With(self, node):
[975]199        return self._new(_ast.With, self.visit(node.expr),
200                            self.visit(node.vars), self.visit(node.body))
[944]201
[979]202    def visit_Raise(self, node):
[975]203        return self._new(_ast.Raise, self.visit(node.expr1),
204                        self.visit(node.expr2), self.visit(node.expr3))
[944]205
[979]206    def visit_TryExcept(self, node):
[975]207        handlers = []
208        for type, name, body in node.handlers:
209            handlers.append(self._new(_ast.excepthandler, self.visit(type), 
210                            self.visit(name), self.visit(body)))
211        return self._new(_ast.TryExcept, self.visit(node.body),
212                        handlers, self.visit(node.else_))
[944]213
[979]214    def visit_TryFinally(self, node):
[975]215        return self._new(_ast.TryFinally, self.visit(node.body),
216                        self.visit(node.final))
[944]217
[979]218    def visit_Assert(self, node):
[975]219        return self._new(_ast.Assert, self.visit(node.test), self.visit(node.fail))
[944]220
[979]221    def visit_Import(self, node):
[975]222        names = [self._new(_ast.alias, n[0], n[1]) for n in node.names]
223        return self._new(_ast.Import, names)
[944]224
[979]225    def visit_From(self, node):
[975]226        names = [self._new(_ast.alias, n[0], n[1]) for n in node.names]
227        return self._new(_ast.ImportFrom, node.modname, names, 0)
[944]228
[979]229    def visit_Exec(self, node):
[975]230        return self._new(_ast.Exec, self.visit(node.expr),
231                        self.visit(node.locals), self.visit(node.globals))
[944]232
[979]233    def visit_Global(self, node):
[975]234        return self._new(_ast.Global, node.names[:])
[944]235
[979]236    def visit_Discard(self, node):
[975]237        return self._new(_ast.Expr, self.visit(node.expr))
[944]238
[975]239    def _map_class(to):
240        def _visit(self, node):
241            return self._new(to)
242        return _visit
[944]243
[979]244    visit_Pass = _map_class(_ast.Pass)
245    visit_Break = _map_class(_ast.Break)
246    visit_Continue = _map_class(_ast.Continue)
[944]247
[979]248    def _visit_BinOperator(opcls):
[975]249        def _visit(self, node):
250            return self._new(_ast.BinOp, self.visit(node.left), 
251                            opcls(), self.visit(node.right)) 
252        return _visit
[979]253    visit_Add = _visit_BinOperator(_ast.Add)
254    visit_Div = _visit_BinOperator(_ast.Div)
255    visit_FloorDiv = _visit_BinOperator(_ast.FloorDiv)
256    visit_LeftShift = _visit_BinOperator(_ast.LShift)
257    visit_Mod = _visit_BinOperator(_ast.Mod)
258    visit_Mul = _visit_BinOperator(_ast.Mult)
259    visit_Power = _visit_BinOperator(_ast.Pow)
260    visit_RightShift = _visit_BinOperator(_ast.RShift)
261    visit_Sub = _visit_BinOperator(_ast.Sub)
262    del _visit_BinOperator
[944]263
[979]264    def _visit_BitOperator(opcls):
[975]265        def _visit(self, node):
266            def _make(nodes):
267                if len(nodes) == 1:
268                    return self.visit(nodes[0])
269                left = _make(nodes[:-1])
270                right = self.visit(nodes[-1])
271                return self._new(_ast.BinOp, left, opcls(), right)
272            return _make(node.nodes)
273        return _visit
[979]274    visit_Bitand = _visit_BitOperator(_ast.BitAnd)
275    visit_Bitor = _visit_BitOperator(_ast.BitOr)
276    visit_Bitxor = _visit_BitOperator(_ast.BitXor)
277    del _visit_BitOperator
[944]278
[979]279    def _visit_UnaryOperator(opcls):
[975]280        def _visit(self, node):
281            return self._new(_ast.UnaryOp, opcls(), self.visit(node.expr))
282        return _visit
[944]283
[979]284    visit_Invert = _visit_UnaryOperator(_ast.Invert)
285    visit_Not = _visit_UnaryOperator(_ast.Not)
286    visit_UnaryAdd = _visit_UnaryOperator(_ast.UAdd)
287    visit_UnarySub = _visit_UnaryOperator(_ast.USub)
288    del _visit_UnaryOperator
[944]289
[979]290    def _visit_BoolOperator(opcls):
[975]291        def _visit(self, node):
292            values = [self.visit(n) for n in node.nodes]
293            return self._new(_ast.BoolOp, opcls(), values)
294        return _visit
[979]295    visit_And = _visit_BoolOperator(_ast.And)
296    visit_Or = _visit_BoolOperator(_ast.Or)
297    del _visit_BoolOperator
[944]298
[975]299    cmp_operators = {
[977]300        '==': _ast.Eq,
301        '!=': _ast.NotEq,
302        '<': _ast.Lt,
303        '<=': _ast.LtE,
304        '>': _ast.Gt,
305        '>=': _ast.GtE,
306        'is': _ast.Is,
307        'is not': _ast.IsNot,
308        'in': _ast.In,
309        'not in': _ast.NotIn,
310    }
[944]311
[979]312    def visit_Compare(self, node):
[975]313        left = self.visit(node.expr)
314        ops = []
315        comparators = []
316        for optype, expr in node.ops:
317            ops.append(self.cmp_operators[optype]())
318            comparators.append(self.visit(expr))
319        return self._new(_ast.Compare, left, ops, comparators)
[944]320
[979]321    def visit_Lambda(self, node):
[975]322        args = self._extract_args(node)
323        body = self.visit(node.code)
324        return self._new(_ast.Lambda, args, body)
[944]325
[979]326    def visit_IfExp(self, node):
[975]327        return self._new(_ast.IfExp, self.visit(node.test), self.visit(node.then),
328                        self.visit(node.else_))
[944]329
[979]330    def visit_Dict(self, node):
[975]331        keys = [self.visit(x[0]) for x in node.items]
332        values = [self.visit(x[1]) for x in node.items]
333        return self._new(_ast.Dict, keys, values)
[944]334
[979]335    def visit_ListComp(self, node):
[975]336        generators = [self.visit(q) for q in node.quals]
337        return self._new(_ast.ListComp, self.visit(node.expr), generators)
[944]338
[979]339    def visit_GenExprInner(self, node):
[975]340        generators = [self.visit(q) for q in node.quals]
341        return self._new(_ast.GeneratorExp, self.visit(node.expr), generators)
[944]342
[979]343    def visit_GenExpr(self, node):
[975]344        return self.visit(node.code)
[944]345
[979]346    def visit_GenExprFor(self, node):
[975]347        ifs = [self.visit(i) for i in node.ifs]
348        return self._new(_ast.comprehension, self.visit(node.assign),
349                        self.visit(node.iter), ifs)
[944]350
[979]351    def visit_ListCompFor(self, node):
[975]352        ifs = [self.visit(i) for i in node.ifs]
353        return self._new(_ast.comprehension, self.visit(node.assign),
354                        self.visit(node.list), ifs)
[944]355
[979]356    def visit_GenExprIf(self, node):
[975]357        return self.visit(node.test)
[979]358    visit_ListCompIf = visit_GenExprIf
[944]359
[979]360    def visit_Yield(self, node):
[975]361        return self._new(_ast.Yield, self.visit(node.value))
[944]362
[979]363    def visit_CallFunc(self, node):
[975]364        args = []
365        keywords = []
366        for arg in node.args:
367            if isinstance(arg, compiler.ast.Keyword):
368                keywords.append(self._new(_ast.keyword, arg.name, 
369                                        self.visit(arg.expr)))
[944]370            else:
[975]371                args.append(self.visit(arg))
372        return self._new(_ast.Call, self.visit(node.node), args, keywords,
373                    self.visit(node.star_args), self.visit(node.dstar_args))
[944]374
[979]375    def visit_Backquote(self, node):
[975]376        return self._new(_ast.Repr, self.visit(node.expr))
[944]377
[979]378    def visit_Const(self, node):
[977]379        if node.value is None: # appears in slices
[975]380            return None
[1078]381        elif isinstance(node.value, basestring):
[975]382            return self._new(_ast.Str, node.value)
383        else:
384            return self._new(_ast.Num, node.value)
[944]385
[979]386    def visit_Name(self, node):
[975]387        return self._new(_ast.Name, node.name, _ast.Load())
[944]388
[979]389    def visit_Getattr(self, node):
[975]390        return self._new(_ast.Attribute, self.visit(node.expr), node.attrname,
[979]391                         _ast.Load())
[944]392
[979]393    def visit_Tuple(self, node):
[975]394        nodes = [self.visit(n) for n in node.nodes]
395        return self._new(_ast.Tuple, nodes, _ast.Load())
[944]396
[979]397    def visit_List(self, node):
[975]398        nodes = [self.visit(n) for n in node.nodes]
399        return self._new(_ast.List, nodes, _ast.Load())
[944]400
[975]401    def get_ctx(self, flags):
402        if flags == 'OP_DELETE':
403            return _ast.Del()
404        elif flags == 'OP_APPLY':
405            return _ast.Load()
406        elif flags == 'OP_ASSIGN':
407            return _ast.Store()
408        else:
409            # FIXME Exception here
410            assert False, repr(flags)
[944]411
[979]412    def visit_AssName(self, node):
[975]413        self.out_flags = node.flags
414        ctx = self.get_ctx(node.flags)
415        return self._new(_ast.Name, node.name, ctx)
[944]416
[979]417    def visit_AssAttr(self, node):
[975]418        self.out_flags = node.flags
419        ctx = self.get_ctx(node.flags)
420        return self._new(_ast.Attribute, self.visit(node.expr), 
[977]421                         node.attrname, ctx)
[944]422
[979]423    def _visit_AssCollection(cls):
[975]424        def _visit(self, node):
425            flags = None
426            elts = []
427            for n in node.nodes:
428                elts.append(self.visit(n))
429                if flags is None:
430                    flags = self.out_flags
431                else:
432                    assert flags == self.out_flags
433            self.out_flags = flags
434            ctx = self.get_ctx(flags)
435            return self._new(cls, elts, ctx)
436        return _visit
[944]437
[979]438    visit_AssList = _visit_AssCollection(_ast.List)
439    visit_AssTuple = _visit_AssCollection(_ast.Tuple)
440    del _visit_AssCollection
[944]441
[979]442    def visit_Slice(self, node):
[975]443        lower = self.visit(node.lower)
444        upper = self.visit(node.upper)
445        ctx = self.get_ctx(node.flags)
446        self.out_flags = node.flags
447        return self._new(_ast.Subscript, self.visit(node.expr),
448                    self._new(_ast.Slice, lower, upper, None), ctx)
449
[979]450    def visit_Subscript(self, node):
[975]451        ctx = self.get_ctx(node.flags)
452        subs = [self.visit(s) for s in node.subs]
453
454        advanced = (_ast.Slice, _ast.Ellipsis)
455        slices = []
456        nonindex = False
457        for sub in subs:
458            if isinstance(sub, advanced):
459                nonindex = True
460                slices.append(sub)
[944]461            else:
[975]462                slices.append(self._new(_ast.Index, sub))
463        if len(slices) == 1:
464            slice = slices[0]
465        elif nonindex:
466            slice = self._new(_ast.ExtSlice, slices)
467        else:
468            slice = self._new(_ast.Tuple, slices, _ast.Load())
[944]469
[975]470        self.out_flags = node.flags
471        return self._new(_ast.Subscript, self.visit(node.expr), slice, ctx)
[944]472
[979]473    def visit_Sliceobj(self, node):
[1078]474        a = [self.visit(n) for n in node.nodes + [None]*(3 - len(node.nodes))]
[975]475        return self._new(_ast.Slice, a[0], a[1], a[2])
[944]476
[979]477    def visit_Ellipsis(self, node):
[975]478        return self._new(_ast.Ellipsis)
[944]479
[979]480    def visit_Stmt(self, node):
[975]481        def _check_del(n):
482            # del x is just AssName('x', 'OP_DELETE')
483            # we want to transform it to Delete([Name('x', Del())])
484            dcls = (_ast.Name, _ast.List, _ast.Subscript, _ast.Attribute)
485            if isinstance(n, dcls) and isinstance(n.ctx, _ast.Del):
486                return self._new(_ast.Delete, [n])
487            elif isinstance(n, _ast.Tuple) and isinstance(n.ctx, _ast.Del):
488                # unpack last tuple to avoid making del (x, y, z,);
489                # out of del x, y, z; (there's no difference between
490                # this two in compiler.ast)
491                return self._new(_ast.Delete, n.elts)
492            else:
493                return n
494        def _keep(n):
495            if isinstance(n, _ast.Expr) and n.value is None:
496                return False
497            else:
498                return True
[1078]499        return [s for s in [_check_del(self.visit(n)) for n in node.nodes]
500                if _keep(s)]
[944]501
[977]502
[975]503def parse(source, mode):
504    node = compiler.parse(source, mode)
505    return ASTUpgrader().visit(node)
Note: See TracBrowser for help on using the repository browser.