summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--__init__.py24
-rw-r--r--_inference_compiler.py158
-rw-r--r--_nodes_ast.py148
-rw-r--r--_nodes_compiler.py212
-rw-r--r--builder.py120
-rw-r--r--inference.py181
-rw-r--r--lookup.py8
-rw-r--r--nodes.py164
-rw-r--r--patchcomptransformer.py123
-rw-r--r--raw_building.py46
-rw-r--r--scoped_nodes.py11
-rw-r--r--test/unittest_builder.py8
-rw-r--r--utils.py24
13 files changed, 739 insertions, 488 deletions
diff --git a/__init__.py b/__init__.py
index 38e69022..b1d27237 100644
--- a/__init__.py
+++ b/__init__.py
@@ -131,6 +131,30 @@ def _infer_stmts(stmts, context, frame=None):
if not infered:
raise InferenceError(str(stmt))
+def path_wrapper(func):
+ """return the given infer function wrapped to handle the path"""
+ def wrapped(node, context=None, _func=func, **kwargs):
+ """wrapper function handling context"""
+ if context is None:
+ context = InferenceContext(node)
+ context.push(node)
+ yielded = set()
+ try:
+ for res in _func(node, context, **kwargs):
+ # unproxy only true instance, not const, tuple, dict...
+ if res.__class__ is Instance:
+ ares = res._proxied
+ else:
+ ares = res
+ if not ares in yielded:
+ yield res
+ yielded.add(ares)
+ context.pop()
+ except:
+ context.pop()
+ raise
+ return wrapped
+
# special inference objects ###################################################
class Yes(object):
diff --git a/_inference_compiler.py b/_inference_compiler.py
new file mode 100644
index 00000000..07cb86ac
--- /dev/null
+++ b/_inference_compiler.py
@@ -0,0 +1,158 @@
+
+from logilab.astng import MANAGER, YES, ASTNGError, _infer_stmts, path_wrapper
+from logilab.astng import nodes
+from logilab.astng.utils import infer_end, end_ass_type
+
+
+nodes.Const.infer = infer_end
+
+def infer_empty_node(self, context=None):
+ if not self.has_underlying_object():
+ yield YES
+ else:
+ try:
+ for infered in MANAGER.infer_astng_from_something(self.object,
+ context=context):
+ yield infered
+ except ASTNGError:
+ yield YES
+nodes.EmptyNode.infer = path_wrapper(infer_empty_node)
+
+
+def infer_assname(self, context=None):
+ """infer a AssName/AssAttr: need to inspect the RHS part of the
+ assign node
+ """
+ stmts = self.assigned_stmts(context=context)
+ return _infer_stmts(stmts, context)
+nodes.AssName.infer = path_wrapper(infer_assname)
+
+
+def infer_assattr(self, context=None):
+ """infer a AssName/AssAttr: need to inspect the RHS part of the
+ assign node
+ """
+ stmts = self.assigned_stmts(context=context)
+ return _infer_stmts(stmts, context)
+nodes.AssAttr.infer = path_wrapper(infer_assattr)
+
+
+def infer_getattr(self, context=None):
+ """infer a Getattr node by using getattr on the associated object
+ """
+ one_infered = False
+ # XXX
+ #context = context.clone()
+ for owner in self.expr.infer(context):
+ if owner is YES:
+ yield owner
+ one_infered = True
+ continue
+ try:
+ context.boundnode = owner
+ for obj in owner.igetattr(self.attrname, context):
+ yield obj
+ one_infered = True
+ context.boundnode = None
+ except (NotFoundError, InferenceError):
+ continue
+ except AttributeError:
+ # XXX method / function
+ continue
+ if not one_infered:
+ raise InferenceError()
+nodes.Getattr.infer = path_wrapper(infer_getattr)
+
+
+def assend_assigned_stmts(self, context=None):
+ # only infer *real* assignments
+ if self.flags == 'OP_DELETE':
+ raise InferenceError()
+ return self.parent.assigned_stmts(self, context=context)
+nodes.AssName.assigned_stmts = assend_assigned_stmts
+nodes.AssAttr.assigned_stmts = assend_assigned_stmts
+
+
+def mulass_assigned_stmts(self, node, context=None, asspath=None):
+ if asspath is None:
+ asspath = []
+ node_idx = self.nodes.index(node)
+ asspath.insert(0, node_idx)
+ return self.parent.assigned_stmts(self, context, asspath)
+nodes.AssTuple.assigned_stmts = mulass_assigned_stmts
+nodes.AssList.assigned_stmts = mulass_assigned_stmts
+
+
+def _resolve_looppart(parts, asspath, context):
+ """recursive function to resolve multiple assignments on loops"""
+ asspath = asspath[:]
+ index = asspath.pop(0)
+ for part in parts:
+ if part is YES:
+ continue
+ if not hasattr(part, 'iter_stmts'):
+ continue
+ for stmt in part.iter_stmts():
+ try:
+ assigned = stmt.getitem(index)
+ except (AttributeError, IndexError):
+ continue
+ if not asspath:
+ # we acheived to resolved the assigment path,
+ # don't infer the last part
+ found = True
+ yield assigned
+ elif assigned is YES:
+ break
+ else:
+ # we are not yet on the last part of the path
+ # search on each possibly infered value
+ try:
+ for infered in _resolve_looppart(assigned.infer(context), asspath, context):
+ yield infered
+ except InferenceError:
+ break
+
+def for_assigned_stmts(self, node, context=None, asspath=None):
+ found = False
+ if asspath is None:
+ for lst in self.loop_node().infer(context):
+ if isinstance(lst, (nodes.Tuple, nodes.List)):
+ for item in lst.nodes:
+ found = True
+ yield item
+ else:
+ for infered in _resolve_looppart(self.loop_node().infer(context), asspath, context):
+ found = True
+ yield infered
+ if not found:
+ raise InferenceError()
+nodes.For.assigned_stmts = for_assigned_stmts
+nodes.ListCompFor.assigned_stmts = for_assigned_stmts
+nodes.GenExprFor.assigned_stmts = for_assigned_stmts
+
+nodes.ListCompFor.ass_type = end_ass_type
+nodes.GenExprFor.ass_type = end_ass_type
+def parent_ass_type(self):
+ return self.parent.ass_type()
+nodes.AssName.ass_type = parent_ass_type
+nodes.AssAttr.ass_type = parent_ass_type
+nodes.AssTuple.ass_type = parent_ass_type
+nodes.AssList.ass_type = parent_ass_type
+def assend_ass_type(self, context=None):
+ # only infer *real* assignments
+ if self.flags == 'OP_DELETE':
+ return self
+ return self.parent.ass_type()
+nodes.AssName.ass_type = assend_ass_type
+nodes.AssAttr.ass_type = assend_ass_type
+
+
+
+def for_loop_node(self):
+ return self.list
+nodes.ListCompFor.loop_node = for_loop_node
+
+def gen_loop_nodes(self):
+ return self.iter
+nodes.GenExprFor.loop_node = gen_loop_nodes
diff --git a/_nodes_ast.py b/_nodes_ast.py
new file mode 100644
index 00000000..3747f0ec
--- /dev/null
+++ b/_nodes_ast.py
@@ -0,0 +1,148 @@
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""python 2.5 builtin _ast compatibility module
+
+:author: Sylvain Thenault
+:copyright: 2008 LOGILAB S.A. (Paris, FRANCE)
+:contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
+:copyright: 2008 Sylvain Thenault
+:contact: mailto:thenault@gmail.com
+"""
+
+__docformat__ = "restructuredtext en"
+
+from logilab.astng.utils import infer_end
+
+from _ast import (Add, And, Assert, Assign, AugAssign,
+ Break,
+ Compare, Continue,
+ Dict, Div,
+ Ellipsis, Exec,
+ FloorDiv, For,
+ Global,
+ If, Import, Invert,
+ Lambda, List, ListComp,
+ Mod, Module,
+ Name, Not,
+ Or,
+ Pass, Print,
+ Raise, Return,
+ Slice, Sub, Subscript,
+ TryExcept, TryFinally, Tuple,
+ While, With,
+ Yield,
+ )
+
+from _ast import (AST as Node,
+ BitAnd as Bitand, BitOr as Bitor, BitXor as Bitxor,
+ Call as CallFunc,
+ ClassDef as Class,
+ FunctionDef as Function,
+ GeneratorExp as GenExpr,
+ ImportFrom as From,
+ LShift as LeftShift,
+ Mult as Mul,
+ Repr as Backquote,
+ Pow as Power,
+ RShift as RightShift,
+ UAdd as UnaryAdd,
+ USub as UnarySub,
+ )
+# XXX : AugLoad, AugStore, Attribute
+# BinOp, BoolOp
+# Del, Delete
+# Eq, Expr, Expression, ExtSlice
+# Gt, GtE
+# IfExp, In, Index, Interactive, Is, IsNot
+# Load, Lt, LtE
+# NotEq, NotIn, Num
+# Param
+# Store, Str, Suite
+# UnaryOp
+from _ast import Num, Str, Expr, alias
+Const = (Num, Str)
+
+class EmptyNode(Node): pass
+
+# scoped nodes ################################################################
+
+def module_append_node(self, child_node):
+ """append a child version specific to Module node"""
+ self.body.append(child_node)
+ child_node.parent = self
+Module._append_node = module_append_node
+
+def _append_node(self, child_node):
+ """append a child, linking it in the tree"""
+ # XXX
+ self.code.nodes.append(child_node)
+ child_node.parent = self
+Class._append_node = _append_node
+Function._append_node = _append_node
+
+# inferences ##################################################################
+
+from logilab.astng.utils import infer_end
+
+Num.infer = infer_end
+Str.infer = infer_end
+
+# raw building ################################################################
+
+def module_factory(doc):
+ node = Module()
+ node.body = []
+ if doc:
+ expr = Expr()
+ node.body.append(expr)
+ expr.parent = None
+ docstr = Str()
+ docstr.s = doc
+ expr.value = docstr
+ docstr.parent = expr
+ return node
+
+def dict_factory():
+ return Dict()
+
+def import_from_factory(modname, membername):
+ node = From()
+ node.level = 0
+ node.module = modname
+ aliasnode = alias()
+ aliasnode.parent = node
+ aliasnode.name = membername
+ aliasnode.asname = None
+ node.names = [aliasnode]
+ return node
+
+def const_factory(value):
+ if value is None:
+ node = Name()
+ node.id = 'None'
+ elif value is True:
+ node = Name()
+ node.id = 'True'
+ elif value is False:
+ node = Name()
+ node.id = 'False'
+ elif isinstance(value, (int, long, complex)):
+ node = Num()
+ node.n = value
+ elif isinstance(value, basestring):
+ node = Str()
+ node.s = value
+ else:
+ raise Exception(repr(value))
+ return node
+
diff --git a/_nodes_compiler.py b/_nodes_compiler.py
new file mode 100644
index 00000000..93765e9d
--- /dev/null
+++ b/_nodes_compiler.py
@@ -0,0 +1,212 @@
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""python < 2.5 compiler package compatibility module
+
+:author: Sylvain Thenault
+:copyright: 2008 LOGILAB S.A. (Paris, FRANCE)
+:contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
+:copyright: 2008 Sylvain Thenault
+:contact: mailto:thenault@gmail.com
+"""
+
+__docformat__ = "restructuredtext en"
+
+from __future__ import generators
+
+import sys
+from compiler.ast import Add, And, AssAttr, AssList, AssName, \
+ AssTuple, Assert, Assign, AugAssign, \
+ Backquote, Bitand, Bitor, Bitxor, Break, CallFunc, Class, \
+ Compare, Const, Continue, Dict, Discard, Div, \
+ Ellipsis, EmptyNode, Exec, FloorDiv, \
+ For, From, Function, Getattr, Global, \
+ If, Import, Invert, Keyword, Lambda, LeftShift, \
+ List, ListComp, ListCompFor, ListCompIf, Mod, Module, Mul, Name, Node, \
+ Not, Or, Pass, Power, Print, Printnl, Raise, Return, RightShift, Slice, \
+ Sliceobj, Stmt, Sub, Subscript, TryExcept, TryFinally, Tuple, UnaryAdd, \
+ UnarySub, While, Yield
+try:
+ # introduced in python 2.4
+ from compiler.ast import GenExpr, GenExprFor, GenExprIf, GenExprInner
+except:
+ class GenExpr:
+ """dummy GenExpr node, shouldn't be used with py < 2.4"""
+ class GenExprFor:
+ """dummy GenExprFor node, shouldn't be used with py < 2.4"""
+ class GenExprIf:
+ """dummy GenExprIf node, shouldn't be used with py < 2.4"""
+ class GenExprInner:
+ """dummy GenExprInner node, shouldn't be used with py < 2.4"""
+try:
+ # introduced in python 2.4
+ from compiler.ast import Decorators
+except:
+ class Decorators:
+ """dummy Decorators node, shouldn't be used with py < 2.4"""
+
+try:
+ # introduced in python 2.5
+ from compiler.ast import With
+except:
+ class With:
+ """dummy With node, shouldn't be used since py < 2.5"""
+
+
+def assattr_as_string(node):
+ """return an ast.AssAttr node as string"""
+ if node.flags == 'OP_DELETE':
+ return 'del %s.%s' % (node.expr.as_string(), node.attrname)
+ return '%s.%s' % (node.expr.as_string(), node.attrname)
+AssAttr.as_string = assattr_as_string
+
+def asslist_as_string(node):
+ """return an ast.AssList node as string"""
+ string = ', '.join([n.as_string() for n in node.nodes])
+ return '[%s]' % string
+AssList.as_string = asslist_as_string
+
+def assname_as_string(node):
+ """return an ast.AssName node as string"""
+ if node.flags == 'OP_DELETE':
+ return 'del %s' % node.name
+ return node.name
+AssName.as_string = assname_as_string
+
+def asstuple_as_string(node):
+ """return an ast.AssTuple node as string"""
+ string = ', '.join([n.as_string() for n in node.nodes])
+ # fix for del statement
+ return string.replace(', del ', ', ')
+AssTuple.as_string = asstuple_as_string
+
+Const.eq = lambda self, value: self.value == value
+
+def const_as_string(node):
+ """return an ast.Const node as string"""
+ return repr(node.value)
+Const.as_string = const_as_string
+
+def decorators_scope(self):
+ # skip the function node to go directly to the upper level scope
+ return self.parent.parent.scope()
+Decorators.scope = decorators_scope
+
+def discard_as_string(node):
+ """return an ast.Discard node as string"""
+ return node.expr.as_string()
+Discard.as_string = discard_as_string
+
+def empty_as_string(node):
+ return ''
+EmptyNode.as_string = empty_as_string
+
+EmptyNode.getChildNodes = lambda self: ()
+
+# introduced in python 2.5
+From.level = 0 # will be overiden by instance attribute with py>=2.5
+
+def genexprinner_as_string(node):
+ """return an ast.GenExpr node as string"""
+ return '%s %s' % (node.expr.as_string(), ' '.join([n.as_string()
+ for n in node.quals]))
+GenExprInner.as_string = genexprinner_as_string
+
+def genexprfor_as_string(node):
+ """return an ast.GenExprFor node as string"""
+ return 'for %s in %s %s' % (node.assign.as_string(),
+ node.iter.as_string(),
+ ' '.join([n.as_string() for n in node.ifs]))
+GenExprFor.as_string = genexprfor_as_string
+
+def genexprif_as_string(node):
+ """return an ast.GenExprIf node as string"""
+ return 'if %s' % node.test.as_string()
+GenExprIf.as_string = genexprif_as_string
+
+def getattr_as_string(node):
+ """return an ast.Getattr node as string"""
+ return '%s.%s' % (node.expr.as_string(), node.attrname)
+Getattr.as_string = getattr_as_string
+
+def keyword_as_string(node):
+ """return an ast.Keyword node as string"""
+ return '%s=%s' % (node.name, node.expr.as_string())
+Keyword.as_string = keyword_as_string
+
+def listcompfor_as_string(node):
+ """return an ast.ListCompFor node as string"""
+ return 'for %s in %s %s' % (node.assign.as_string(),
+ node.list.as_string(),
+ ' '.join([n.as_string() for n in node.ifs]))
+ListCompFor.as_string = listcompfor_as_string
+
+def listcompif_as_string(node):
+ """return an ast.ListCompIf node as string"""
+ return 'if %s' % node.test.as_string()
+ListCompIf.as_string = listcompif_as_string
+
+def printnl_as_string(node):
+ """return an ast.Printnl node as string"""
+ nodes = ', '.join([n.as_string() for n in node.nodes])
+ if node.dest:
+ return 'print >> %s, %s' % (node.dest.as_string(), nodes)
+ return 'print %s' % nodes
+Printnl.as_string = printnl_as_string
+
+def sliceobj_as_string(node):
+ """return an ast.Sliceobj node as string"""
+ return ':'.join([n.as_string() for n in node.nodes])
+Sliceobj.as_string = sliceobj_as_string
+
+def stmt_as_string(node):
+ """return an ast.Stmt node as string"""
+ stmts = '\n'.join([n.as_string() for n in node.nodes])
+ if isinstance(node.parent, Module):
+ return stmts
+ return stmts.replace('\n', '\n ')
+Stmt.as_string = stmt_as_string
+
+# scoped nodes ################################################################
+
+def module_append_node(self, child_node):
+ """append a child version specific to Module node"""
+ self.node.nodes.append(child_node)
+ child_node.parent = self
+Module._append_node = module_append_node
+
+def _append_node(self, child_node):
+ """append a child, linking it in the tree"""
+ self.code.nodes.append(child_node)
+ child_node.parent = self
+Class._append_node = _append_node
+Function._append_node = _append_node
+
+# raw building ################################################################
+
+def module_factory(doc):
+ node = Module(doc, Stmt([]))
+ node.node.parent = node
+ return node
+
+def dict_factory():
+ return Dict([])
+
+if sys.version_info < (2, 5):
+ def import_from_factory(modname, membername):
+ return From(modname, ( (membername, None), ) )
+else:
+ def import_from_factory(modname, membername):
+ return From(modname, ( (membername, None), ), 0)
+
+def const_factory(value):
+ return Const(value)
diff --git a/builder.py b/builder.py
index 6b4a2089..0069d5c8 100644
--- a/builder.py
+++ b/builder.py
@@ -12,13 +12,11 @@
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""The ASTNGBuilder makes astng from living object and / or from compiler.ast
+With python >= 2.5, the internal _ast module is used instead
+
The builder is not thread safe and can't be used to parse different sources
at the same time.
-TODO:
- - more complet representation on inspect build
- (imported modules ? use dis.dis ?)
-
:author: Sylvain Thenault
:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE)
@@ -31,8 +29,6 @@ __docformat__ = "restructuredtext en"
import sys
from os.path import splitext, basename, dirname, exists, abspath
-from parser import ParserError
-from compiler import parse
from inspect import isfunction, ismethod, ismethoddescriptor, isclass, \
isbuiltin
from inspect import isdatadescriptor
@@ -44,108 +40,15 @@ from logilab.astng import nodes, YES, Instance
from logilab.astng.utils import ASTWalker
from logilab.astng._exceptions import ASTNGBuildingException, InferenceError
from logilab.astng.raw_building import *
-from logilab.astng.astutils import cvrtr
-
-import token
-from compiler import transformer, consts
-from types import TupleType
-
-def fromto_lineno(asttuple):
- """return the minimum and maximum line number of the given ast tuple"""
- return from_lineno(asttuple), to_lineno(asttuple)
-def from_lineno(asttuple):
- """return the minimum line number of the given ast tuple"""
- if type(asttuple[1]) is TupleType:
- return from_lineno(asttuple[1])
- return asttuple[2]
-def to_lineno(asttuple):
- """return the maximum line number of the given ast tuple"""
- if type(asttuple[-1]) is TupleType:
- return to_lineno(asttuple[-1])
- return asttuple[2]
-
-def fix_lineno(node, fromast, toast=None):
- if 'fromlineno' in node.__dict__:
- return node
- #print 'fixing', id(node), id(node.__dict__), node.__dict__.keys(), repr(node)
- if isinstance(node, nodes.Stmt):
- node.fromlineno = from_lineno(fromast)#node.nodes[0].fromlineno
- node.tolineno = node.nodes[-1].tolineno
- return node
- if toast is None:
- node.fromlineno, node.tolineno = fromto_lineno(fromast)
- else:
- node.fromlineno, node.tolineno = from_lineno(fromast), to_lineno(toast)
- #print 'fixed', id(node)
- return node
-BaseTransformer = transformer.Transformer
-
-COORD_MAP = {
- # if: test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
- 'if': (0, 0),
- # 'while' test ':' suite ['else' ':' suite]
- 'while': (0, 1),
- # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite]
- 'for': (0, 3),
- # 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
- 'try': (0, 0),
- # | 'try' ':' suite 'finally' ':' suite
-
- }
-
-def fixlineno_wrap(function, stype):
- def fixlineno_wrapper(self, nodelist):
- node = function(self, nodelist)
- idx1, idx2 = COORD_MAP.get(stype, (0, -1))
- return fix_lineno(node, nodelist[idx1], nodelist[idx2])
- return fixlineno_wrapper
-nodes.Module.fromlineno = 0
-nodes.Module.tolineno = 0
-class ASTNGTransformer(BaseTransformer):
- """ovverides transformer for a better source line number handling"""
- def com_NEWLINE(self, *args):
- # A ';' at the end of a line can make a NEWLINE token appear
- # here, Render it harmless. (genc discards ('discard',
- # ('const', xxxx)) Nodes)
- lineno = args[0][1]
- # don't put fromlineno/tolineno on Const None to mark it as dynamically
- # added, without "physical" reference in the source
- n = nodes.Discard(nodes.Const(None))
- n.fromlineno = n.tolineno = lineno
- return n
- def com_node(self, node):
- res = self._dispatch[node[0]](node[1:])
- return fix_lineno(res, node)
- def com_assign(self, node, assigning):
- res = BaseTransformer.com_assign(self, node, assigning)
- return fix_lineno(res, node)
- def com_apply_trailer(self, primaryNode, nodelist):
- node = BaseTransformer.com_apply_trailer(self, primaryNode, nodelist)
- return fix_lineno(node, nodelist)
-
-## def atom(self, nodelist):
-## node = BaseTransformer.atom(self, nodelist)
-## return fix_lineno(node, nodelist[0], nodelist[-1])
+try:
+ from _ast import PyCF_ONLY_AST
+ def parse(string):
+ return compile(string, "<string>", 'exec', PyCF_ONLY_AST)
+except:
+ from compiler import parse
+ from logilab.astng import patchcomptransformer
- def funcdef(self, nodelist):
- node = BaseTransformer.funcdef(self, nodelist)
- # XXX decorators
- return fix_lineno(node, nodelist[-5], nodelist[-3])
- def classdef(self, nodelist):
- node = BaseTransformer.classdef(self, nodelist)
- return fix_lineno(node, nodelist[0], nodelist[-2])
-
-# wrap *_stmt methods
-for name in dir(BaseTransformer):
- if name.endswith('_stmt') and not (name in ('com_stmt',
- 'com_append_stmt')
- or name in ASTNGTransformer.__dict__):
- setattr(BaseTransformer, name,
- fixlineno_wrap(getattr(BaseTransformer, name), name[:-5]))
-
-transformer.Transformer = ASTNGTransformer
-
# ast NG builder ##############################################################
class ASTNGBuilder:
@@ -590,8 +493,3 @@ def imported_member(node, member, name):
else:
attach_import_node(node, member_module, name)
-# optimize the tokenize module
-#from logilab.common.bind import optimize_module
-#import tokenize
-#optimize_module(sys.modules['tokenize'], tokenize.__dict__)
-#optimize_module(sys.modules[__name__], sys.modules[__name__].__dict__)
diff --git a/inference.py b/inference.py
index 024bde55..fba9cde3 100644
--- a/inference.py
+++ b/inference.py
@@ -28,34 +28,11 @@ from copy import copy
from logilab.common.compat import imap, chain, set
from logilab.astng import MANAGER, YES, InferenceContext, Instance, Generator, \
- unpack_infer, _infer_stmts, nodes, copy_context
+ unpack_infer, _infer_stmts, nodes, copy_context, path_wrapper
from logilab.astng import ASTNGError, InferenceError, UnresolvableName, \
NoDefault, NotFoundError, ASTNGBuildingException
-
+from logilab.astng.utils import infer_end, end_ass_type
-def path_wrapper(func):
- """return the given infer function wrapped to handle the path"""
- def wrapped(node, context=None, _func=func, **kwargs):
- """wrapper function handling context"""
- if context is None:
- context = InferenceContext(node)
- context.push(node)
- yielded = set()
- try:
- for res in _func(node, context, **kwargs):
- # unproxy only true instance, not const, tuple, dict...
- if res.__class__ is Instance:
- ares = res._proxied
- else:
- ares = res
- if not ares in yielded:
- yield res
- yielded.add(ares)
- context.pop()
- except:
- context.pop()
- raise
- return wrapped
# .infer method ###############################################################
@@ -67,31 +44,11 @@ def infer_default(self, context=None):
#infer_default = infer_default
nodes.Node.infer = infer_default
-
-def infer_end(self, context=None):
- """inference's end for node such as Module, Class, Function, Const...
- """
- yield self
-
#infer_end = path_wrapper(infer_end)
nodes.Module.infer = nodes.Class.infer = infer_end
nodes.List.infer = infer_end
nodes.Tuple.infer = infer_end
nodes.Dict.infer = infer_end
-nodes.Const.infer = infer_end
-
-def infer_empty_node(self, context=None):
- if not self.has_underlying_object():
- yield YES
- else:
- try:
- for infered in MANAGER.infer_astng_from_something(self.object,
- context=context):
- yield infered
- except ASTNGError:
- yield YES
-nodes.EmptyNode.infer = path_wrapper(infer_empty_node)
-
class CallContext:
@@ -243,26 +200,6 @@ def infer_name(self, context=None):
nodes.Name.infer = path_wrapper(infer_name)
-
-def infer_assname(self, context=None):
- """infer a AssName/AssAttr: need to inspect the RHS part of the
- assign node
- """
- stmts = self.assigned_stmts(context=context)
- return _infer_stmts(stmts, context)
-
-nodes.AssName.infer = path_wrapper(infer_assname)
-
-
-def infer_assattr(self, context=None):
- """infer a AssName/AssAttr: need to inspect the RHS part of the
- assign node
- """
- stmts = self.assigned_stmts(context=context)
- return _infer_stmts(stmts, context)
-
-nodes.AssAttr.infer = path_wrapper(infer_assattr)
-
def infer_callfunc(self, context=None):
"""infer a CallFunc node by trying to guess what's the function is
@@ -289,34 +226,6 @@ def infer_callfunc(self, context=None):
nodes.CallFunc.infer = path_wrapper(infer_callfunc)
-def infer_getattr(self, context=None):
- """infer a Getattr node by using getattr on the associated object
- """
- one_infered = False
- # XXX
- #context = context.clone()
- for owner in self.expr.infer(context):
- if owner is YES:
- yield owner
- one_infered = True
- continue
- try:
- context.boundnode = owner
- for obj in owner.igetattr(self.attrname, context):
- yield obj
- one_infered = True
- context.boundnode = None
- except (NotFoundError, InferenceError):
- continue
- except AttributeError:
- # XXX method / function
- continue
- if not one_infered:
- raise InferenceError()
-
-nodes.Getattr.infer = path_wrapper(infer_getattr)
-
-
def _imported_module_astng(node, modname):
"""return the ast for a module whose name is <modname> imported by <node>
"""
@@ -526,23 +435,6 @@ will be [1, 1] once arrived to the Assign node.
The `context` argument is the current inference context which should be given
to any intermediary inference necessary.
"""
-def assend_assigned_stmts(self, context=None):
- # only infer *real* assignments
- if self.flags == 'OP_DELETE':
- raise InferenceError()
- return self.parent.assigned_stmts(self, context=context)
-
-nodes.AssName.assigned_stmts = assend_assigned_stmts
-nodes.AssAttr.assigned_stmts = assend_assigned_stmts
-
-def mulass_assigned_stmts(self, node, context=None, asspath=None):
- if asspath is None:
- asspath = []
- node_idx = self.nodes.index(node)
- asspath.insert(0, node_idx)
- return self.parent.assigned_stmts(self, context, asspath)
-nodes.AssTuple.assigned_stmts = mulass_assigned_stmts
-nodes.AssList.assigned_stmts = mulass_assigned_stmts
def assign_assigned_stmts(self, node, context=None, asspath=None):
if not asspath:
@@ -597,53 +489,7 @@ def tryexcept_assigned_stmts(self, node, context=None, asspath=None):
nodes.TryExcept.assigned_stmts = tryexcept_assigned_stmts
-def _resolve_looppart(parts, asspath, context):
- """recursive function to resolve multiple assignments on loops"""
- asspath = asspath[:]
- index = asspath.pop(0)
- for part in parts:
- if part is YES:
- continue
- if not hasattr(part, 'iter_stmts'):
- continue
- for stmt in part.iter_stmts():
- try:
- assigned = stmt.getitem(index)
- except (AttributeError, IndexError):
- continue
- if not asspath:
- # we acheived to resolved the assigment path,
- # don't infer the last part
- found = True
- yield assigned
- elif assigned is YES:
- break
- else:
- # we are not yet on the last part of the path
- # search on each possibly infered value
- try:
- for infered in _resolve_looppart(assigned.infer(context), asspath, context):
- yield infered
- except InferenceError:
- break
-def for_assigned_stmts(self, node, context=None, asspath=None):
- found = False
- if asspath is None:
- for lst in self.loop_node().infer(context):
- if isinstance(lst, (nodes.Tuple, nodes.List)):
- for item in lst.nodes:
- found = True
- yield item
- else:
- for infered in _resolve_looppart(self.loop_node().infer(context), asspath, context):
- found = True
- yield infered
- if not found:
- raise InferenceError()
-nodes.For.assigned_stmts = for_assigned_stmts
-nodes.ListCompFor.assigned_stmts = for_assigned_stmts
-nodes.GenExprFor.assigned_stmts = for_assigned_stmts
def with_assigned_stmts(self, node, context=None, asspath=None):
found = False
@@ -660,28 +506,11 @@ def with_assigned_stmts(self, node, context=None, asspath=None):
nodes.With.assigned_stmts = with_assigned_stmts
-def end_ass_type(self):
- return self
nodes.With.ass_type = end_ass_type
nodes.For.ass_type = end_ass_type
-nodes.ListCompFor.ass_type = end_ass_type
-nodes.GenExprFor.ass_type = end_ass_type
nodes.TryExcept.ass_type = end_ass_type
nodes.Assign.ass_type = end_ass_type
nodes.AugAssign.ass_type = end_ass_type
-def parent_ass_type(self):
- return self.parent.ass_type()
-nodes.AssName.ass_type = parent_ass_type
-nodes.AssAttr.ass_type = parent_ass_type
-nodes.AssTuple.ass_type = parent_ass_type
-nodes.AssList.ass_type = parent_ass_type
-def assend_ass_type(self, context=None):
- # only infer *real* assignments
- if self.flags == 'OP_DELETE':
- return self
- return self.parent.ass_type()
-nodes.AssName.ass_type = assend_ass_type
-nodes.AssAttr.ass_type = assend_ass_type
# subscription protocol #######################################################
@@ -716,8 +545,6 @@ nodes.Dict.iter_stmts = dict_iter_stmts
def for_loop_node(self):
return self.list
nodes.For.loop_node = for_loop_node
-nodes.ListCompFor.loop_node = for_loop_node
-def gen_loop_nodes(self):
- return self.iter
-nodes.GenExprFor.loop_node = gen_loop_nodes
+if nodes.AST_MODE == 'compiler':
+ from logilab.astng._inference_compiler import *
diff --git a/lookup.py b/lookup.py
index 278072db..6da0ecbf 100644
--- a/lookup.py
+++ b/lookup.py
@@ -222,6 +222,10 @@ def _decorate(astmodule):
astmodule.Module.scope_lookup = scope_lookup
astmodule.GenExpr.scope_lookup = scope_lookup
for name in ('Class', 'Function', 'Lambda',
- 'For', 'ListCompFor', 'GenExprFor',
- 'AssName', 'Name', 'Const'):
+ 'For', 'Name', 'Const'):
globals()[name] = getattr(astmodule, name)
+ if hasattr(astmodule, 'ListCompFor'):
+ for name in ('ListCompFor', 'GenExprFor', 'AssName',):
+ globals()[name] = getattr(astmodule, name)
+ #else: XXX
+
diff --git a/nodes.py b/nodes.py
index fc8a5a07..1e93062c 100644
--- a/nodes.py
+++ b/nodes.py
@@ -32,9 +32,9 @@ on From and Import :
[1] http://docs.python.org/lib/module-compiler.ast.html
:author: Sylvain Thenault
-:copyright: 2003-2007 LOGILAB S.A. (Paris, FRANCE)
+:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE)
:contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
-:copyright: 2003-2007 Sylvain Thenault
+:copyright: 2003-2008 Sylvain Thenault
:contact: mailto:thenault@gmail.com
"""
@@ -42,56 +42,24 @@ from __future__ import generators
__docformat__ = "restructuredtext en"
-from compiler.ast import Assign, Add, And, AssAttr, AssList, AssName, \
- AssTuple, Assert, Assign, AugAssign, \
- Backquote, Bitand, Bitor, Bitxor, Break, CallFunc, Class, \
- Compare, Const, Continue, Dict, Discard, Div, FloorDiv, \
- Ellipsis, EmptyNode, Exec, \
- For, From, Function, Getattr, Global, \
- If, Import, Invert, Keyword, Lambda, LeftShift, \
- List, ListComp, ListCompFor, ListCompIf, Mod, Module, Mul, Name, Node, \
- Not, Or, Pass, Power, Print, Printnl, Raise, Return, RightShift, Slice, \
- Sliceobj, Stmt, Sub, Subscript, TryExcept, TryFinally, Tuple, UnaryAdd, \
- UnarySub, While, Yield
try:
- # introduced in python 2.4
- from compiler.ast import GenExpr, GenExprFor, GenExprIf, GenExprInner
+ from logilab.astng._nodes_ast import *
+ AST_MODE = '_ast'
except:
- class GenExpr:
- """dummy GenExpr node, shouldn't be used since py < 2.4"""
- class GenExprFor:
- """dummy GenExprFor node, shouldn't be used since py < 2.4"""
- class GenExprIf:
- """dummy GenExprIf node, shouldn't be used since py < 2.4"""
- class GenExprInner:
- """dummy GenExprInner node, shouldn't be used since py < 2.4"""
+ from logilab.astng._nodes_compiler import *
+ AST_MODE = 'compiler'
-try:
- # introduced in python 2.4
- from compiler.ast import Decorators
-except:
- class Decorators:
- """dummy Decorators node, shouldn't be used since py < 2.4"""
-try:
- # introduced in python 2.5
- from compiler.ast import With
-except:
- class With:
- """dummy With node, shouldn't be used since py < 2.5"""
-
-from logilab.astng._exceptions import NotFoundError, InferenceError
-from logilab.astng.utils import extend_class
from logilab.astng import InferenceContext
+from logilab.astng._exceptions import NotFoundError
+from logilab.astng.utils import extend_class
-# introduced in python 2.5
-From.level = 0 # will be overiden by instance attribute with py>=2.5
+INFER_NEED_NAME_STMTS = (From, Import, Global, TryExcept)
import re
ID_RGX = re.compile('^[a-zA-Z_][a-zA-Z_0-9]*$')
del re
-INFER_NEED_NAME_STMTS = (From, Import, Global, TryExcept)
# Node ######################################################################
@@ -260,12 +228,8 @@ class NodeNG:
extend_class(Node, NodeNG)
-Const.eq = lambda self, value: self.value == value
-
-def decorators_scope(self):
- # skip the function node to go directly to the upper level scope
- return self.parent.parent.scope()
-Decorators.scope = decorators_scope
+Module.fromlineno = 0
+Module.tolineno = 0
# block range overrides #######################################################
@@ -363,33 +327,6 @@ def and_as_string(node):
return ' and '.join(['(%s)' % n.as_string() for n in node.nodes])
And.as_string = and_as_string
-def assattr_as_string(node):
- """return an ast.AssAttr node as string"""
- if node.flags == 'OP_DELETE':
- return 'del %s.%s' % (node.expr.as_string(), node.attrname)
- return '%s.%s' % (node.expr.as_string(), node.attrname)
-AssAttr.as_string = assattr_as_string
-
-def asslist_as_string(node):
- """return an ast.AssList node as string"""
- string = ', '.join([n.as_string() for n in node.nodes])
- return '[%s]' % string
-AssList.as_string = asslist_as_string
-
-def assname_as_string(node):
- """return an ast.AssName node as string"""
- if node.flags == 'OP_DELETE':
- return 'del %s' % node.name
- return node.name
-AssName.as_string = assname_as_string
-
-def asstuple_as_string(node):
- """return an ast.AssTuple node as string"""
- string = ', '.join([n.as_string() for n in node.nodes])
- # fix for del statement
- return string.replace(', del ', ', ')
-AssTuple.as_string = asstuple_as_string
-
def assert_as_string(node):
"""return an ast.Assert node as string"""
if node.fail:
@@ -460,11 +397,6 @@ def compare_as_string(node):
return '%s %s' % (node.expr.as_string(), rhs_str)
Compare.as_string = compare_as_string
-def const_as_string(node):
- """return an ast.Const node as string"""
- return repr(node.value)
-Const.as_string = const_as_string
-
def continue_as_string(node):
"""return an ast.Continue node as string"""
return 'continue'
@@ -476,11 +408,6 @@ def dict_as_string(node):
for key, value in node.items])
Dict.as_string = dict_as_string
-def discard_as_string(node):
- """return an ast.Discard node as string"""
- return node.expr.as_string()
-Discard.as_string = discard_as_string
-
def div_as_string(node):
"""return an ast.Div node as string"""
return '(%s) / (%s)' % (node.left.as_string(), node.right.as_string())
@@ -496,10 +423,6 @@ def ellipsis_as_string(node):
return '...'
Ellipsis.as_string = ellipsis_as_string
-def empty_as_string(node):
- return ''
-EmptyNode.as_string = empty_as_string
-
def exec_as_string(node):
"""return an ast.Exec node as string"""
if node.globals:
@@ -524,6 +447,7 @@ For.as_string = for_as_string
def from_as_string(node):
"""return an ast.From node as string"""
+ # XXX level
return 'from %s import %s' % (node.modname, _import_string(node.names))
From.as_string = from_as_string
@@ -540,29 +464,6 @@ def genexpr_as_string(node):
return '(%s)' % node.code.as_string()
GenExpr.as_string = genexpr_as_string
-def genexprinner_as_string(node):
- """return an ast.GenExpr node as string"""
- return '%s %s' % (node.expr.as_string(), ' '.join([n.as_string()
- for n in node.quals]))
-GenExprInner.as_string = genexprinner_as_string
-
-def genexprfor_as_string(node):
- """return an ast.GenExprFor node as string"""
- return 'for %s in %s %s' % (node.assign.as_string(),
- node.iter.as_string(),
- ' '.join([n.as_string() for n in node.ifs]))
-GenExprFor.as_string = genexprfor_as_string
-
-def genexprif_as_string(node):
- """return an ast.GenExprIf node as string"""
- return 'if %s' % node.test.as_string()
-GenExprIf.as_string = genexprif_as_string
-
-def getattr_as_string(node):
- """return an ast.Getattr node as string"""
- return '%s.%s' % (node.expr.as_string(), node.attrname)
-Getattr.as_string = getattr_as_string
-
def global_as_string(node):
"""return an ast.Global node as string"""
return 'global %s' % ', '.join(node.names)
@@ -589,11 +490,6 @@ def invert_as_string(node):
return '~%s' % node.expr.as_string()
Invert.as_string = invert_as_string
-def keyword_as_string(node):
- """return an ast.Keyword node as string"""
- return '%s=%s' % (node.name, node.expr.as_string())
-Keyword.as_string = keyword_as_string
-
def lambda_as_string(node):
"""return an ast.Lambda node as string"""
return 'lambda %s: %s' % (node.format_args(), node.code.as_string())
@@ -615,18 +511,6 @@ def listcomp_as_string(node):
for n in node.quals]))
ListComp.as_string = listcomp_as_string
-def listcompfor_as_string(node):
- """return an ast.ListCompFor node as string"""
- return 'for %s in %s %s' % (node.assign.as_string(),
- node.list.as_string(),
- ' '.join([n.as_string() for n in node.ifs]))
-ListCompFor.as_string = listcompfor_as_string
-
-def listcompif_as_string(node):
- """return an ast.ListCompIf node as string"""
- return 'if %s' % node.test.as_string()
-ListCompIf.as_string = listcompif_as_string
-
def mod_as_string(node):
"""return an ast.Mod node as string"""
return '(%s) %% (%s)' % (node.left.as_string(), node.right.as_string())
@@ -676,14 +560,6 @@ def print_as_string(node):
return 'print %s,' % nodes
Print.as_string = print_as_string
-def printnl_as_string(node):
- """return an ast.Printnl node as string"""
- nodes = ', '.join([n.as_string() for n in node.nodes])
- if node.dest:
- return 'print >> %s, %s' % (node.dest.as_string(), nodes)
- return 'print %s' % nodes
-Printnl.as_string = printnl_as_string
-
def raise_as_string(node):
"""return an ast.Raise node as string"""
if node.expr1:
@@ -716,19 +592,6 @@ def slice_as_string(node):
return '%s[%s:%s]' % (node.expr.as_string(), lower, upper)
Slice.as_string = slice_as_string
-def sliceobj_as_string(node):
- """return an ast.Sliceobj node as string"""
- return ':'.join([n.as_string() for n in node.nodes])
-Sliceobj.as_string = sliceobj_as_string
-
-def stmt_as_string(node):
- """return an ast.Stmt node as string"""
- stmts = '\n'.join([n.as_string() for n in node.nodes])
- if isinstance(node.parent, Module):
- return stmts
- return stmts.replace('\n', '\n ')
-Stmt.as_string = stmt_as_string
-
def sub_as_string(node):
"""return an ast.Sub node as string"""
return '(%s) - (%s)' % (node.left.as_string(), node.right.as_string())
@@ -812,6 +675,3 @@ def _import_string(names):
_names.append(name)
return ', '.join(_names)
-# to backport into compiler ###################################################
-
-EmptyNode.getChildNodes = lambda self: ()
diff --git a/patchcomptransformer.py b/patchcomptransformer.py
new file mode 100644
index 00000000..d060b68c
--- /dev/null
+++ b/patchcomptransformer.py
@@ -0,0 +1,123 @@
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""Monkey patch compiler.transformer to fix line numbering bugs
+
+:author: Sylvain Thenault
+:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE)
+:contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
+:copyright: 2003-2008 Sylvain Thenault
+:contact: mailto:thenault@gmail.com
+"""
+
+from types import TupleType
+from compiler import transformer
+
+from logilab.astng import nodes
+
+def fromto_lineno(asttuple):
+ """return the minimum and maximum line number of the given ast tuple"""
+ return from_lineno(asttuple), to_lineno(asttuple)
+
+def from_lineno(asttuple):
+ """return the minimum line number of the given ast tuple"""
+ if type(asttuple[1]) is TupleType:
+ return from_lineno(asttuple[1])
+ return asttuple[2]
+
+def to_lineno(asttuple):
+ """return the maximum line number of the given ast tuple"""
+ if type(asttuple[-1]) is TupleType:
+ return to_lineno(asttuple[-1])
+ return asttuple[2]
+
+def fix_lineno(node, fromast, toast=None):
+ if 'fromlineno' in node.__dict__:
+ return node
+ #print 'fixing', id(node), id(node.__dict__), node.__dict__.keys(), repr(node)
+ if isinstance(node, nodes.Stmt):
+ node.fromlineno = from_lineno(fromast)#node.nodes[0].fromlineno
+ node.tolineno = node.nodes[-1].tolineno
+ return node
+ if toast is None:
+ node.fromlineno, node.tolineno = fromto_lineno(fromast)
+ else:
+ node.fromlineno, node.tolineno = from_lineno(fromast), to_lineno(toast)
+ #print 'fixed', id(node)
+ return node
+
+BaseTransformer = transformer.Transformer
+
+COORD_MAP = {
+ # if: test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
+ 'if': (0, 0),
+ # 'while' test ':' suite ['else' ':' suite]
+ 'while': (0, 1),
+ # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite]
+ 'for': (0, 3),
+ # 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
+ 'try': (0, 0),
+ # | 'try' ':' suite 'finally' ':' suite
+
+ }
+
+def fixlineno_wrap(function, stype):
+ def fixlineno_wrapper(self, nodelist):
+ node = function(self, nodelist)
+ idx1, idx2 = COORD_MAP.get(stype, (0, -1))
+ return fix_lineno(node, nodelist[idx1], nodelist[idx2])
+ return fixlineno_wrapper
+
+class ASTNGTransformer(BaseTransformer):
+ """ovverides transformer for a better source line number handling"""
+ def com_NEWLINE(self, *args):
+ # A ';' at the end of a line can make a NEWLINE token appear
+ # here, Render it harmless. (genc discards ('discard',
+ # ('const', xxxx)) Nodes)
+ lineno = args[0][1]
+ # don't put fromlineno/tolineno on Const None to mark it as dynamically
+ # added, without "physical" reference in the source
+ n = nodes.Discard(nodes.Const(None))
+ n.fromlineno = n.tolineno = lineno
+ return n
+ def com_node(self, node):
+ res = self._dispatch[node[0]](node[1:])
+ return fix_lineno(res, node)
+ def com_assign(self, node, assigning):
+ res = BaseTransformer.com_assign(self, node, assigning)
+ return fix_lineno(res, node)
+ def com_apply_trailer(self, primaryNode, nodelist):
+ node = BaseTransformer.com_apply_trailer(self, primaryNode, nodelist)
+ return fix_lineno(node, nodelist)
+
+## def atom(self, nodelist):
+## node = BaseTransformer.atom(self, nodelist)
+## return fix_lineno(node, nodelist[0], nodelist[-1])
+
+ def funcdef(self, nodelist):
+ node = BaseTransformer.funcdef(self, nodelist)
+ # XXX decorators
+ return fix_lineno(node, nodelist[-5], nodelist[-3])
+ def classdef(self, nodelist):
+ node = BaseTransformer.classdef(self, nodelist)
+ return fix_lineno(node, nodelist[0], nodelist[-2])
+
+# wrap *_stmt methods
+for name in dir(BaseTransformer):
+ if name.endswith('_stmt') and not (name in ('com_stmt',
+ 'com_append_stmt')
+ or name in ASTNGTransformer.__dict__):
+ setattr(BaseTransformer, name,
+ fixlineno_wrap(getattr(BaseTransformer, name), name[:-5]))
+
+transformer.Transformer = ASTNGTransformer
+
diff --git a/raw_building.py b/raw_building.py
index 502ad8d8..1ac5715f 100644
--- a/raw_building.py
+++ b/raw_building.py
@@ -14,9 +14,9 @@
(build_* functions) or from living object (object_build_* functions)
:author: Sylvain Thenault
-:copyright: 2003-2007 LOGILAB S.A. (Paris, FRANCE)
+:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE)
:contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
-:copyright: 2003-2007 Sylvain Thenault
+:copyright: 2003-2008 Sylvain Thenault
:contact: mailto:thenault@gmail.com
"""
@@ -27,9 +27,16 @@ from inspect import getargspec
from logilab.astng import nodes
+
+def _attach_local_node(parent, node, name):
+ node.name = name # needed by add_local_node
+ node.parent = parent
+ node.lineno = 1
+ parent.add_local_node(node)
+
def attach___dict__(node):
"""attach the __dict__ attribute to Class and Module objects"""
- dictn = nodes.Dict([])
+ dictn = nodes.dict_factory()
dictn.parent = node
node.locals['__dict__'] = [dictn]
@@ -49,36 +56,19 @@ def attach_const_node(node, name, value):
"""create a Const node and register it in the locals of the given
node with the specified name
"""
- _attach_local_node(node, nodes.Const(value), name)
+ _attach_local_node(node, nodes.const_factory(value), name)
-if sys.version_info < (2, 5):
- def attach_import_node(node, modname, membername):
- """create a From node and register it in the locals of the given
- node with the specified name
- """
- _attach_local_node(node,
- nodes.From(modname, ( (membername, None), ) ),
- membername)
-else:
- def attach_import_node(node, modname, membername):
- """create a From node and register it in the locals of the given
- node with the specified name
- """
- _attach_local_node(node,
- nodes.From(modname, ( (membername, None), ), 0),
- membername)
-
-def _attach_local_node(parent, node, name):
- node.name = name # needed by add_local_node
- node.parent = parent
- node.lineno = 1
- parent.add_local_node(node)
+def attach_import_node(node, modname, membername):
+ """create a From node and register it in the locals of the given
+ node with the specified name
+ """
+ _attach_local_node(node, nodes.import_from_factory(modname, membername),
+ membername)
def build_module(name, doc=None):
"""create and initialize a astng Module node"""
- node = nodes.Module(doc, nodes.Stmt([]))
- node.node.parent = node
+ node = nodes.module_factory(doc)
node.name = name
node.pure_python = False
node.package = False
diff --git a/scoped_nodes.py b/scoped_nodes.py
index 8895538c..fef17fd5 100644
--- a/scoped_nodes.py
+++ b/scoped_nodes.py
@@ -96,10 +96,6 @@ class LocalsDictMixIn(object):
self._append_node(child_node)
self.set_local(name or child_node.name, child_node)
- def _append_node(self, child_node):
- """append a child, linking it in the tree"""
- self.code.nodes.append(child_node)
- child_node.parent = self
def __getitem__(self, item):
"""method from the `dict` interface returning the first node
@@ -226,12 +222,7 @@ class ModuleNG(object):
raise
except:
pass
- raise NotFoundError(name)
-
- def _append_node(self, child_node):
- """append a child version specific to Module node"""
- self.node.nodes.append(child_node)
- child_node.parent = self
+ raise NotFoundError(name)
def source_line(self):
"""return the source line number, 0 on a module"""
diff --git a/test/unittest_builder.py b/test/unittest_builder.py
index 3b98a71b..0a62f262 100644
--- a/test/unittest_builder.py
+++ b/test/unittest_builder.py
@@ -21,8 +21,8 @@ from logilab.common.testlib import TestCase, unittest_main
from unittest_inference import get_name_node
from pprint import pprint
-from logilab.astng.astutils import cvrtr
-from logilab.astng import builder, nodes, Module, YES
+from logilab.astng import builder, nodes, patchcomptransformer
+from logilab.astng import Module, YES, InferenceError
import data
from data import module as test_module
@@ -30,7 +30,7 @@ from data import module as test_module
class TransformerTC(TestCase):
def setUp(self):
- transformer = builder.ASTNGTransformer()
+ transformer = patchcomptransformer.ASTNGTransformer()
self.astng = transformer.parsesuite(open('data/format.py').read())
def test_callfunc_lineno(self):
@@ -293,7 +293,7 @@ def global_no_effect():
self.failUnlessEqual(astng.getattr('CSTE')[1].source_line(), 6)
self.assertRaises(nodes.NotFoundError,
astng.getattr, 'CSTE2')
- self.assertRaises(nodes.InferenceError,
+ self.assertRaises(InferenceError,
astng['global_no_effect'].ilookup('CSTE2').next)
def test_socket_build(self):
diff --git a/utils.py b/utils.py
index 97815e9e..084f785b 100644
--- a/utils.py
+++ b/utils.py
@@ -14,9 +14,9 @@
extract information from it
:author: Sylvain Thenault
-:copyright: 2003-2007 LOGILAB S.A. (Paris, FRANCE)
+:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE)
:contact: http://www.logilab.fr/ -- mailto:python-projects@logilab.org
-:copyright: 2003-2007 Sylvain Thenault
+:copyright: 2003-2008 Sylvain Thenault
:contact: mailto:thenault@gmail.com
"""
@@ -30,10 +30,15 @@ def extend_class(original, addons):
class
"""
brain = addons.__dict__.copy()
- for special_key in ('__doc__', '__module__'):
+ for special_key in ('__doc__', '__module__', '__dict__'):
if special_key in addons.__dict__:
del brain[special_key]
- original.__dict__.update(brain)
+ try:
+ original.__dict__.update(brain)
+ except AttributeError:
+ # dictproxy object
+ for k, v in brain.iteritems():
+ setattr(original, k, v)
class ASTWalker:
"""a walker visiting a tree in preorder, calling on the handler:
@@ -171,3 +176,14 @@ def _try_except_from_branch(node, stmt):
for i, block_nodes in enumerate(node.handlers):
if stmt in block_nodes:
return 'except', i
+
+
+# inference utilities #########################################################
+
+def infer_end(self, context=None):
+ """inference's end for node such as Module, Class, Function, Const...
+ """
+ yield self
+
+def end_ass_type(self):
+ return self