summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--__init__.py150
-rw-r--r--_inference_ast.py46
-rw-r--r--_inference_compiler.py29
-rw-r--r--_nodes_ast.py170
-rw-r--r--_nodes_compiler.py71
-rw-r--r--builder.py51
-rw-r--r--inference.py18
-rw-r--r--lookup.py3
-rw-r--r--manager.py3
-rw-r--r--nodes.py2
-rw-r--r--raw_building.py64
-rw-r--r--scoped_nodes.py8
-rw-r--r--test/unittest_builder.py220
-rw-r--r--utils.py144
14 files changed, 611 insertions, 368 deletions
diff --git a/__init__.py b/__init__.py
index b1d27237..98692115 100644
--- a/__init__.py
+++ b/__init__.py
@@ -155,116 +155,6 @@ def path_wrapper(func):
raise
return wrapped
-# special inference objects ###################################################
-
-class Yes(object):
- """a yes object"""
- def __repr__(self):
- return 'YES'
- def __getattribute__(self, name):
- return self
- def __call__(self, *args, **kwargs):
- return self
-YES = Yes()
-
-class Proxy:
- """a simple proxy object"""
- def __init__(self, proxied):
- self._proxied = proxied
-
- def __getattr__(self, name):
- return getattr(self._proxied, name)
-
- def infer(self, context=None):
- yield self
-
-
-class InstanceMethod(Proxy):
- """a special node representing a function bound to an instance"""
- def __repr__(self):
- instance = self._proxied.parent.frame()
- return 'Bound method %s of %s.%s' % (self._proxied.name,
- instance.root().name,
- instance.name)
- __str__ = __repr__
-
- def is_bound(self):
- return True
-
-
-class Instance(Proxy):
- """a special node representing a class instance"""
- def getattr(self, name, context=None, lookupclass=True):
- try:
- return self._proxied.instance_attr(name, context)
- except NotFoundError:
- if name == '__class__':
- return [self._proxied]
- if name == '__name__':
- # access to __name__ gives undefined member on class
- # instances but not on class objects
- raise NotFoundError(name)
- if lookupclass:
- return self._proxied.getattr(name, context)
- raise NotFoundError(name)
-
- def igetattr(self, name, context=None):
- """infered getattr"""
- try:
- # XXX frame should be self._proxied, or not ?
- return _infer_stmts(
- self._wrap_attr(self.getattr(name, context, lookupclass=False)),
- context, frame=self)
- except NotFoundError:
- try:
- # fallback to class'igetattr since it has some logic to handle
- # descriptors
- return self._wrap_attr(self._proxied.igetattr(name, context))
- except NotFoundError:
- raise InferenceError(name)
-
- def _wrap_attr(self, attrs):
- """wrap bound methods of attrs in a InstanceMethod proxies"""
- # Guess which attrs are used in inference.
- def wrap(attr):
- if isinstance(attr, Function) and attr.type == 'method':
- return InstanceMethod(attr)
- else:
- return attr
- return imap(wrap, attrs)
-
- def infer_call_result(self, caller, context=None):
- """infer what's a class instance is returning when called"""
- infered = False
- for node in self._proxied.igetattr('__call__', context):
- for res in node.infer_call_result(caller, context):
- infered = True
- yield res
- if not infered:
- raise InferenceError()
-
- def __repr__(self):
- return 'Instance of %s.%s' % (self._proxied.root().name,
- self._proxied.name)
- __str__ = __repr__
-
- def callable(self):
- try:
- self._proxied.getattr('__call__')
- return True
- except NotFoundError:
- return False
-
- def pytype(self):
- return self._proxied.qname()
-
-class Generator(Proxy):
- """a special node representing a generator"""
- def callable(self):
- return True
-
- def pytype(self):
- return '__builtin__.generator'
# imports #####################################################################
@@ -278,42 +168,4 @@ from logilab.astng import inference
from logilab.astng import lookup
lookup._decorate(nodes)
-List._proxied = MANAGER.astng_from_class(list)
-List.__bases__ += (inference.Instance,)
-List.pytype = lambda x: '__builtin__.list'
-
-Tuple._proxied = MANAGER.astng_from_class(tuple)
-Tuple.__bases__ += (inference.Instance,)
-Tuple.pytype = lambda x: '__builtin__.tuple'
-
-Dict.__bases__ += (inference.Instance,)
-Dict._proxied = MANAGER.astng_from_class(dict)
-Dict.pytype = lambda x: '__builtin__.dict'
-
-builtin_astng = Dict._proxied.root()
-
-Const.__bases__ += (inference.Instance,)
-Const._proxied = None
-def Const___getattr__(self, name):
- if self.value is None:
- raise AttributeError(name)
- if self._proxied is None:
- self._proxied = MANAGER.astng_from_class(self.value.__class__)
- return getattr(self._proxied, name)
-Const.__getattr__ = Const___getattr__
-def Const_getattr(self, name, context=None, lookupclass=None):
- if self.value is None:
- raise NotFoundError(name)
- if self._proxied is None:
- self._proxied = MANAGER.astng_from_class(self.value.__class__)
- return self._proxied.getattr(name, context)
-Const.getattr = Const_getattr
-Const.has_dynamic_getattr = lambda x: False
-
-def Const_pytype(self):
- if self.value is None:
- return '__builtin__.NoneType'
- if self._proxied is None:
- self._proxied = MANAGER.astng_from_class(self.value.__class__)
- return self._proxied.qname()
-Const.pytype = Const_pytype
+from logilab.astng.utils import Instance, InstanceMethod, YES, NONE, TRUE, FALSE
diff --git a/_inference_ast.py b/_inference_ast.py
new file mode 100644
index 00000000..9cea5626
--- /dev/null
+++ b/_inference_ast.py
@@ -0,0 +1,46 @@
+from logilab.astng import MANAGER, YES, ASTNGError, Instance, _infer_stmts, path_wrapper
+from logilab.astng import nodes
+from logilab.astng.utils import infer_end, end_ass_type
+
+nodes.Num.__bases__ += (Instance,)
+nodes.Num._proxied = None
+nodes.Num.has_dynamic_getattr = lambda x: False
+def _Num_value_proxy(node):
+ if node._proxied is None:
+ node._proxied = MANAGER.astng_from_class(node.n.__class__)
+ return node._proxied
+nodes.Num._value_proxy = _Num_value_proxy
+
+
+nodes.Str.__bases__ += (Instance,)
+nodes.Str._proxied = None
+nodes.Str.has_dynamic_getattr = lambda x: False
+def _Str_value_proxy(node):
+ if node._proxied is None:
+ node._proxied = MANAGER.astng_from_class(node.s.__class__)
+ return node._proxied
+nodes.Str._value_proxy = _Str_value_proxy
+
+
+def Const___getattr__(node, name):
+ #if node.value is None:
+ # raise AttributeError(name)
+ return getattr(node._value_proxy(), name)
+nodes.Str.__getattr__ = Const___getattr__
+nodes.Num.__getattr__ = Const___getattr__
+
+def Const_getattr(node, name, context=None, lookupclass=None):
+ #if node.value is None:
+ # raise NotFoundError(name)
+ return node._value_proxy().getattr(name, context)
+nodes.Str.getattr = Const_getattr
+nodes.Num.getattr = Const_getattr
+
+def Const_pytype(node):
+ return node._value_proxy().qname()
+nodes.Num.pytype = Const_pytype
+nodes.Str.pytype = Const_pytype
+
+
+nodes.Num.infer = infer_end
+nodes.Str.infer = infer_end
diff --git a/_inference_compiler.py b/_inference_compiler.py
index 07cb86ac..81758bae 100644
--- a/_inference_compiler.py
+++ b/_inference_compiler.py
@@ -1,8 +1,35 @@
-from logilab.astng import MANAGER, YES, ASTNGError, _infer_stmts, path_wrapper
from logilab.astng import nodes
+from logilab.astng import (MANAGER, YES, ASTNGError, InferenceError, Instance,
+ _infer_stmts, path_wrapper)
from logilab.astng.utils import infer_end, end_ass_type
+nodes.Const.__bases__ += (Instance,)
+nodes.Const._proxied = None
+def Const___getattr__(self, name):
+ if self.value is None:
+ raise AttributeError(name)
+ if self._proxied is None:
+ self._proxied = MANAGER.astng_from_class(self.value.__class__)
+ return getattr(self._proxied, name)
+nodes.Const.__getattr__ = Const___getattr__
+def Const_getattr(self, name, context=None, lookupclass=None):
+ if self.value is None:
+ raise NotFoundError(name)
+ if self._proxied is None:
+ self._proxied = MANAGER.astng_from_class(self.value.__class__)
+ return self._proxied.getattr(name, context)
+nodes.Const.getattr = Const_getattr
+nodes.Const.has_dynamic_getattr = lambda x: False
+
+def Const_pytype(self):
+ if self.value is None:
+ return '__builtin__.NoneType'
+ if self._proxied is None:
+ self._proxied = MANAGER.astng_from_class(self.value.__class__)
+ return self._proxied.qname()
+nodes.Const.pytype = Const_pytype
+
nodes.Const.infer = infer_end
diff --git a/_nodes_ast.py b/_nodes_ast.py
index 3747f0ec..0dd135e4 100644
--- a/_nodes_ast.py
+++ b/_nodes_ast.py
@@ -1,3 +1,4 @@
+
# 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
@@ -21,7 +22,7 @@
__docformat__ = "restructuredtext en"
-from logilab.astng.utils import infer_end
+from logilab.astng.utils import infer_end, NoneType, Bool
from _ast import (Add, And, Assert, Assign, AugAssign,
Break,
@@ -33,7 +34,7 @@ from _ast import (Add, And, Assert, Assign, AugAssign,
If, Import, Invert,
Lambda, List, ListComp,
Mod, Module,
- Name, Not,
+ Not,
Or,
Pass, Print,
Raise, Return,
@@ -44,6 +45,7 @@ from _ast import (Add, And, Assert, Assign, AugAssign,
)
from _ast import (AST as Node,
+ Attribute as Getattr,
BitAnd as Bitand, BitOr as Bitor, BitXor as Bitxor,
Call as CallFunc,
ClassDef as Class,
@@ -57,6 +59,8 @@ from _ast import (AST as Node,
RShift as RightShift,
UAdd as UnaryAdd,
USub as UnarySub,
+
+ Name,
)
# XXX : AugLoad, AugStore, Attribute
# BinOp, BoolOp
@@ -69,39 +73,117 @@ from _ast import (AST as Node,
# Param
# Store, Str, Suite
# UnaryOp
-from _ast import Num, Str, Expr, alias
+from _ast import Num, Str, Eq, Expr, alias, arguments, comprehension
Const = (Num, Str)
class EmptyNode(Node): pass
+def Name__init__(self, name):
+ self.id = name
+Name.__init__ = Name__init__
+
+def Name_get_name(self):
+ return self.id
+Name.name = property(Name_get_name)
+
+def _get_children_value(self):
+ return (self.value,)
+Expr.getChildNodes = _get_children_value
+Getattr.getChildNodes = _get_children_value
+
+def _get_children_nochildren(self):
+ return ()
+Import.getChildNodes = _get_children_nochildren
+print 'patching', Name, id(Name)
+Name.getChildNodes = _get_children_nochildren
+Str.getChildNodes = _get_children_nochildren
+Num.getChildNodes = _get_children_nochildren
+NoneType.getChildNodes = _get_children_nochildren
+Bool.getChildNodes = _get_children_nochildren
+Pass.getChildNodes = _get_children_nochildren
+Eq.getChildNodes = _get_children_nochildren
+
+def _get_children_call(self):
+ children = [self.func]
+ children.extend(self.args)
+ children.extend(self.keywords)
+ if self.starargs:
+ children.extend(self.starargs)
+ if self.kwargs:
+ children.extend(self.kwargs)
+ return children
+CallFunc.getChildNodes = _get_children_call
+
+
+def _get_children_assign(self):
+ return self.targets + [self.value]
+Assign.getChildNodes = _get_children_assign
+
+def _get_children_if(self):
+ return [self.test] + self.body + self.orelse
+If.getChildNodes = _get_children_if
+
+def _get_children_print(self):
+ if self.dest:
+ return [self.dest] + self.values
+ return self.values
+Print.getChildNodes = _get_children_print
+
+def _get_children_compare(self):
+ return [self.left] + self.ops + self.comparators
+Compare.getChildNodes = _get_children_compare
+
+def _get_children_generatorexp(self):
+ return [self.elt] + self.generators
+GenExpr.getChildNodes = _get_children_generatorexp
+
+def _get_children_comprehension(self):
+ return [self.target] + [self.iter] + self.ifs
+comprehension.getChildNodes = _get_children_comprehension
+
+
+def getattr_as_string(node):
+ """return an ast.Getattr node as string"""
+ return '%s.%s' % (node.value.as_string(), node.attr)
+Getattr.as_string = getattr_as_string
+
# 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 _get_children_body(self):
+ return self.body
+Module.getChildNodes = _get_children_body
+Class.getChildNodes = _get_children_body
+Function.getChildNodes = _get_children_body
def _append_node(self, child_node):
"""append a child, linking it in the tree"""
- # XXX
- self.code.nodes.append(child_node)
+ self.body.append(child_node)
child_node.parent = self
+Module._append_node = _append_node
Class._append_node = _append_node
Function._append_node = _append_node
-# inferences ##################################################################
-
-from logilab.astng.utils import infer_end
+#
+def _init_set_doc(node):
+ node.doc = None
+ if isinstance(node.body[0], Expr) and isinstance(node.body[0].value, Str):
+ node.doc = node.body[0].value.s
+ print 'set doc', node
+ return node
+init_module = init_function = init_class = _init_set_doc
-Num.infer = infer_end
-Str.infer = infer_end
+def init_import(node):
+ node.names = [(alias.name, alias.asname) for alias in node.names]
+ return node
+def init_assign(node):
+ return node
+
# raw building ################################################################
+from logilab.astng.utils import NoneType, Bool
-def module_factory(doc):
- node = Module()
- node.body = []
+def _add_docstring(node, doc):
+ node.doc = doc
if doc:
expr = Expr()
node.body.append(expr)
@@ -110,6 +192,11 @@ def module_factory(doc):
docstr.s = doc
expr.value = docstr
docstr.parent = expr
+
+def module_factory(doc):
+ node = Module()
+ node.body = []
+ _add_docstring(node, doc)
return node
def dict_factory():
@@ -128,21 +215,52 @@ def import_from_factory(modname, membername):
def const_factory(value):
if value is None:
- node = Name()
- node.id = 'None'
+ node = NoneType(None)
elif value is True:
- node = Name()
- node.id = 'True'
+ node = Bool(False)
elif value is False:
- node = Name()
- node.id = 'False'
+ node = Bool(True)
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))
+ node.value = value
return node
+def function_factory(name, args, defaults, flag=0, doc=None):
+ """create and initialize a astng Function node"""
+ node = Function()
+ node.body = []
+ node.name = name
+ argsnode = arguments()
+ argsnode.args = []
+ for arg in args:
+ argsnode.args.append(Name(arg))
+ argsnode.args[-1].parent = argsnode
+ argsnode.defaults = []
+ for default in defaults:
+ argsnode.defaults.append(const_factory(default))
+ argsnode.defaults[-1].parent = argsnode
+ argsnode.kwarg = None # XXX
+ argsnode.vararg = None # XXX
+ argsnode.parent = node
+ node.args = argsnode
+ _add_docstring(node, doc)
+ return node
+
+
+def class_factory(name, basenames=None, doc=None):
+ """create and initialize a astng Class node"""
+ node = Class()
+ node.body = []
+ node.name = name
+ # XXX to check
+ node.bases = []
+ for base in basenames:
+ basenode = Name(base)
+ node.bases.append(basenode)
+ basenode.parent = node
+ _add_docstring(node, doc)
+ return node
diff --git a/_nodes_compiler.py b/_nodes_compiler.py
index 93765e9d..477f0eca 100644
--- a/_nodes_compiler.py
+++ b/_nodes_compiler.py
@@ -18,11 +18,10 @@
:copyright: 2008 Sylvain Thenault
:contact: mailto:thenault@gmail.com
"""
+from __future__ import generators
__docformat__ = "restructuredtext en"
-from __future__ import generators
-
import sys
from compiler.ast import Add, And, AssAttr, AssList, AssName, \
AssTuple, Assert, Assign, AugAssign, \
@@ -191,13 +190,33 @@ def _append_node(self, child_node):
Class._append_node = _append_node
Function._append_node = _append_node
+#
+def init_module(node):
+ return node
+
+def init_function(node):
+ return node
+
+def init_class(node):
+ return node
+
+def init_import(node):
+ return node
+
+def init_assign(node):
+ node.value = node.expr
+ node.targets = node.nodes
+ return node
+
# raw building ################################################################
+from logilab.astng.utils import NoneType, Bool
+
def module_factory(doc):
node = Module(doc, Stmt([]))
node.node.parent = node
return node
-
+
def dict_factory():
return Dict([])
@@ -209,4 +228,48 @@ else:
return From(modname, ( (membername, None), ), 0)
def const_factory(value):
- return Const(value)
+ if value is None:
+ nodecls = NoneType
+ elif value is True:
+ nodecls = Bool
+ elif value is False:
+ nodecls = Bool
+ else:
+ nodecls = Const
+ return nodecls(value)
+
+# introduction of decorators has changed the Function initializer arguments
+if sys.version_info >= (2, 4):
+ def function_factory(name, args, defaults, flag=0, doc=None):
+ """create and initialize a astng Function node"""
+ # first argument is now a list of decorators
+ func = Function(Decorators([]), name, args, defaults, flag, doc,
+ Stmt([]))
+ func.code.parent = func
+ return func
+
+else:
+ def function_factory(name, args, defaults, flag=0, doc=None):
+ """create and initialize a astng Function node"""
+ func = Function(name, args, defaults, flag, doc, Stmt([]))
+ func.code.parent = func
+ return func
+
+def class_factory(name, basenames=None, doc=None):
+ """create and initialize a astng Class node"""
+ klass = Class(name, [], doc, Stmt([]))
+ bases = [Name(base) for base in basenames]
+ for base in bases:
+ base.parent = klass
+ klass.basenames = basenames
+ klass.bases = bases
+ klass.code.parent = klass
+ klass.locals = {}
+ klass.instance_attrs = {}
+ for name, value in ( ('__name__', name),
+ #('__module__', node.root().name),
+ ):
+ const = Const(value)
+ const.parent = klass
+ klass.locals[name] = [const]
+ return klass
diff --git a/builder.py b/builder.py
index 0069d5c8..f2c72012 100644
--- a/builder.py
+++ b/builder.py
@@ -166,21 +166,20 @@ class ASTNGBuilder:
self._par_stack = [node]
self._metaclass = ['']
self._global_names = []
- node.parent = None
node.globals = node.locals = {}
+ nodes.init_module(node)
for name, value in ( ('__name__', node.name),
('__file__', node.path),
('__doc__', node.doc) ):
- const = nodes.Const(value)
+ const = nodes.const_factory(value)
const.parent = node
node.locals[name] = [const]
- attach___dict__(node)
if node.package:
# FIXME: List(Const())
- const = nodes.Const(dirname(node.path))
+ const = const_factory(value)
const.parent = node
node.locals['__path__'] = [const]
-
+ attach___dict__(node)
def leave_module(self, _):
"""leave a stmt.Module node -> pop the last item on the stack and check
@@ -200,14 +199,16 @@ class ASTNGBuilder:
node.instance_attrs = {}
node.basenames = [b_node.as_string() for b_node in node.bases]
self._push(node)
+ nodes.init_class(node)
for name, value in ( ('__name__', node.name),
('__module__', node.root().name),
('__doc__', node.doc) ):
- const = nodes.Const(value)
+ const = nodes.const_factory(value)
const.parent = node
node.locals[name] = [const]
attach___dict__(node)
self._metaclass.append(self._metaclass[-1])
+ visit_classdef = visit_class
def leave_class(self, node):
"""leave a stmt.Class node -> pop the last item on the stack
@@ -219,6 +220,7 @@ class ASTNGBuilder:
# no base classes, detect new / style old style according to
# current scope
node._newstyle = metaclass == 'type'
+ leave_classdef = leave_class
def visit_function(self, node):
"""visit a stmt.Function node -> init node and push the corresponding
@@ -227,19 +229,22 @@ class ASTNGBuilder:
self.visit_default(node)
self._global_names.append({})
node.argnames = list(node.argnames)
+ nodes.init_function(node)
if isinstance(node.parent.frame(), nodes.Class):
node.type = 'method'
if node.name == '__new__':
node.type = 'classmethod'
self._push(node)
register_arguments(node, node.argnames)
-
+ visit_funcdef = visit_function
+
def leave_function(self, node):
"""leave a stmt.Function node -> pop the last item on the stack
"""
self.leave_default(node)
self._stack.pop()
self._global_names.pop()
+ leave_funcdef = leave_function
def visit_lambda(self, node):
"""visit a stmt.Lambda node -> init node locals
@@ -254,7 +259,8 @@ class ASTNGBuilder:
"""
self.visit_default(node)
node.locals = {}
-
+ visit_generatorexp = visit_genexpr
+
def visit_global(self, node):
"""visit a stmt.Global node -> add declared names to locals
"""
@@ -273,6 +279,7 @@ class ASTNGBuilder:
"""visit a stmt.Import node -> add imported names to locals
"""
self.visit_default(node)
+ nodes.init_import(node)
for (name, asname) in node.names:
name = asname or name
node.parent.set_local(name.split('.')[0], node)
@@ -318,23 +325,21 @@ class ASTNGBuilder:
klass = node.parent.frame()
#print node
if isinstance(klass, nodes.Class) and \
- isinstance(node.expr, nodes.CallFunc) and \
- isinstance(node.expr.node, nodes.Name):
- func_name = node.expr.node.name
+ isinstance(node.value, nodes.CallFunc) and \
+ isinstance(node.value.node, nodes.Name):
+ func_name = node.value.node.name
if func_name in ('classmethod', 'staticmethod'):
for ass_node in node.nodes:
- if isinstance(ass_node, nodes.AssName):
- try:
- meth = klass[ass_node.name]
- if isinstance(meth, nodes.Function):
- meth.type = func_name
- #else:
- # print >> sys.stderr, 'FIXME 1', meth
- except KeyError:
- #print >> sys.stderr, 'FIXME 2', ass_node.name
- continue
- elif (isinstance(node.nodes[0], nodes.AssName)
- and node.nodes[0].name == '__metaclass__'): # XXX check more...
+ try:
+ meth = klass[ass_node.name]
+ if isinstance(meth, nodes.Function):
+ meth.type = func_name
+ #else:
+ # print >> sys.stderr, 'FIXME 1', meth
+ except (AttributeError, KeyError):
+ #print >> sys.stderr, 'FIXME 2', ass_node.name
+ continue
+ elif getattr(node.targets[0], 'name', None) == '__metaclass__': # XXX check more...
self._metaclass[-1] = 'type' # XXX get the actual metaclass
def visit_assname(self, node):
diff --git a/inference.py b/inference.py
index fba9cde3..22f546db 100644
--- a/inference.py
+++ b/inference.py
@@ -27,12 +27,24 @@ from copy import copy
from logilab.common.compat import imap, chain, set
-from logilab.astng import MANAGER, YES, InferenceContext, Instance, Generator, \
+from logilab.astng import MANAGER, InferenceContext, \
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
+from logilab.astng.utils import YES, Instance, Generator, infer_end, end_ass_type
+nodes.List._proxied = MANAGER.astng_from_class(list)
+nodes.List.__bases__ += (Instance,)
+nodes.List.pytype = lambda x: '__builtin__.list'
+nodes.Tuple._proxied = MANAGER.astng_from_class(tuple)
+nodes.Tuple.__bases__ += (Instance,)
+nodes.Tuple.pytype = lambda x: '__builtin__.tuple'
+nodes.Dict.__bases__ += (Instance,)
+nodes.Dict._proxied = MANAGER.astng_from_class(dict)
+nodes.Dict.pytype = lambda x: '__builtin__.dict'
+
+builtin_astng = nodes.Dict._proxied.root()
+
# .infer method ###############################################################
@@ -548,3 +560,5 @@ nodes.For.loop_node = for_loop_node
if nodes.AST_MODE == 'compiler':
from logilab.astng._inference_compiler import *
+else: #nodes.AST_MODE == '_ast'
+ from logilab.astng._inference_ast import *
diff --git a/lookup.py b/lookup.py
index 6da0ecbf..c998bf11 100644
--- a/lookup.py
+++ b/lookup.py
@@ -55,6 +55,9 @@ def scope_lookup(self, node, name, offset=0):
stmts = node._filter_stmts(self.locals[name], self, offset)
except KeyError:
stmts = ()
+ except :
+ print self, self.locals
+ raise
if stmts:
return self, stmts
if self.parent:
diff --git a/manager.py b/manager.py
index 2b0e95a0..a9d1bdb5 100644
--- a/manager.py
+++ b/manager.py
@@ -33,7 +33,8 @@ from logilab.common.modutils import NoSourceFile, is_python_source, \
get_module_files, get_source_file
from logilab.common.configuration import OptionsProviderMixIn
-from logilab.astng import ASTNGBuildingException, Instance, nodes
+from logilab.astng import ASTNGBuildingException, nodes
+from logilab.astng.utils import Instance
def astng_wrapper(func, modname):
"""wrapper to give to ASTNGManager.project_from_files"""
diff --git a/nodes.py b/nodes.py
index 1e93062c..3b319966 100644
--- a/nodes.py
+++ b/nodes.py
@@ -45,7 +45,7 @@ __docformat__ = "restructuredtext en"
try:
from logilab.astng._nodes_ast import *
AST_MODE = '_ast'
-except:
+except ImportError:
from logilab.astng._nodes_compiler import *
AST_MODE = 'compiler'
diff --git a/raw_building.py b/raw_building.py
index 1ac5715f..6cb905f1 100644
--- a/raw_building.py
+++ b/raw_building.py
@@ -51,7 +51,7 @@ def attach_dummy_node(node, name, object=_marker):
_attach_local_node(node, enode, name)
nodes.EmptyNode.has_underlying_object = lambda self: self.object is not _marker
-
+
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
@@ -78,55 +78,27 @@ def build_module(name, doc=None):
def build_class(name, basenames=None, doc=None):
"""create and initialize a astng Class node"""
- klass = nodes.Class(name, [], doc, nodes.Stmt([]))
- bases = [nodes.Name(base) for base in basenames]
- for base in bases:
- base.parent = klass
- klass.basenames = basenames
- klass.bases = bases
- klass.code.parent = klass
- klass.locals = {}
- klass.instance_attrs = {}
+ node = nodes.class_factory(name, basenames, doc)
+ node.basenames = basenames
+ node.locals = {}
+ node.instance_attrs = {}
for name, value in ( ('__name__', name),
#('__module__', node.root().name),
):
- const = nodes.Const(value)
- const.parent = klass
- klass.locals[name] = [const]
- return klass
+ const = nodes.const_factory(value)
+ const.parent = node
+ node.locals[name] = [const]
+ return node
-# introduction of decorators has changed the Function initializer arguments
-if sys.version_info >= (2, 4):
- try:
- from compiler.ast import Decorators as BaseDecorators
- class Decorators(BaseDecorators):
- def __init__(self):
- BaseDecorators.__init__(self, [], 0)
- except ImportError:
- Decorators = list
-
- def build_function(name, args=None, defaults=None, flag=0, doc=None):
- """create and initialize a astng Function node"""
- args, defaults = args or [], defaults or []
- # first argument is now a list of decorators
- func = nodes.Function(Decorators(), name, args, defaults, flag, doc,
- nodes.Stmt([]))
- func.code.parent = func
- func.locals = {}
- if args:
- register_arguments(func, args)
- return func
-
-else:
- def build_function(name, args=None, defaults=None, flag=0, doc=None):
- """create and initialize a astng Function node"""
- args, defaults = args or [], defaults or []
- func = nodes.Function(name, args, defaults, flag, doc, nodes.Stmt([]))
- func.code.parent = func
- func.locals = {}
- if args:
- register_arguments(func, args)
- return func
+def build_function(name, args=None, defaults=None, flag=0, doc=None):
+ """create and initialize a astng Function node"""
+ args, defaults = args or [], defaults or []
+ # first argument is now a list of decorators
+ func = nodes.function_factory(name, args, defaults, flag, doc)
+ func.locals = {}
+ if args:
+ register_arguments(func, args)
+ return func
def build_name_assign(name, value):
diff --git a/scoped_nodes.py b/scoped_nodes.py
index fef17fd5..184dc24f 100644
--- a/scoped_nodes.py
+++ b/scoped_nodes.py
@@ -19,9 +19,9 @@ below.
: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
"""
from __future__ import generators
@@ -32,13 +32,13 @@ import sys
from logilab.common.compat import chain, set
-from logilab.astng.utils import extend_class
-from logilab.astng import YES, MANAGER, Instance, InferenceContext, copy_context, \
+from logilab.astng import MANAGER, InferenceContext, copy_context, \
unpack_infer, _infer_stmts, \
Class, Const, Dict, Function, GenExpr, Lambda, \
Module, Name, Pass, Raise, Tuple, Yield
from logilab.astng import NotFoundError, NoDefault, \
ASTNGBuildingException, InferenceError
+from logilab.astng.utils import YES, extend_class, Instance
# module class dict/iterator interface ########################################
diff --git a/test/unittest_builder.py b/test/unittest_builder.py
index 0a62f262..b75a72af 100644
--- a/test/unittest_builder.py
+++ b/test/unittest_builder.py
@@ -27,113 +27,115 @@ from logilab.astng import Module, YES, InferenceError
import data
from data import module as test_module
-class TransformerTC(TestCase):
-
- def setUp(self):
- transformer = patchcomptransformer.ASTNGTransformer()
- self.astng = transformer.parsesuite(open('data/format.py').read())
-
- def test_callfunc_lineno(self):
- stmts = self.astng.getChildNodes()[0].nodes
- # on line 4:
- # function('aeozrijz\
- # earzer', hop)
- discard = stmts[0]
- self.assertIsInstance(discard, nodes.Discard)
- self.assertEquals(discard.fromlineno, 4)
- self.assertEquals(discard.tolineno, 5)
- callfunc = discard.expr
- self.assertIsInstance(callfunc, nodes.CallFunc)
- self.assertEquals(callfunc.fromlineno, 4)
- self.assertEquals(callfunc.tolineno, 5)
- name = callfunc.node
- self.assertIsInstance(name, nodes.Name)
- self.assertEquals(name.fromlineno, 4)
- self.assertEquals(name.tolineno, 4)
- strarg = callfunc.args[0]
- self.assertIsInstance(strarg, nodes.Const)
- self.assertEquals(strarg.fromlineno, 5) # no way for this one (is 4 actually)
- self.assertEquals(strarg.tolineno, 5)
- namearg = callfunc.args[1]
- self.assertIsInstance(namearg, nodes.Name)
- self.assertEquals(namearg.fromlineno, 5)
- self.assertEquals(namearg.tolineno, 5)
- # on line 10:
- # fonction(1,
- # 2,
- # 3,
- # 4)
- discard = stmts[2]
- self.assertIsInstance(discard, nodes.Discard)
- self.assertEquals(discard.fromlineno, 10)
- self.assertEquals(discard.tolineno, 13)
- callfunc = discard.expr
- self.assertIsInstance(callfunc, nodes.CallFunc)
- self.assertEquals(callfunc.fromlineno, 10)
- self.assertEquals(callfunc.tolineno, 13)
- name = callfunc.node
- self.assertIsInstance(name, nodes.Name)
- self.assertEquals(name.fromlineno, 10)
- self.assertEquals(name.tolineno, 10)
- for i, arg in enumerate(callfunc.args):
- self.assertIsInstance(arg, nodes.Const)
- self.assertEquals(arg.fromlineno, 10+i)
- self.assertEquals(arg.tolineno, 10+i)
-
- def test_function_lineno(self):
- stmts = self.astng.getChildNodes()[0].nodes
- # on line 15:
- # def definition(a,
- # b,
- # c):
- # return a + b + c
- function = stmts[3]
- self.assertIsInstance(function, nodes.Function)
- self.assertEquals(function.fromlineno, 15)
- self.assertEquals(function.tolineno, 17)
- code = function.code
- self.assertIsInstance(code, nodes.Stmt)
-## self.assertEquals(code.fromlineno, 18)
-## self.assertEquals(code.tolineno, 18)
- return_ = code.nodes[0]
- self.assertIsInstance(return_, nodes.Return)
- self.assertEquals(return_.fromlineno, 18)
- self.assertEquals(return_.tolineno, 18)
-
- def test_class_lineno(self):
- stmts = self.astng.getChildNodes()[0].nodes
- # on line 20:
- # class debile(dict,
- # object):
- # pass
- class_ = stmts[4]
- self.assertIsInstance(class_, nodes.Class)
- self.assertEquals(class_.fromlineno, 20)
- self.assertEquals(class_.tolineno, 21)
- code = class_.code
- self.assertIsInstance(code, nodes.Stmt)
-## self.assertEquals(code.fromlineno, 18)
-## self.assertEquals(code.tolineno, 18)
- pass_ = code.nodes[0]
- self.assertIsInstance(pass_, nodes.Pass)
- self.assertEquals(pass_.fromlineno, 22)
- self.assertEquals(pass_.tolineno, 22)
-
- def test_if_lineno(self):
- stmts = self.astng.getChildNodes()[0].nodes
- # on line 20:
- # if aaaa: pass
- # else:
- # aaaa,bbbb = 1,2
- # aaaa,bbbb = bbbb,aaaa
- if_ = stmts[5]
- self.assertIsInstance(if_, nodes.If)
- self.assertEquals(if_.fromlineno, 24)
- self.assertEquals(if_.tolineno, 24)
- else_ = if_.else_
- self.assertIsInstance(else_, nodes.Stmt)
- self.assertEquals(else_.fromlineno, 25)
- self.assertEquals(else_.tolineno, 27)
+if nodes.AST_MODE == 'compiler':
+
+ class TransformerTC(TestCase):
+
+ def setUp(self):
+ transformer = patchcomptransformer.ASTNGTransformer()
+ self.astng = transformer.parsesuite(open('data/format.py').read())
+
+ def test_callfunc_lineno(self):
+ stmts = self.astng.getChildNodes()[0].nodes
+ # on line 4:
+ # function('aeozrijz\
+ # earzer', hop)
+ discard = stmts[0]
+ self.assertIsInstance(discard, nodes.Discard)
+ self.assertEquals(discard.fromlineno, 4)
+ self.assertEquals(discard.tolineno, 5)
+ callfunc = discard.expr
+ self.assertIsInstance(callfunc, nodes.CallFunc)
+ self.assertEquals(callfunc.fromlineno, 4)
+ self.assertEquals(callfunc.tolineno, 5)
+ name = callfunc.node
+ self.assertIsInstance(name, nodes.Name)
+ self.assertEquals(name.fromlineno, 4)
+ self.assertEquals(name.tolineno, 4)
+ strarg = callfunc.args[0]
+ self.assertIsInstance(strarg, nodes.Const)
+ self.assertEquals(strarg.fromlineno, 5) # no way for this one (is 4 actually)
+ self.assertEquals(strarg.tolineno, 5)
+ namearg = callfunc.args[1]
+ self.assertIsInstance(namearg, nodes.Name)
+ self.assertEquals(namearg.fromlineno, 5)
+ self.assertEquals(namearg.tolineno, 5)
+ # on line 10:
+ # fonction(1,
+ # 2,
+ # 3,
+ # 4)
+ discard = stmts[2]
+ self.assertIsInstance(discard, nodes.Discard)
+ self.assertEquals(discard.fromlineno, 10)
+ self.assertEquals(discard.tolineno, 13)
+ callfunc = discard.expr
+ self.assertIsInstance(callfunc, nodes.CallFunc)
+ self.assertEquals(callfunc.fromlineno, 10)
+ self.assertEquals(callfunc.tolineno, 13)
+ name = callfunc.node
+ self.assertIsInstance(name, nodes.Name)
+ self.assertEquals(name.fromlineno, 10)
+ self.assertEquals(name.tolineno, 10)
+ for i, arg in enumerate(callfunc.args):
+ self.assertIsInstance(arg, nodes.Const)
+ self.assertEquals(arg.fromlineno, 10+i)
+ self.assertEquals(arg.tolineno, 10+i)
+
+ def test_function_lineno(self):
+ stmts = self.astng.getChildNodes()[0].nodes
+ # on line 15:
+ # def definition(a,
+ # b,
+ # c):
+ # return a + b + c
+ function = stmts[3]
+ self.assertIsInstance(function, nodes.Function)
+ self.assertEquals(function.fromlineno, 15)
+ self.assertEquals(function.tolineno, 17)
+ code = function.code
+ self.assertIsInstance(code, nodes.Stmt)
+ ## self.assertEquals(code.fromlineno, 18)
+ ## self.assertEquals(code.tolineno, 18)
+ return_ = code.nodes[0]
+ self.assertIsInstance(return_, nodes.Return)
+ self.assertEquals(return_.fromlineno, 18)
+ self.assertEquals(return_.tolineno, 18)
+
+ def test_class_lineno(self):
+ stmts = self.astng.getChildNodes()[0].nodes
+ # on line 20:
+ # class debile(dict,
+ # object):
+ # pass
+ class_ = stmts[4]
+ self.assertIsInstance(class_, nodes.Class)
+ self.assertEquals(class_.fromlineno, 20)
+ self.assertEquals(class_.tolineno, 21)
+ code = class_.code
+ self.assertIsInstance(code, nodes.Stmt)
+ ## self.assertEquals(code.fromlineno, 18)
+ ## self.assertEquals(code.tolineno, 18)
+ pass_ = code.nodes[0]
+ self.assertIsInstance(pass_, nodes.Pass)
+ self.assertEquals(pass_.fromlineno, 22)
+ self.assertEquals(pass_.tolineno, 22)
+
+ def test_if_lineno(self):
+ stmts = self.astng.getChildNodes()[0].nodes
+ # on line 20:
+ # if aaaa: pass
+ # else:
+ # aaaa,bbbb = 1,2
+ # aaaa,bbbb = bbbb,aaaa
+ if_ = stmts[5]
+ self.assertIsInstance(if_, nodes.If)
+ self.assertEquals(if_.fromlineno, 24)
+ self.assertEquals(if_.tolineno, 24)
+ else_ = if_.else_
+ self.assertIsInstance(else_, nodes.Stmt)
+ self.assertEquals(else_.fromlineno, 25)
+ self.assertEquals(else_.tolineno, 27)
class BuilderTC(TestCase):
@@ -178,7 +180,9 @@ class BuilderTC(TestCase):
self.assert_(time_astng)
#
unittest_astng = self.builder.inspect_build(unittest)
- self.failUnless(isinstance(builtin_astng['None'], nodes.Const), builtin_astng['None'])
+ self.failUnless(isinstance(builtin_astng['None'], NoneType), builtin_astng['None'])
+ self.failUnless(isinstance(builtin_astng['True'], Bool), builtin_astng['True'])
+ self.failUnless(isinstance(builtin_astng['False'], Bool), builtin_astng['False'])
self.failUnless(isinstance(builtin_astng['Exception'], nodes.From), builtin_astng['Exception'])
self.failUnless(isinstance(builtin_astng['NotImplementedError'], nodes.From))
diff --git a/utils.py b/utils.py
index 084f785b..eabe145c 100644
--- a/utils.py
+++ b/utils.py
@@ -53,17 +53,29 @@ class ASTWalker:
self.handler = handler
self._cache = {}
- def walk(self, node):
+ def walk(self, node, _done=None):
"""walk on the tree from <node>, getting callbacks from handler
"""
+ if _done is None:
+ _done = set()
+ if node in _done:
+ raise AssertionError((id(node), node.parent))
+ _done.add(node)
try:
self.visit(node)
except IgnoreChild:
pass
else:
- for child_node in node.getChildNodes():
- self.walk(child_node)
+ print 'visit', node, id(node)
+ try:
+ for child_node in node.getChildNodes():
+ assert child_node is not node
+ self.walk(child_node, _done)
+ except AttributeError:
+ print node.__class__, id(node.__class__)
+ raise
self.leave(node)
+ assert node.parent is not node
def get_callbacks(self, node):
"""get callbacks from handler for the visited node
@@ -178,6 +190,132 @@ def _try_except_from_branch(node, stmt):
return 'except', i
+# special inference objects ###################################################
+
+class Yes(object):
+ """a yes object"""
+ def __repr__(self):
+ return 'YES'
+ def __getattribute__(self, name):
+ return self
+ def __call__(self, *args, **kwargs):
+ return self
+
+YES = Yes()
+
+class Proxy:
+ """a simple proxy object"""
+ def __init__(self, proxied):
+ self._proxied = proxied
+
+ def __getattr__(self, name):
+ return getattr(self._proxied, name)
+
+ def infer(self, context=None):
+ yield self
+
+
+class InstanceMethod(Proxy):
+ """a special node representing a function bound to an instance"""
+ def __repr__(self):
+ instance = self._proxied.parent.frame()
+ return 'Bound method %s of %s.%s' % (self._proxied.name,
+ instance.root().name,
+ instance.name)
+ __str__ = __repr__
+
+ def is_bound(self):
+ return True
+
+
+class Instance(Proxy):
+ """a special node representing a class instance"""
+ def getattr(self, name, context=None, lookupclass=True):
+ try:
+ return self._proxied.instance_attr(name, context)
+ except NotFoundError:
+ if name == '__class__':
+ return [self._proxied]
+ if name == '__name__':
+ # access to __name__ gives undefined member on class
+ # instances but not on class objects
+ raise NotFoundError(name)
+ if lookupclass:
+ return self._proxied.getattr(name, context)
+ raise NotFoundError(name)
+
+ def igetattr(self, name, context=None):
+ """infered getattr"""
+ try:
+ # XXX frame should be self._proxied, or not ?
+ return _infer_stmts(
+ self._wrap_attr(self.getattr(name, context, lookupclass=False)),
+ context, frame=self)
+ except NotFoundError:
+ try:
+ # fallback to class'igetattr since it has some logic to handle
+ # descriptors
+ return self._wrap_attr(self._proxied.igetattr(name, context))
+ except NotFoundError:
+ raise InferenceError(name)
+
+ def _wrap_attr(self, attrs):
+ """wrap bound methods of attrs in a InstanceMethod proxies"""
+ # Guess which attrs are used in inference.
+ def wrap(attr):
+ if isinstance(attr, Function) and attr.type == 'method':
+ return InstanceMethod(attr)
+ else:
+ return attr
+ return imap(wrap, attrs)
+
+ def infer_call_result(self, caller, context=None):
+ """infer what's a class instance is returning when called"""
+ infered = False
+ for node in self._proxied.igetattr('__call__', context):
+ for res in node.infer_call_result(caller, context):
+ infered = True
+ yield res
+ if not infered:
+ raise InferenceError()
+
+ def __repr__(self):
+ return 'Instance of %s.%s' % (self._proxied.root().name,
+ self._proxied.name)
+ __str__ = __repr__
+
+ def callable(self):
+ try:
+ self._proxied.getattr('__call__')
+ return True
+ except NotFoundError:
+ return False
+
+ def pytype(self):
+ return self._proxied.qname()
+
+class Generator(Proxy):
+ """a special node representing a generator"""
+ def callable(self):
+ return True
+
+ def pytype(self):
+ return '__builtin__.generator'
+
+# additional nodes ##########################################################
+
+class NoneType(Instance):
+ """None value (instead of Name('None')"""
+
+NONE = NoneType(None)
+
+class Bool(Instance):
+ """None value (instead of Name('True') / Name('False')"""
+ def __init__(self, value):
+ self.value = value
+TRUE = Bool(True)
+FALSE = Bool(True)
+
# inference utilities #########################################################
def infer_end(self, context=None):