Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/genshi/template/ast24.py
diff options
context:
space:
mode:
Diffstat (limited to 'genshi/template/ast24.py')
-rw-r--r--genshi/template/ast24.py505
1 files changed, 505 insertions, 0 deletions
diff --git a/genshi/template/ast24.py b/genshi/template/ast24.py
new file mode 100644
index 0000000..af6dce9
--- /dev/null
+++ b/genshi/template/ast24.py
@@ -0,0 +1,505 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Emulation of the proper abstract syntax tree API for Python 2.4."""
+
+import compiler
+import compiler.ast
+
+from genshi.template import _ast24 as _ast
+
+__all__ = ['_ast', 'parse']
+__docformat__ = 'restructuredtext en'
+
+
+def _new(cls, *args, **kwargs):
+ ret = cls()
+ if ret._fields:
+ for attr, value in zip(ret._fields, args):
+ if attr in kwargs:
+ raise ValueError('Field set both in args and kwargs')
+ setattr(ret, attr, value)
+ for attr in kwargs:
+ if (getattr(ret, '_fields', None) and attr in ret._fields) \
+ or (getattr(ret, '_attributes', None) and
+ attr in ret._attributes):
+ setattr(ret, attr, kwargs[attr])
+ return ret
+
+
+class ASTUpgrader(object):
+ """Transformer changing structure of Python 2.4 ASTs to
+ Python 2.5 ones.
+
+ Transforms ``compiler.ast`` Abstract Syntax Tree to builtin ``_ast``.
+ It can use fake`` _ast`` classes and this way allow ``_ast`` emulation
+ in Python 2.4.
+ """
+
+ def __init__(self):
+ self.out_flags = None
+ self.lines = [-1]
+
+ def _new(self, *args, **kwargs):
+ return _new(lineno = self.lines[-1], *args, **kwargs)
+
+ def visit(self, node):
+ if node is None:
+ return None
+ if type(node) is tuple:
+ return tuple([self.visit(n) for n in node])
+ lno = getattr(node, 'lineno', None)
+ if lno is not None:
+ self.lines.append(lno)
+ visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None)
+ if visitor is None:
+ raise Exception('Unhandled node type %r' % type(node))
+
+ retval = visitor(node)
+ if lno is not None:
+ self.lines.pop()
+ return retval
+
+ def visit_Module(self, node):
+ body = self.visit(node.node)
+ if node.doc:
+ body = [self._new(_ast.Expr, self._new(_ast.Str, node.doc))] + body
+ return self._new(_ast.Module, body)
+
+ def visit_Expression(self, node):
+ return self._new(_ast.Expression, self.visit(node.node))
+
+ def _extract_args(self, node):
+ tab = node.argnames[:]
+ if node.flags & compiler.ast.CO_VARKEYWORDS:
+ kwarg = tab[-1]
+ tab = tab[:-1]
+ else:
+ kwarg = None
+
+ if node.flags & compiler.ast.CO_VARARGS:
+ vararg = tab[-1]
+ tab = tab[:-1]
+ else:
+ vararg = None
+
+ def _tup(t):
+ if isinstance(t, str):
+ return self._new(_ast.Name, t, _ast.Store())
+ elif isinstance(t, tuple):
+ elts = [_tup(x) for x in t]
+ return self._new(_ast.Tuple, elts, _ast.Store())
+ else:
+ raise NotImplemented
+
+ args = []
+ for arg in tab:
+ if isinstance(arg, str):
+ args.append(self._new(_ast.Name, arg, _ast.Param()))
+ elif isinstance(arg, tuple):
+ args.append(_tup(arg))
+ else:
+ assert False, node.__class__
+
+ defaults = [self.visit(d) for d in node.defaults]
+ return self._new(_ast.arguments, args, vararg, kwarg, defaults)
+
+
+ def visit_Function(self, node):
+ if getattr(node, 'decorators', ()):
+ decorators = [self.visit(d) for d in node.decorators.nodes]
+ else:
+ decorators = []
+
+ args = self._extract_args(node)
+ body = self.visit(node.code)
+ if node.doc:
+ body = [self._new(_ast.Expr, self._new(_ast.Str, node.doc))] + body
+ return self._new(_ast.FunctionDef, node.name, args, body, decorators)
+
+ def visit_Class(self, node):
+ #self.name_types.append(_ast.Load)
+ bases = [self.visit(b) for b in node.bases]
+ #self.name_types.pop()
+ body = self.visit(node.code)
+ if node.doc:
+ body = [self._new(_ast.Expr, self._new(_ast.Str, node.doc))] + body
+ return self._new(_ast.ClassDef, node.name, bases, body)
+
+ def visit_Return(self, node):
+ return self._new(_ast.Return, self.visit(node.value))
+
+ def visit_Assign(self, node):
+ #self.name_types.append(_ast.Store)
+ targets = [self.visit(t) for t in node.nodes]
+ #self.name_types.pop()
+ return self._new(_ast.Assign, targets, self.visit(node.expr))
+
+ aug_operators = {
+ '+=': _ast.Add,
+ '/=': _ast.Div,
+ '//=': _ast.FloorDiv,
+ '<<=': _ast.LShift,
+ '%=': _ast.Mod,
+ '*=': _ast.Mult,
+ '**=': _ast.Pow,
+ '>>=': _ast.RShift,
+ '-=': _ast.Sub,
+ }
+
+ def visit_AugAssign(self, node):
+ target = self.visit(node.node)
+
+ # Because it's AugAssign target can't be list nor tuple
+ # so we only have to change context of one node
+ target.ctx = _ast.Store()
+ op = self.aug_operators[node.op]()
+ return self._new(_ast.AugAssign, target, op, self.visit(node.expr))
+
+ def _visit_Print(nl):
+ def _visit(self, node):
+ values = [self.visit(v) for v in node.nodes]
+ return self._new(_ast.Print, self.visit(node.dest), values, nl)
+ return _visit
+
+ visit_Print = _visit_Print(False)
+ visit_Printnl = _visit_Print(True)
+ del _visit_Print
+
+ def visit_For(self, node):
+ return self._new(_ast.For, self.visit(node.assign), self.visit(node.list),
+ self.visit(node.body), self.visit(node.else_))
+
+ def visit_While(self, node):
+ return self._new(_ast.While, self.visit(node.test), self.visit(node.body),
+ self.visit(node.else_))
+
+ def visit_If(self, node):
+ def _level(tests, else_):
+ test = self.visit(tests[0][0])
+ body = self.visit(tests[0][1])
+ if len(tests) == 1:
+ orelse = self.visit(else_)
+ else:
+ orelse = [_level(tests[1:], else_)]
+ return self._new(_ast.If, test, body, orelse)
+ return _level(node.tests, node.else_)
+
+ def visit_With(self, node):
+ return self._new(_ast.With, self.visit(node.expr),
+ self.visit(node.vars), self.visit(node.body))
+
+ def visit_Raise(self, node):
+ return self._new(_ast.Raise, self.visit(node.expr1),
+ self.visit(node.expr2), self.visit(node.expr3))
+
+ def visit_TryExcept(self, node):
+ handlers = []
+ for type, name, body in node.handlers:
+ handlers.append(self._new(_ast.excepthandler, self.visit(type),
+ self.visit(name), self.visit(body)))
+ return self._new(_ast.TryExcept, self.visit(node.body),
+ handlers, self.visit(node.else_))
+
+ def visit_TryFinally(self, node):
+ return self._new(_ast.TryFinally, self.visit(node.body),
+ self.visit(node.final))
+
+ def visit_Assert(self, node):
+ return self._new(_ast.Assert, self.visit(node.test), self.visit(node.fail))
+
+ def visit_Import(self, node):
+ names = [self._new(_ast.alias, n[0], n[1]) for n in node.names]
+ return self._new(_ast.Import, names)
+
+ def visit_From(self, node):
+ names = [self._new(_ast.alias, n[0], n[1]) for n in node.names]
+ return self._new(_ast.ImportFrom, node.modname, names, 0)
+
+ def visit_Exec(self, node):
+ return self._new(_ast.Exec, self.visit(node.expr),
+ self.visit(node.locals), self.visit(node.globals))
+
+ def visit_Global(self, node):
+ return self._new(_ast.Global, node.names[:])
+
+ def visit_Discard(self, node):
+ return self._new(_ast.Expr, self.visit(node.expr))
+
+ def _map_class(to):
+ def _visit(self, node):
+ return self._new(to)
+ return _visit
+
+ visit_Pass = _map_class(_ast.Pass)
+ visit_Break = _map_class(_ast.Break)
+ visit_Continue = _map_class(_ast.Continue)
+
+ def _visit_BinOperator(opcls):
+ def _visit(self, node):
+ return self._new(_ast.BinOp, self.visit(node.left),
+ opcls(), self.visit(node.right))
+ return _visit
+ visit_Add = _visit_BinOperator(_ast.Add)
+ visit_Div = _visit_BinOperator(_ast.Div)
+ visit_FloorDiv = _visit_BinOperator(_ast.FloorDiv)
+ visit_LeftShift = _visit_BinOperator(_ast.LShift)
+ visit_Mod = _visit_BinOperator(_ast.Mod)
+ visit_Mul = _visit_BinOperator(_ast.Mult)
+ visit_Power = _visit_BinOperator(_ast.Pow)
+ visit_RightShift = _visit_BinOperator(_ast.RShift)
+ visit_Sub = _visit_BinOperator(_ast.Sub)
+ del _visit_BinOperator
+
+ def _visit_BitOperator(opcls):
+ def _visit(self, node):
+ def _make(nodes):
+ if len(nodes) == 1:
+ return self.visit(nodes[0])
+ left = _make(nodes[:-1])
+ right = self.visit(nodes[-1])
+ return self._new(_ast.BinOp, left, opcls(), right)
+ return _make(node.nodes)
+ return _visit
+ visit_Bitand = _visit_BitOperator(_ast.BitAnd)
+ visit_Bitor = _visit_BitOperator(_ast.BitOr)
+ visit_Bitxor = _visit_BitOperator(_ast.BitXor)
+ del _visit_BitOperator
+
+ def _visit_UnaryOperator(opcls):
+ def _visit(self, node):
+ return self._new(_ast.UnaryOp, opcls(), self.visit(node.expr))
+ return _visit
+
+ visit_Invert = _visit_UnaryOperator(_ast.Invert)
+ visit_Not = _visit_UnaryOperator(_ast.Not)
+ visit_UnaryAdd = _visit_UnaryOperator(_ast.UAdd)
+ visit_UnarySub = _visit_UnaryOperator(_ast.USub)
+ del _visit_UnaryOperator
+
+ def _visit_BoolOperator(opcls):
+ def _visit(self, node):
+ values = [self.visit(n) for n in node.nodes]
+ return self._new(_ast.BoolOp, opcls(), values)
+ return _visit
+ visit_And = _visit_BoolOperator(_ast.And)
+ visit_Or = _visit_BoolOperator(_ast.Or)
+ del _visit_BoolOperator
+
+ cmp_operators = {
+ '==': _ast.Eq,
+ '!=': _ast.NotEq,
+ '<': _ast.Lt,
+ '<=': _ast.LtE,
+ '>': _ast.Gt,
+ '>=': _ast.GtE,
+ 'is': _ast.Is,
+ 'is not': _ast.IsNot,
+ 'in': _ast.In,
+ 'not in': _ast.NotIn,
+ }
+
+ def visit_Compare(self, node):
+ left = self.visit(node.expr)
+ ops = []
+ comparators = []
+ for optype, expr in node.ops:
+ ops.append(self.cmp_operators[optype]())
+ comparators.append(self.visit(expr))
+ return self._new(_ast.Compare, left, ops, comparators)
+
+ def visit_Lambda(self, node):
+ args = self._extract_args(node)
+ body = self.visit(node.code)
+ return self._new(_ast.Lambda, args, body)
+
+ def visit_IfExp(self, node):
+ return self._new(_ast.IfExp, self.visit(node.test), self.visit(node.then),
+ self.visit(node.else_))
+
+ def visit_Dict(self, node):
+ keys = [self.visit(x[0]) for x in node.items]
+ values = [self.visit(x[1]) for x in node.items]
+ return self._new(_ast.Dict, keys, values)
+
+ def visit_ListComp(self, node):
+ generators = [self.visit(q) for q in node.quals]
+ return self._new(_ast.ListComp, self.visit(node.expr), generators)
+
+ def visit_GenExprInner(self, node):
+ generators = [self.visit(q) for q in node.quals]
+ return self._new(_ast.GeneratorExp, self.visit(node.expr), generators)
+
+ def visit_GenExpr(self, node):
+ return self.visit(node.code)
+
+ def visit_GenExprFor(self, node):
+ ifs = [self.visit(i) for i in node.ifs]
+ return self._new(_ast.comprehension, self.visit(node.assign),
+ self.visit(node.iter), ifs)
+
+ def visit_ListCompFor(self, node):
+ ifs = [self.visit(i) for i in node.ifs]
+ return self._new(_ast.comprehension, self.visit(node.assign),
+ self.visit(node.list), ifs)
+
+ def visit_GenExprIf(self, node):
+ return self.visit(node.test)
+ visit_ListCompIf = visit_GenExprIf
+
+ def visit_Yield(self, node):
+ return self._new(_ast.Yield, self.visit(node.value))
+
+ def visit_CallFunc(self, node):
+ args = []
+ keywords = []
+ for arg in node.args:
+ if isinstance(arg, compiler.ast.Keyword):
+ keywords.append(self._new(_ast.keyword, arg.name,
+ self.visit(arg.expr)))
+ else:
+ args.append(self.visit(arg))
+ return self._new(_ast.Call, self.visit(node.node), args, keywords,
+ self.visit(node.star_args), self.visit(node.dstar_args))
+
+ def visit_Backquote(self, node):
+ return self._new(_ast.Repr, self.visit(node.expr))
+
+ def visit_Const(self, node):
+ if node.value is None: # appears in slices
+ return None
+ elif isinstance(node.value, basestring):
+ return self._new(_ast.Str, node.value)
+ else:
+ return self._new(_ast.Num, node.value)
+
+ def visit_Name(self, node):
+ return self._new(_ast.Name, node.name, _ast.Load())
+
+ def visit_Getattr(self, node):
+ return self._new(_ast.Attribute, self.visit(node.expr), node.attrname,
+ _ast.Load())
+
+ def visit_Tuple(self, node):
+ nodes = [self.visit(n) for n in node.nodes]
+ return self._new(_ast.Tuple, nodes, _ast.Load())
+
+ def visit_List(self, node):
+ nodes = [self.visit(n) for n in node.nodes]
+ return self._new(_ast.List, nodes, _ast.Load())
+
+ def get_ctx(self, flags):
+ if flags == 'OP_DELETE':
+ return _ast.Del()
+ elif flags == 'OP_APPLY':
+ return _ast.Load()
+ elif flags == 'OP_ASSIGN':
+ return _ast.Store()
+ else:
+ # FIXME Exception here
+ assert False, repr(flags)
+
+ def visit_AssName(self, node):
+ self.out_flags = node.flags
+ ctx = self.get_ctx(node.flags)
+ return self._new(_ast.Name, node.name, ctx)
+
+ def visit_AssAttr(self, node):
+ self.out_flags = node.flags
+ ctx = self.get_ctx(node.flags)
+ return self._new(_ast.Attribute, self.visit(node.expr),
+ node.attrname, ctx)
+
+ def _visit_AssCollection(cls):
+ def _visit(self, node):
+ flags = None
+ elts = []
+ for n in node.nodes:
+ elts.append(self.visit(n))
+ if flags is None:
+ flags = self.out_flags
+ else:
+ assert flags == self.out_flags
+ self.out_flags = flags
+ ctx = self.get_ctx(flags)
+ return self._new(cls, elts, ctx)
+ return _visit
+
+ visit_AssList = _visit_AssCollection(_ast.List)
+ visit_AssTuple = _visit_AssCollection(_ast.Tuple)
+ del _visit_AssCollection
+
+ def visit_Slice(self, node):
+ lower = self.visit(node.lower)
+ upper = self.visit(node.upper)
+ ctx = self.get_ctx(node.flags)
+ self.out_flags = node.flags
+ return self._new(_ast.Subscript, self.visit(node.expr),
+ self._new(_ast.Slice, lower, upper, None), ctx)
+
+ def visit_Subscript(self, node):
+ ctx = self.get_ctx(node.flags)
+ subs = [self.visit(s) for s in node.subs]
+
+ advanced = (_ast.Slice, _ast.Ellipsis)
+ slices = []
+ nonindex = False
+ for sub in subs:
+ if isinstance(sub, advanced):
+ nonindex = True
+ slices.append(sub)
+ else:
+ slices.append(self._new(_ast.Index, sub))
+ if len(slices) == 1:
+ slice = slices[0]
+ elif nonindex:
+ slice = self._new(_ast.ExtSlice, slices)
+ else:
+ slice = self._new(_ast.Tuple, slices, _ast.Load())
+
+ self.out_flags = node.flags
+ return self._new(_ast.Subscript, self.visit(node.expr), slice, ctx)
+
+ def visit_Sliceobj(self, node):
+ a = [self.visit(n) for n in node.nodes + [None]*(3 - len(node.nodes))]
+ return self._new(_ast.Slice, a[0], a[1], a[2])
+
+ def visit_Ellipsis(self, node):
+ return self._new(_ast.Ellipsis)
+
+ def visit_Stmt(self, node):
+ def _check_del(n):
+ # del x is just AssName('x', 'OP_DELETE')
+ # we want to transform it to Delete([Name('x', Del())])
+ dcls = (_ast.Name, _ast.List, _ast.Subscript, _ast.Attribute)
+ if isinstance(n, dcls) and isinstance(n.ctx, _ast.Del):
+ return self._new(_ast.Delete, [n])
+ elif isinstance(n, _ast.Tuple) and isinstance(n.ctx, _ast.Del):
+ # unpack last tuple to avoid making del (x, y, z,);
+ # out of del x, y, z; (there's no difference between
+ # this two in compiler.ast)
+ return self._new(_ast.Delete, n.elts)
+ else:
+ return n
+ def _keep(n):
+ if isinstance(n, _ast.Expr) and n.value is None:
+ return False
+ else:
+ return True
+ return [s for s in [_check_del(self.visit(n)) for n in node.nodes]
+ if _keep(s)]
+
+
+def parse(source, mode):
+ node = compiler.parse(source, mode)
+ return ASTUpgrader().visit(node)