summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Jehannet <julien.jehannet@logilab.fr>2011-01-18 09:50:25 +0100
committerJulien Jehannet <julien.jehannet@logilab.fr>2011-01-18 09:50:25 +0100
commitdb7ea8451a98b437ee635e99d83944f2a9d3acc2 (patch)
tree0316a63d8dcd1244056c5bedf4f77aba9e5ed986
parenta73ee39ee8abb46d5785f890295aa128766927ed (diff)
parent3c3c34d9706ff5f9cea065b5544f18a36bb8216c (diff)
downloadastroid-git-db7ea8451a98b437ee635e99d83944f2a9d3acc2.tar.gz
(merge py3k changes)
-rw-r--r--.hgtags2
-rw-r--r--ChangeLog31
-rw-r--r--__init__.py4
-rw-r--r--__pkginfo__.py2
-rw-r--r--_nodes_ast.py725
-rw-r--r--as_string.py16
-rw-r--r--bases.py72
-rw-r--r--builder.py256
-rw-r--r--debian/changelog6
-rw-r--r--exceptions.py (renamed from _exceptions.py)4
-rw-r--r--inference.py45
-rw-r--r--inspector.py2
-rw-r--r--manager.py162
-rw-r--r--mixins.py41
-rw-r--r--node_classes.py143
-rw-r--r--protocols.py7
-rw-r--r--raw_building.py217
-rw-r--r--rebuilder.py902
-rw-r--r--scoped_nodes.py75
-rw-r--r--test/data/module2.py1
-rw-r--r--test/unittest_builder.py66
-rw-r--r--test/unittest_inference.py22
-rw-r--r--test/unittest_inspector.py4
-rw-r--r--test/unittest_lookup.py14
-rw-r--r--test/unittest_manager.py79
-rw-r--r--test/unittest_nodes.py131
-rw-r--r--test/unittest_regrtest.py9
-rw-r--r--test/unittest_scoped_nodes.py38
-rw-r--r--utils.py235
29 files changed, 1559 insertions, 1752 deletions
diff --git a/.hgtags b/.hgtags
index 3126511c..9a93e4c2 100644
--- a/.hgtags
+++ b/.hgtags
@@ -27,3 +27,5 @@ ba70ecabe1f5d7d87b908aaf33381d0a1c1dd11d logilab-astng-version-0.20.4
b3681c63587356f66af36774e39453b0fb129475 logilab-astng-debian-version-0.20.4-1
fd1a1cfd5d2779619787fa49100c033d700e3061 logilab-astng-version-0.21.0
8c96858bbfa68c19f0fe8939d2f53f0747bf7042 logilab-astng-debian-version-0.21.0-1
+aef5848d64cc1202755502b3d1caf96d6745a2cf logilab-astng-version-0.21.1
+71d957477e3a4840b37ead8dd2d4ec38162427e5 logilab-astng-debian-version-0.21.1-1
diff --git a/ChangeLog b/ChangeLog
index daebaa87..bf2bb361 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,25 @@
Change log for the astng package
================================
+2011-01-11 -- 0.21.1
+ * python3: handle file encoding; fix a lot of tests
+
+ * fix #52006: "True" and "False" can be assigned as variable in Python2x
+
+ * fix #8847: pylint doesn't understand function attributes at all
+
+ * fix #8774: iterator / generator / next method
+
+ * fix bad building of ast from living object w/ container classes
+ (eg dict, set, list, tuple): contained elements should be turned to
+ ast as well (not doing it will much probably cause crash later)
+
+ * somewhat fix #57299 and other similar issue: Exception when
+ trying to validate file using PyQt's PyQt4.QtCore module: we can't
+ do much about it but at least catch such exception to avoid crash
+
+
+
2010-11-15 -- 0.21.0
* python3.x: first python3.x release
@@ -8,8 +27,9 @@ Change log for the astng package
* python2.4: drop python < 2.4 support
-2010-10-27 -- 0.20.4
+
+2010-10-27 -- 0.20.4
* fix #37868 #37665 #33638 #37909: import problems with absolute_import_activated
* fix #8969: false positive when importing from zip-safe eggs
@@ -20,14 +40,17 @@ Change log for the astng package
* important progress on Py3k compatibility
-2010-09-28 -- 0.20.3
- * restored python 2.3 compatibility
- * fix #45959: AttributeError: 'NoneType' object has no attribute 'frame', due
+2010-09-28 -- 0.20.3
+ * restored python 2.3 compatibility
+
+ * fix #45959: AttributeError: 'NoneType' object has no attribute 'frame', due
to handling of __class__ when importing from living object (because of missing
source code or C-compiled object)
+
+
2010-09-10 -- 0.20.2
* fix astng building bug: we've to set module.package flag at the node
creation time otherwise we'll miss this information when infering relative
diff --git a/__init__.py b/__init__.py
index 840c53c6..e3cd8a08 100644
--- a/__init__.py
+++ b/__init__.py
@@ -60,7 +60,7 @@ __doctype__ = "restructuredtext en"
# WARNING: internal imports order matters !
# make all exception classes accessible from astng package
-from logilab.astng._exceptions import *
+from logilab.astng.exceptions import *
# make all node classes accessible from astng package
from logilab.astng.nodes import *
@@ -76,7 +76,7 @@ from logilab.astng.scoped_nodes import builtin_lookup
# make a manager instance (borg) as well as Project and Package classes
# accessible from astng package
-from logilab.astng.manager import ASTNGManager, Project, Package
+from logilab.astng.manager import ASTNGManager, Project
MANAGER = ASTNGManager()
del ASTNGManager
diff --git a/__pkginfo__.py b/__pkginfo__.py
index 12d0d9dc..f29d646a 100644
--- a/__pkginfo__.py
+++ b/__pkginfo__.py
@@ -24,7 +24,7 @@ distname = 'logilab-astng'
modname = 'astng'
subpackage_of = 'logilab'
-numversion = (0, 21, 0)
+numversion = (0, 21, 1)
version = '.'.join([str(num) for num in numversion])
install_requires = ['logilab-common >= 0.53.0']
diff --git a/_nodes_ast.py b/_nodes_ast.py
deleted file mode 100644
index 3f48f0a3..00000000
--- a/_nodes_ast.py
+++ /dev/null
@@ -1,725 +0,0 @@
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser 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.
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-# copyright 2003-2010 Sylvain Thenault, all rights reserved.
-# contact mailto:thenault@gmail.com
-#
-# This file is part of logilab-astng.
-#
-# logilab-astng is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by the
-# Free Software Foundation, either version 2.1 of the License, or (at your
-# option) any later version.
-#
-# logilab-astng 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 Lesser General Public License
-# for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""python 2.5 builtin _ast compatibility module
-
-"""
-
-__docformat__ = "restructuredtext en"
-
-
-# aliased nodes
-from _ast import AST as Node, Expr as Discard
-# nodes which are not part of astng
-from _ast import (
- # binary operators
- Add as _Add, Div as _Div, FloorDiv as _FloorDiv,
- Mod as _Mod, Mult as _Mult, Pow as _Pow, Sub as _Sub,
- BitAnd as _BitAnd, BitOr as _BitOr, BitXor as _BitXor,
- LShift as _LShift, RShift as _RShift,
- # logical operators
- And as _And, Or as _Or,
- # unary operators
- UAdd as _UAdd, USub as _USub, Not as _Not, Invert as _Invert,
- # comparison operators
- Eq as _Eq, Gt as _Gt, GtE as _GtE, In as _In, Is as _Is,
- IsNot as _IsNot, Lt as _Lt, LtE as _LtE, NotEq as _NotEq,
- NotIn as _NotIn,
- # other nodes which are not part of astng
- Num as _Num, Str as _Str, Load as _Load, Store as _Store, Del as _Del,
- )
-
-from logilab.astng import nodes as new
-import sys
-
-_BIN_OP_CLASSES = {_Add: '+',
- _BitAnd: '&',
- _BitOr: '|',
- _BitXor: '^',
- _Div: '/',
- _FloorDiv: '//',
- _Mod: '%',
- _Mult: '*',
- _Pow: '**',
- _Sub: '-',
- _LShift: '<<',
- _RShift: '>>'}
-
-_BOOL_OP_CLASSES = {_And: 'and',
- _Or: 'or'}
-
-_UNARY_OP_CLASSES = {_UAdd: '+',
- _USub: '-',
- _Not: 'not',
- _Invert: '~'}
-
-_CMP_OP_CLASSES = {_Eq: '==',
- _Gt: '>',
- _GtE: '>=',
- _In: 'in',
- _Is: 'is',
- _IsNot: 'is not',
- _Lt: '<',
- _LtE: '<=',
- _NotEq: '!=',
- _NotIn: 'not in'}
-
-
-CONST_NAME_TRANSFORMS = {'None': None,
- 'True': True,
- 'False': False}
-
-
-def _init_set_doc(node, newnode):
- newnode.doc = None
- try:
- if isinstance(node.body[0], Discard) and isinstance(node.body[0].value, _Str):
- newnode.tolineno = node.body[0].lineno
- newnode.doc = node.body[0].value.s
- node.body = node.body[1:]
-
- except IndexError:
- pass # ast built from scratch
-
-
-from logilab.astng.rebuilder import RebuildVisitor
-# _ast rebuilder ##############################################################
-
-def _lineno_parent(oldnode, newnode, parent):
- newnode.parent = parent
- if hasattr(oldnode, 'lineno'):
- newnode.lineno = oldnode.lineno
-
-class TreeRebuilder(RebuildVisitor):
- """Rebuilds the _ast tree to become an ASTNG tree"""
-
- def _set_infos(self, oldnode, newnode, parent):
- newnode.parent = parent
- if hasattr(oldnode, 'lineno'):
- newnode.lineno = oldnode.lineno
- last = newnode.last_child()
- newnode.set_line_info(last) # set_line_info accepts None
-
- def visit_arguments(self, node, parent):
- """visit a Arguments node by returning a fresh instance of it"""
- newnode = new.Arguments()
- _lineno_parent(node, newnode, parent)
- self.asscontext = "Ass"
- newnode.args = [self.visit(child, newnode) for child in node.args]
- self.asscontext = None
- newnode.defaults = [self.visit(child, newnode) for child in node.defaults]
- newnode.vararg = node.vararg
- newnode.kwarg = node.kwarg
- self._save_argument_name(newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_assattr(self, node, parent):
- """visit a AssAttr node by returning a fresh instance of it"""
- assc, self.asscontext = self.asscontext, None
- newnode = new.AssAttr()
- _lineno_parent(node, newnode, parent)
- newnode.expr = self.visit(node.expr, newnode)
- self.asscontext = assc
- self._delayed_assattr.append(newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_assert(self, node, parent):
- """visit a Assert node by returning a fresh instance of it"""
- newnode = new.Assert()
- _lineno_parent(node, newnode, parent)
- newnode.test = self.visit(node.test, newnode)
- newnode.fail = self.visit(node.msg, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_assign(self, node, parent):
- """visit a Assign node by returning a fresh instance of it"""
- newnode = new.Assign()
- _lineno_parent(node, newnode, parent)
- self.asscontext = "Ass"
- newnode.targets = [self.visit(child, newnode) for child in node.targets]
- self.asscontext = None
- newnode.value = self.visit(node.value, newnode)
- self._set_assign_infos(newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_assname(self, node, parent, node_name=None):
- '''visit a node and return a AssName node'''
- newnode = new.AssName()
- self._set_infos(node, newnode, parent)
- newnode.name = node_name
- self._save_assignment(newnode)
- return newnode
-
- def visit_augassign(self, node, parent):
- """visit a AugAssign node by returning a fresh instance of it"""
- newnode = new.AugAssign()
- _lineno_parent(node, newnode, parent)
- newnode.op = _BIN_OP_CLASSES[node.op.__class__] + "="
- self.asscontext = "Ass"
- newnode.target = self.visit(node.target, newnode)
- self.asscontext = None
- newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_backquote(self, node, parent):
- """visit a Backquote node by returning a fresh instance of it"""
- newnode = new.Backquote()
- _lineno_parent(node, newnode, parent)
- newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_binop(self, node, parent):
- """visit a BinOp node by returning a fresh instance of it"""
- newnode = new.BinOp()
- _lineno_parent(node, newnode, parent)
- newnode.left = self.visit(node.left, newnode)
- newnode.right = self.visit(node.right, newnode)
- newnode.op = _BIN_OP_CLASSES[node.op.__class__]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_boolop(self, node, parent):
- """visit a BoolOp node by returning a fresh instance of it"""
- newnode = new.BoolOp()
- _lineno_parent(node, newnode, parent)
- newnode.values = [self.visit(child, newnode) for child in node.values]
- newnode.op = _BOOL_OP_CLASSES[node.op.__class__]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_callfunc(self, node, parent):
- """visit a CallFunc node by returning a fresh instance of it"""
- newnode = new.CallFunc()
- _lineno_parent(node, newnode, parent)
- newnode.func = self.visit(node.func, newnode)
- newnode.args = [self.visit(child, newnode) for child in node.args]
- newnode.starargs = self.visit(node.starargs, newnode)
- newnode.kwargs = self.visit(node.kwargs, newnode)
- newnode.args.extend(self.visit(child, newnode) for child in node.keywords)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def _visit_class(self, node, parent):
- """visit a Class node by returning a fresh instance of it"""
- newnode = new.Class(node.name, None)
- _lineno_parent(node, newnode, parent)
- _init_set_doc(node, newnode)
- newnode.bases = [self.visit(child, newnode) for child in node.bases]
- newnode.body = [self.visit(child, newnode) for child in node.body]
- if 'decorator_list' in node._fields and node.decorator_list:# py >= 2.6
- newnode.decorators = self.visit_decorators(node, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_compare(self, node, parent):
- """visit a Compare node by returning a fresh instance of it"""
- newnode = new.Compare()
- _lineno_parent(node, newnode, parent)
- newnode.left = self.visit(node.left, newnode)
- newnode.ops = [(_CMP_OP_CLASSES[op.__class__], self.visit(expr, newnode))
- for (op, expr) in zip(node.ops, node.comparators)]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_comprehension(self, node, parent):
- """visit a Comprehension node by returning a fresh instance of it"""
- newnode = new.Comprehension()
- _lineno_parent(node, newnode, parent)
- self.asscontext = "Ass"
- newnode.target = self.visit(node.target, newnode)
- self.asscontext = None
- newnode.iter = self.visit(node.iter, newnode)
- newnode.ifs = [self.visit(child, newnode) for child in node.ifs]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_decorators(self, node, parent):
- """visit a Decorators node by returning a fresh instance of it"""
- # /!\ node is actually a _ast.Function node while
- # parent is a astng.nodes.Function node
- newnode = new.Decorators()
- _lineno_parent(node, newnode, parent)
- if 'decorators' in node._fields: # py < 2.6, i.e. 2.5
- decorators = node.decorators
- else:
- decorators= node.decorator_list
- newnode.nodes = [self.visit(child, newnode) for child in decorators]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_delete(self, node, parent):
- """visit a Delete node by returning a fresh instance of it"""
- newnode = new.Delete()
- _lineno_parent(node, newnode, parent)
- self.asscontext = "Del"
- newnode.targets = [self.visit(child, newnode) for child in node.targets]
- self.asscontext = None
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_dict(self, node, parent):
- """visit a Dict node by returning a fresh instance of it"""
- newnode = new.Dict()
- _lineno_parent(node, newnode, parent)
- newnode.items = [(self.visit(key, newnode), self.visit(value, newnode))
- for key, value in zip(node.keys, node.values)]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_dictcomp(self, node, parent):
- """visit a DictComp node by returning a fresh instance of it"""
- newnode = new.DictComp()
- _lineno_parent(node, newnode, parent)
- newnode.key = self.visit(node.key, newnode)
- newnode.value = self.visit(node.value, newnode)
- newnode.generators = [self.visit(child, newnode)
- for child in node.generators]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_discard(self, node, parent):
- """visit a Discard node by returning a fresh instance of it"""
- newnode = new.Discard()
- _lineno_parent(node, newnode, parent)
- newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_excepthandler(self, node, parent):
- """visit an ExceptHandler node by returning a fresh instance of it"""
- newnode = new.ExceptHandler()
- _lineno_parent(node, newnode, parent)
- newnode.type = self.visit(node.type, newnode)
- if node.name is not None:
- # /!\ node.name can be a tuple
- self.asscontext = "Ass"
- newnode.name = self.visit(node.name, newnode)
- self.asscontext = None
- newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_exec(self, node, parent):
- """visit an Exec node by returning a fresh instance of it"""
- newnode = new.Exec()
- _lineno_parent(node, newnode, parent)
- newnode.expr = self.visit(node.body, newnode)
- newnode.globals = self.visit(node.globals, newnode)
- newnode.locals = self.visit(node.locals, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_extslice(self, node, parent):
- """visit an ExtSlice node by returning a fresh instance of it"""
- newnode = new.ExtSlice()
- _lineno_parent(node, newnode, parent)
- newnode.dims = [self.visit(dim, newnode) for dim in node.dims]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_for(self, node, parent):
- """visit a For node by returning a fresh instance of it"""
- newnode = new.For()
- _lineno_parent(node, newnode, parent)
- self.asscontext = "Ass"
- newnode.target = self.visit(node.target, newnode)
- self.asscontext = None
- newnode.iter = self.visit(node.iter, newnode)
- newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_from(self, node, parent):
- """visit a From node by returning a fresh instance of it"""
- names = [(alias.name, alias.asname) for alias in node.names]
- newnode = new.From(node.module or '', names, node.level)
- self._set_infos(node, newnode, parent)
- self._store_from_node(newnode)
-
- return newnode
-
- def _visit_function(self, node, parent):
- """visit a Function node by returning a fresh instance of it"""
- newnode = new.Function(node.name, None)
- _lineno_parent(node, newnode, parent)
- _init_set_doc(node, newnode)
- newnode.args = self.visit(node.args, newnode)
- newnode.body = [self.visit(child, newnode) for child in node.body]
- if 'decorators' in node._fields: # py < 2.6
- attr = 'decorators'
- else:
- attr = 'decorator_list'
- decorators = getattr(node, attr)
- if decorators:
- newnode.decorators = self.visit_decorators(node, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_genexpr(self, node, parent):
- """visit a GenExpr node by returning a fresh instance of it"""
- newnode = new.GenExpr()
- _lineno_parent(node, newnode, parent)
- newnode.elt = self.visit(node.elt, newnode)
- newnode.generators = [self.visit(child, newnode) for child in node.generators]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_getattr(self, node, parent):
- """visit a Getattr node by returning a fresh instance of it"""
- if self.asscontext == "Del":
- # FIXME : maybe we should reintroduce and visit_delattr ?
- # for instance, deactivating asscontext
- newnode = new.DelAttr()
- elif self.asscontext == "Ass":
- # FIXME : maybe we should call visit_assattr ?
- newnode = new.AssAttr()
- self._delayed_assattr.append(newnode)
- else:
- newnode = new.Getattr()
- _lineno_parent(node, newnode, parent)
- asscontext, self.asscontext = self.asscontext, None
- newnode.expr = self.visit(node.value, newnode)
- self.asscontext = asscontext
- newnode.attrname = node.attr
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_if(self, node, parent):
- """visit a If node by returning a fresh instance of it"""
- newnode = new.If()
- _lineno_parent(node, newnode, parent)
- newnode.test = self.visit(node.test, newnode)
- newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_ifexp(self, node, parent):
- """visit a IfExp node by returning a fresh instance of it"""
- newnode = new.IfExp()
- _lineno_parent(node, newnode, parent)
- newnode.test = self.visit(node.test, newnode)
- newnode.body = self.visit(node.body, newnode)
- newnode.orelse = self.visit(node.orelse, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_import(self, node, parent):
- """visit a Import node by returning a fresh instance of it"""
- newnode = new.Import()
- self._set_infos(node, newnode, parent)
- newnode.names = [(alias.name, alias.asname) for alias in node.names]
- self._save_import_locals(newnode)
- return newnode
-
- def visit_index(self, node, parent):
- """visit a Index node by returning a fresh instance of it"""
- newnode = new.Index()
- _lineno_parent(node, newnode, parent)
- newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_keyword(self, node, parent):
- """visit a Keyword node by returning a fresh instance of it"""
- newnode = new.Keyword()
- _lineno_parent(node, newnode, parent)
- newnode.arg = node.arg
- newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_lambda(self, node, parent):
- """visit a Lambda node by returning a fresh instance of it"""
- newnode = new.Lambda()
- _lineno_parent(node, newnode, parent)
- newnode.args = self.visit(node.args, newnode)
- newnode.body = self.visit(node.body, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_list(self, node, parent):
- """visit a List node by returning a fresh instance of it"""
- newnode = new.List()
- _lineno_parent(node, newnode, parent)
- newnode.elts = [self.visit(child, newnode) for child in node.elts]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_listcomp(self, node, parent):
- """visit a ListComp node by returning a fresh instance of it"""
- newnode = new.ListComp()
- _lineno_parent(node, newnode, parent)
- newnode.elt = self.visit(node.elt, newnode)
- newnode.generators = [self.visit(child, newnode)
- for child in node.generators]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_module(self, node, modname, package):
- """visit a Module node by returning a fresh instance of it"""
- newnode = new.Module(modname, None)
- newnode.package = package
- _lineno_parent(node, newnode, parent=None)
- _init_set_doc(node, newnode)
- newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_name(self, node, parent):
- """visit a Name node by returning a fresh instance of it"""
- if node.id in CONST_NAME_TRANSFORMS:
- newnode = new.Const(CONST_NAME_TRANSFORMS[node.id])
- self._set_infos(node, newnode, parent)
- return newnode
- if self.asscontext == "Del":
- newnode = new.DelName()
- elif self.asscontext is not None: # Ass
- assert self.asscontext == "Ass"
- newnode = new.AssName()
- else:
- newnode = new.Name()
- _lineno_parent(node, newnode, parent)
- newnode.name = node.id
- # XXX REMOVE me :
- if self.asscontext in ('Del', 'Ass'): # 'Aug' ??
- self._save_assignment(newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_bytes(self, node, parent):
- """visit a Bytes node by returning a fresh instance of Const"""
- newnode = new.Const(node.s)
- self._set_infos(node, newnode, parent)
- return newnode
-
- def visit_num(self, node, parent):
- """visit a Num node by returning a fresh instance of Const"""
- newnode = new.Const(node.n)
- self._set_infos(node, newnode, parent)
- return newnode
-
- def visit_str(self, node, parent):
- """visit a Str node by returning a fresh instance of Const"""
- newnode = new.Const(node.s)
- self._set_infos(node, newnode, parent)
- return newnode
-
- def visit_print(self, node, parent):
- """visit a Print node by returning a fresh instance of it"""
- newnode = new.Print()
- _lineno_parent(node, newnode, parent)
- newnode.nl = node.nl
- newnode.dest = self.visit(node.dest, newnode)
- newnode.values = [self.visit(child, newnode) for child in node.values]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_raise(self, node, parent):
- """visit a Raise node by returning a fresh instance of it"""
- newnode = new.Raise()
- _lineno_parent(node, newnode, parent)
- newnode.exc = self.visit(node.type, newnode)
- newnode.inst = self.visit(node.inst, newnode)
- newnode.tback = self.visit(node.tback, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_return(self, node, parent):
- """visit a Return node by returning a fresh instance of it"""
- newnode = new.Return()
- _lineno_parent(node, newnode, parent)
- newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_set(self, node, parent):
- """visit a Tuple node by returning a fresh instance of it"""
- newnode = new.Set()
- _lineno_parent(node, newnode, parent)
- newnode.elts = [self.visit(child, newnode) for child in node.elts]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_setcomp(self, node, parent):
- """visit a SetComp node by returning a fresh instance of it"""
- newnode = new.SetComp()
- _lineno_parent(node, newnode, parent)
- newnode.elt = self.visit(node.elt, newnode)
- newnode.generators = [self.visit(child, newnode)
- for child in node.generators]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_slice(self, node, parent):
- """visit a Slice node by returning a fresh instance of it"""
- newnode = new.Slice()
- _lineno_parent(node, newnode, parent)
- newnode.lower = self.visit(node.lower, newnode)
- newnode.upper = self.visit(node.upper, newnode)
- newnode.step = self.visit(node.step, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_subscript(self, node, parent):
- """visit a Subscript node by returning a fresh instance of it"""
- newnode = new.Subscript()
- _lineno_parent(node, newnode, parent)
- subcontext, self.asscontext = self.asscontext, None
- newnode.value = self.visit(node.value, newnode)
- newnode.slice = self.visit(node.slice, newnode)
- self.asscontext = subcontext
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_tryexcept(self, node, parent):
- """visit a TryExcept node by returning a fresh instance of it"""
- newnode = new.TryExcept()
- _lineno_parent(node, newnode, parent)
- newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.handlers = [self.visit(child, newnode) for child in node.handlers]
- newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_tryfinally(self, node, parent):
- """visit a TryFinally node by returning a fresh instance of it"""
- newnode = new.TryFinally()
- _lineno_parent(node, newnode, parent)
- newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_tuple(self, node, parent):
- """visit a Tuple node by returning a fresh instance of it"""
- newnode = new.Tuple()
- _lineno_parent(node, newnode, parent)
- newnode.elts = [self.visit(child, newnode) for child in node.elts]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_unaryop(self, node, parent):
- """visit a UnaryOp node by returning a fresh instance of it"""
- newnode = new.UnaryOp()
- _lineno_parent(node, newnode, parent)
- newnode.operand = self.visit(node.operand, newnode)
- newnode.op = _UNARY_OP_CLASSES[node.op.__class__]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_while(self, node, parent):
- """visit a While node by returning a fresh instance of it"""
- newnode = new.While()
- _lineno_parent(node, newnode, parent)
- newnode.test = self.visit(node.test, newnode)
- newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_with(self, node, parent):
- """visit a With node by returning a fresh instance of it"""
- newnode = new.With()
- _lineno_parent(node, newnode, parent)
- newnode.expr = self.visit(node.context_expr, newnode)
- self.asscontext = "Ass"
- newnode.vars = self.visit(node.optional_vars, newnode)
- self.asscontext = None
- newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_yield(self, node, parent):
- """visit a Yield node by returning a fresh instance of it"""
- newnode = new.Yield()
- _lineno_parent(node, newnode, parent)
- newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
-
-class TreeRebuilder3k(TreeRebuilder):
- """extend and overwrite TreeRebuilder for python3k"""
-
- def visit_arg(self, node, parent):
- """visit a arg node by returning a fresh AssName instance"""
- # the <arg> node is coming from py>=3.0, but we use AssName in py2.x
- # XXX or we should instead introduce a Arg node in astng ?
- return self.visit_assname(node, parent, node.arg)
-
- def visit_excepthandler(self, node, parent):
- """visit an ExceptHandler node by returning a fresh instance of it"""
- newnode = new.ExceptHandler()
- _lineno_parent(node, newnode, parent)
- newnode.type = self.visit(node.type, newnode)
- if node.name is not None:
- newnode.name = self.visit_assname(node, newnode, node.name)
- newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_nonlocal(self, node, parent):
- """visit a Nonlocal node and return a new instance of it"""
- newnode = new.Nonlocal(node.names)
- self._set_infos(node, newnode, parent)
- return newnode
-
- def visit_raise(self, node, parent):
- """visit a Raise node by returning a fresh instance of it"""
- newnode = new.Raise()
- _lineno_parent(node, newnode, parent)
- # no traceback; anyway it is not used in Pylint
- newnode.exc = self.visit(node.exc, newnode)
- newnode.cause = self.visit(node.cause, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
- def visit_starred(self, node, parent):
- """visit a Starred node and return a new instance of it"""
- newnode = new.Starred()
- _lineno_parent(node, newnode, parent)
- newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
- return newnode
-
-
-if sys.version_info >= (3, 0):
- TreeRebuilder = TreeRebuilder3k
-
-
diff --git a/as_string.py b/as_string.py
index d9b53dfe..3eeda096 100644
--- a/as_string.py
+++ b/as_string.py
@@ -34,7 +34,6 @@ It will probably not work on compiler.ast or _ast trees.
"""
import sys
-from logilab.astng.utils import ASTVisitor
INDENT = ' ' # 4 spaces ; keep indentation variable
@@ -50,7 +49,7 @@ def _import_string(names):
return ', '.join(_names)
-class AsStringVisitor(ASTVisitor):
+class AsStringVisitor(object):
"""Visitor to render an ASTNG node as string """
def __call__(self, node):
@@ -179,7 +178,11 @@ class AsStringVisitor(ASTVisitor):
def visit_discard(self, node):
"""return an astng.Discard node as string"""
return node.value.accept(self)
-
+
+ def visit_emptynode(self, node):
+ """dummy method for visiting an Empty node"""
+ return ''
+
def visit_excepthandler(self, node):
if node.type:
if node.name:
@@ -225,8 +228,8 @@ class AsStringVisitor(ASTVisitor):
def visit_from(self, node):
"""return an astng.From node as string"""
- # XXX level
- return 'from %s import %s' % (node.modname, _import_string(node.names))
+ return 'from %s import %s' % ('.' * (node.level or 0) + node.modname,
+ _import_string(node.names))
def visit_function(self, node):
"""return an astng.Function node as string"""
@@ -392,7 +395,8 @@ class AsStringVisitor(ASTVisitor):
def visit_yield(self, node):
"""yield an ast.Yield node as string"""
- return 'yield %s' % node.value.accept(self)
+ yi_val = node.value and (" " + node.value.accept(self)) or ""
+ return 'yield' + yi_val
class AsStringVisitor3k(AsStringVisitor):
diff --git a/bases.py b/bases.py
index f03a13e0..44dfd568 100644
--- a/bases.py
+++ b/bases.py
@@ -1,16 +1,4 @@
# -*- coding: utf-8 -*-
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser 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.
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
# copyright 2003-2010 Sylvain Thenault, all rights reserved.
@@ -30,34 +18,21 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-
"""This module contains base classes and functions for the nodes and some
inference utils.
-
"""
-from __future__ import generators
-
__docformat__ = "restructuredtext en"
-try:
- from _ast import AST
- del AST
- class BaseClass(object):
- pass
-except ImportError:
- class BaseClass:
- pass
-
from logilab.common.compat import builtins
-from logilab.astng._exceptions import InferenceError, ASTNGError, \
+from logilab.astng.exceptions import InferenceError, ASTNGError, \
NotFoundError, UnresolvableName
from logilab.astng.as_string import as_string
BUILTINS_NAME = builtins.__name__
-class Proxy(BaseClass):
+class Proxy(object):
"""a simple proxy object"""
_proxied = None
@@ -75,9 +50,8 @@ class Proxy(BaseClass):
def infer(self, context=None):
yield self
-# Inference ##################################################################
-
+# Inference ##################################################################
class InferenceContext(object):
__slots__ = ('path', 'lookupname', 'callcontext', 'boundnode')
@@ -203,7 +177,7 @@ class Instance(Proxy):
"""wrap bound methods of attrs in a InstanceMethod proxies"""
for attr in attrs:
if isinstance(attr, UnboundMethod):
- if '__builtin__.property' in attr.decoratornames():
+ if BUILTINS_NAME + '.property' in attr.decoratornames():
for infered in attr.infer_call_result(self, context):
yield infered
else:
@@ -264,6 +238,14 @@ class UnboundMethod(Proxy):
return iter((self._proxied,))
return super(UnboundMethod, self).igetattr(name, context)
+ def infer_call_result(self, caller, context):
+ # If we're unbound method __new__ of builtin object, the result is an
+ # instance of the class given as first argument.
+ if (self._proxied.name == '__new__' and
+ self._proxied.parent.frame().qname() == '__builtin__.object'):
+ return (x is YES and x or Instance(x) for x in caller.args[0].infer())
+ return self._proxied.infer_call_result(caller, context)
+
class BoundMethod(UnboundMethod):
"""a special node representing a method bound to an instance"""
@@ -280,7 +262,7 @@ class BoundMethod(UnboundMethod):
return self._proxied.infer_call_result(caller, context)
-class Generator(Proxy):
+class Generator(Instance):
"""a special node representing a generator"""
def callable(self):
return True
@@ -291,6 +273,12 @@ class Generator(Proxy):
def display_type(self):
return 'Generator'
+ def __repr__(self):
+ return '<Generator(%s) l.%s at 0x%s>' % (self._proxied.name, self.lineno, id(self))
+
+ def __str__(self):
+ return 'Generator(%s)' % (self._proxied.name)
+
# decorators ##################################################################
@@ -336,7 +324,7 @@ def raise_if_nothing_infered(func):
# Node ######################################################################
-class NodeNG(BaseClass):
+class NodeNG(object):
"""Base Class for all ASTNG node classes.
It represents a node of the new abstract syntax tree.
@@ -574,6 +562,26 @@ class NodeNG(BaseClass):
return "\n".join(result)
+class Statement(NodeNG):
+ """Statement node adding a few attributes"""
+ is_statement = True
+
+ def next_sibling(self):
+ """return the next sibling statement"""
+ stmts = self.parent.child_sequence(self)
+ index = stmts.index(self)
+ try:
+ return stmts[index +1]
+ except IndexError:
+ pass
+
+ def previous_sibling(self):
+ """return the previous sibling statement"""
+ stmts = self.parent.child_sequence(self)
+ index = stmts.index(self)
+ if index >= 1:
+ return stmts[index -1]
+
INDENT = " "
def _repr_tree(node, result, indent='', _done=None, ids=False):
diff --git a/builder.py b/builder.py
index 7aa0f247..799cc7e7 100644
--- a/builder.py
+++ b/builder.py
@@ -27,45 +27,75 @@ at the same time.
__docformat__ = "restructuredtext en"
-import sys
+import sys, re
from os.path import splitext, basename, dirname, exists, abspath
-from inspect import isfunction, ismethod, ismethoddescriptor, isclass, \
- isbuiltin
-from inspect import isdatadescriptor
from logilab.common.modutils import modpath_from_file
-from logilab.astng._exceptions import ASTNGBuildingException
-from logilab.astng.raw_building import build_module, object_build_class, \
- object_build_function, object_build_datadescriptor, attach_dummy_node, \
- object_build_methoddescriptor, attach_const_node, attach_import_node
+from logilab.astng.exceptions import ASTNGBuildingException, InferenceError
+from logilab.astng.raw_building import InspectBuilder
+from logilab.astng.rebuilder import TreeRebuilder
from logilab.astng.manager import ASTNGManager
+from logilab.astng.bases import YES, Instance
from _ast import PyCF_ONLY_AST
def parse(string):
return compile(string, "<string>", 'exec', PyCF_ONLY_AST)
-from logilab.astng._nodes_ast import TreeRebuilder
+
+if sys.version_info >= (3, 0):
+ from tokenize import detect_encoding
+
+ def open_source_file(filename):
+ byte_stream = open(filename, 'bU')
+ encoding = detect_encoding(byte_stream.readline)[0]
+ stream = open(filename, 'U', encoding=encoding)
+ try:
+ data = stream.read()
+ except UnicodeError, uex: # wrong encodingg
+ # detect_encoding returns utf-8 if no encoding specified
+ msg = 'Wrong (%s) or no encoding specified' % encoding
+ raise ASTNGBuildingException(msg)
+ return stream, encoding, data
+
+else:
+ import re
+
+ _ENCODING_RGX = re.compile("[^#]*#*.*coding[:=]\s*([^\s]+)")
+
+ def _guess_encoding(string):
+ """get encoding from a python file as string or return None if not found
+ """
+ # check for UTF-8 byte-order mark
+ if string.startswith('\xef\xbb\xbf'):
+ return 'UTF-8'
+ for line in string.split('\n', 2)[:2]:
+ # check for encoding declaration
+ match = _ENCODING_RGX.match(line)
+ if match is not None:
+ return match.group(1)
+
+ def open_source_file(filename):
+ """get data for parsing a file"""
+ stream = open(filename, 'U')
+ data = stream.read()
+ encoding = _guess_encoding(data)
+ return stream, encoding, data
# ast NG builder ##############################################################
-class ASTNGBuilder:
- """provide astng building methods
- """
+MANAGER = ASTNGManager()
+
+class ASTNGBuilder(InspectBuilder):
+ """provide astng building methods"""
+ rebuilder = TreeRebuilder()
def __init__(self, manager=None):
- if manager is None:
- manager = ASTNGManager()
- self._manager = manager
- self._module = None
- self._done = None
- self.rebuilder = TreeRebuilder(manager)
- self._dyn_modname_map = {'gtk': 'gtk._gtk'}
+ self._manager = manager or MANAGER
def module_build(self, module, modname=None):
"""build an astng from a living module instance
"""
node = None
- self._module = module
path = getattr(module, '__file__', None)
if path is not None:
path_, ext = splitext(module.__file__)
@@ -77,33 +107,20 @@ class ASTNGBuilder:
node = self.inspect_build(module, modname=modname, path=path)
return node
- def inspect_build(self, module, modname=None, path=None):
- """build astng from a living module (i.e. using inspect)
- this is used when there is no python source code available (either
- because it's a built-in module or because the .py is not available)
- """
- self._module = module
- if modname is None:
- modname = module.__name__
- node = build_module(modname, module.__doc__)
- node.file = node.path = path and abspath(path) or path
- if self._manager is not None:
- self._manager._cache[modname] = node
- node.package = hasattr(module, '__path__')
- self._done = {}
- self.object_build(node, module)
- return node
-
def file_build(self, path, modname=None):
"""build astng from a source code file (i.e. from an ast)
path is expected to be a python source file
"""
try:
- data = open(path, 'U').read()
- except IOError, ex:
- msg = 'Unable to load file %r (%s)' % (path, ex)
+ stream, encoding, data = open_source_file(path)
+ except IOError, exc:
+ msg = 'Unable to load file %r (%s)' % (path, exc)
raise ASTNGBuildingException(msg)
+ except SyntaxError, exc: # py3k encoding specification error
+ raise ASTNGBuildingException(exc)
+ except LookupError, exc: # unknown encoding
+ raise ASTNGBuildingException(exc)
# get module name if necessary, *before modifying sys.path*
if modname is None:
try:
@@ -112,19 +129,31 @@ class ASTNGBuilder:
modname = splitext(basename(path))[0]
# build astng representation
try:
- sys.path.insert(0, dirname(path))
+ sys.path.insert(0, dirname(path)) # XXX (syt) iirk
node = self.string_build(data, modname, path)
finally:
sys.path.pop(0)
-
+ node.file_encoding = encoding
+ node.file_stream = stream
return node
def string_build(self, data, modname='', path=None):
- """build astng from a source code stream (i.e. from an ast)"""
- return self.ast_build(parse(data + '\n'), modname, path)
-
- def ast_build(self, node, modname='', path=None):
- """build the astng from AST, return the new tree"""
+ """build astng from source code string and return rebuilded astng"""
+ module = self._data_build(data, modname, path)
+ if self._manager is not None:
+ self._manager.astng_cache[module.name] = module
+ # post tree building steps after we stored the module in the cache:
+ for from_node in module._from_nodes:
+ self.add_from_names_to_locals(from_node)
+ # handle delayed assattr nodes
+ for delayed in module._delayed_assattr:
+ self.delayed_assattr(delayed)
+ return module
+
+ def _data_build(self, data, modname, path):
+ """build tree node from data and add some informations"""
+ # this method could be wrapped with a pickle/cache function
+ node = parse(data + '\n')
if path is not None:
node_file = abspath(path)
else:
@@ -134,86 +163,69 @@ class ASTNGBuilder:
package = True
else:
package = path and path.find('__init__.py') > -1 or False
- newnode = self.rebuilder.build(node, modname, node_file, package)
- return newnode
+ self.rebuilder.init()
+ module = self.rebuilder.visit_module(node, modname, package)
+ module.file = module.path = node_file
+ module._from_nodes = self.rebuilder._from_nodes
+ module._delayed_assattr = self.rebuilder._delayed_assattr
+ return module
+
+ def add_from_names_to_locals(self, node):
+ """store imported names to the locals;
+ resort the locals if coming from a delayed node
+ """
- # astng from living objects ###############################################
- #
- # this is actually a really minimal representation, including only Module,
- # Function and Class nodes and some others as guessed
+ _key_func = lambda node: node.fromlineno
+ def sort_locals(my_list):
+ my_list.sort(key=_key_func)
+ for (name, asname) in node.names:
+ if name == '*':
+ try:
+ imported = node.root().import_module(node.modname)
+ except ASTNGBuildingException:
+ continue
+ for name in imported.wildcard_import_names():
+ node.parent.set_local(name, node)
+ sort_locals(node.parent.scope().locals[name])
+ else:
+ node.parent.set_local(asname or name, node)
+ sort_locals(node.parent.scope().locals[asname or name])
- def object_build(self, node, obj):
- """recursive method which create a partial ast from real objects
- (only function, class, and method are handled)
+ def delayed_assattr(self, node):
+ """visit a AssAttr node -> add name to locals, handle members
+ definition
"""
- if obj in self._done:
- return self._done[obj]
- self._done[obj] = node
- for name in dir(obj):
- try:
- member = getattr(obj, name)
- except AttributeError:
- # damned ExtensionClass.Base, I know you're there !
- attach_dummy_node(node, name)
- continue
- if ismethod(member):
- member = member.im_func
- if isfunction(member):
- # verify this is not an imported function
- if member.func_code.co_filename != getattr(self._module, '__file__', None):
- attach_dummy_node(node, name, member)
+ try:
+ frame = node.frame()
+ for infered in node.expr.infer():
+ if infered is YES:
continue
- object_build_function(node, member, name)
- elif isbuiltin(member):
- # verify this is not an imported member
- if self._member_module(member) != self._module.__name__:
- imported_member(node, member, name)
+ try:
+ if infered.__class__ is Instance:
+ infered = infered._proxied
+ iattrs = infered.instance_attrs
+ elif isinstance(infered, Instance):
+ # Const, Tuple, ... we may be wrong, may be not, but
+ # anyway we don't want to pollute builtin's namespace
+ continue
+ elif infered.is_function:
+ iattrs = infered.instance_attrs
+ else:
+ iattrs = infered.locals
+ except AttributeError:
+ # XXX log error
+ #import traceback
+ #traceback.print_exc()
continue
- object_build_methoddescriptor(node, member, name)
- elif isclass(member):
- # verify this is not an imported class
- if self._member_module(member) != self._module.__name__:
- imported_member(node, member, name)
+ values = iattrs.setdefault(node.attrname, [])
+ if node in values:
continue
- if member in self._done:
- class_node = self._done[member]
- if not class_node in node.locals.get(name, ()):
- node.add_local_node(class_node, name)
+ # get assign in __init__ first XXX useful ?
+ if frame.name == '__init__' and values and not \
+ values[0].frame().name == '__init__':
+ values.insert(0, node)
else:
- class_node = object_build_class(node, member, name)
- # recursion
- self.object_build(class_node, member)
- if name == '__class__' and class_node.parent is None:
- class_node.parent = self._done[self._module]
- elif ismethoddescriptor(member):
- assert isinstance(member, object)
- object_build_methoddescriptor(node, member, name)
- elif isdatadescriptor(member):
- assert isinstance(member, object)
- object_build_datadescriptor(node, member, name)
- elif isinstance(member, (int, long, float, str, unicode)) or member is None:
- attach_const_node(node, name, member)
- else:
- # create an empty node so that the name is actually defined
- attach_dummy_node(node, name, member)
-
- def _member_module(self, member):
- modname = getattr(member, '__module__', None)
- return self._dyn_modname_map.get(modname, modname)
-
-
-def imported_member(node, member, name):
- """consider a class/builtin member where __module__ != current module name
-
- check if it's sound valid and then add an import node, else use a dummy node
- """
- # /!\ some classes like ExtensionClass doesn't have a
- # __module__ attribute !
- member_module = getattr(member, '__module__', '__builtin__')
- try:
- getattr(sys.modules[member_module], name)
- except (KeyError, AttributeError):
- attach_dummy_node(node, name, member)
- else:
- attach_import_node(node, member_module, name)
+ values.append(node)
+ except InferenceError:
+ pass
diff --git a/debian/changelog b/debian/changelog
index 07752ac5..e6181fbf 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+logilab-astng (0.21.1-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Tue, 11 Jan 2011 15:38:20 +0100
+
logilab-astng (0.21.0-1) unstable; urgency=low
* new upstream release
diff --git a/_exceptions.py b/exceptions.py
index 9cf2e98b..7dd6135e 100644
--- a/_exceptions.py
+++ b/exceptions.py
@@ -58,7 +58,3 @@ class NoDefault(ASTNGError):
no default value
"""
-class IgnoreChild(Exception):
- """exception that maybe raised by visit methods to avoid children traversal
- """
-
diff --git a/inference.py b/inference.py
index 25c5fed5..edc71839 100644
--- a/inference.py
+++ b/inference.py
@@ -20,59 +20,22 @@
"""this module contains a set of functions to handle inference on astng trees
"""
-from __future__ import generators
-
__doctype__ = "restructuredtext en"
from itertools import chain
import sys
-try:
- GeneratorExit # introduced in py2.5
-except NameError:
- class GeneratorExit(Exception): pass
+from logilab.astng import nodes
-from logilab.astng import nodes, raw_building
from logilab.astng.manager import ASTNGManager
-from logilab.astng import ASTNGError, InferenceError, UnresolvableName, \
- NoDefault, NotFoundError, ASTNGBuildingException
-from logilab.astng.bases import YES, Instance, InferenceContext, \
+from logilab.astng.exceptions import (ASTNGBuildingException, ASTNGError,
+ InferenceError, NoDefault, NotFoundError, UnresolvableName)
+from logilab.astng.bases import YES, Instance, InferenceContext, Generator, \
_infer_stmts, copy_context, path_wrapper, raise_if_nothing_infered
from logilab.astng.protocols import _arguments_infer_argname
MANAGER = ASTNGManager()
-_CONST_PROXY = {
- type(None): raw_building.build_class('NoneType'),
- bool: MANAGER.astng_from_class(bool),
- int: MANAGER.astng_from_class(int),
- long: MANAGER.astng_from_class(long),
- float: MANAGER.astng_from_class(float),
- complex: MANAGER.astng_from_class(complex),
- str: MANAGER.astng_from_class(str),
- unicode: MANAGER.astng_from_class(unicode),
- }
-_CONST_PROXY[type(None)].parent = _CONST_PROXY[bool].parent
-if sys.version_info >= (2, 6):
- _CONST_PROXY[bytes] = MANAGER.astng_from_class(bytes)
-
-# TODO : find a nicer way to handle this situation; we should at least
-# be able to avoid calling MANAGER.astng_from_class(const.value.__class__)
-# each time (if we can not avoid the property). However __proxied introduced an
-# infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870)
-def _set_proxied(const):
- return _CONST_PROXY[const.value.__class__]
-nodes.Const._proxied = property(_set_proxied)
-
-def Const_pytype(self):
- return self._proxied.qname()
-nodes.Const.pytype = Const_pytype
-
-
-nodes.List._proxied = MANAGER.astng_from_class(list)
-nodes.Tuple._proxied = MANAGER.astng_from_class(tuple)
-nodes.Dict._proxied = MANAGER.astng_from_class(dict)
-
class CallContext:
"""when inferring a function call, this class is used to remember values
diff --git a/inspector.py b/inspector.py
index 181a3fa3..a4abd1f2 100644
--- a/inspector.py
+++ b/inspector.py
@@ -43,7 +43,7 @@ from logilab.common.modutils import get_module_part, is_relative, \
is_standard_module
from logilab import astng
-from logilab.astng import InferenceError
+from logilab.astng.exceptions import InferenceError
from logilab.astng.utils import LocalsVisitor
class IdGeneratorMixIn:
diff --git a/manager.py b/manager.py
index 836d0c23..5890078c 100644
--- a/manager.py
+++ b/manager.py
@@ -46,7 +46,7 @@ from logilab.common.modutils import NoSourceFile, is_python_source, \
get_module_files, get_source_file, zipimport
from logilab.common.configuration import OptionsProviderMixIn
-from logilab.astng._exceptions import ASTNGBuildingException
+from logilab.astng.exceptions import ASTNGBuildingException
def astng_wrapper(func, modname):
"""wrapper to give to ASTNGManager.project_from_files"""
@@ -55,12 +55,14 @@ def astng_wrapper(func, modname):
return func(modname)
except ASTNGBuildingException, exc:
print exc
- except KeyboardInterrupt:
- raise
except Exception, exc:
import traceback
traceback.print_exc()
+def _silent_no_wrap(func, modname):
+ """silent wrapper that doesn't do anything; can be used for tests"""
+ return func(modname)
+
def safe_repr(obj):
try:
return repr(obj)
@@ -95,15 +97,9 @@ class ASTNGManager(OptionsProviderMixIn):
OptionsProviderMixIn.__init__(self)
self.load_defaults()
# NOTE: cache entries are added by the [re]builder
- self._cache = {}
+ self.astng_cache = {}
self._mod_file_cache = {}
- def from_directory(self, directory, modname=None):
- """given a module name, return the astng object"""
- # FIXME : seems to be dead or unused code
- modname = modname or basename(directory)
- directory = abspath(directory)
- return Package(directory, modname, self)
def astng_from_file(self, filepath, modname=None, fallback=True, source=False):
"""given a module name, return the astng object"""
@@ -117,21 +113,11 @@ class ASTNGManager(OptionsProviderMixIn):
modname = '.'.join(modpath_from_file(filepath))
except ImportError:
modname = filepath
- if modname in self._cache:
- return self._cache[modname]
+ if modname in self.astng_cache:
+ return self.astng_cache[modname]
if source:
- try:
- from logilab.astng.builder import ASTNGBuilder
- return ASTNGBuilder(self).file_build(filepath, modname)
- except (SyntaxError, KeyboardInterrupt, SystemExit):
- raise
- except Exception, ex:
- if __debug__:
- print 'error while building astng for', filepath
- import traceback
- traceback.print_exc()
- msg = 'Unable to load module %s (%s)' % (modname, ex)
- raise ASTNGBuildingException, msg, sys.exc_info()[-1]
+ from logilab.astng.builder import ASTNGBuilder
+ return ASTNGBuilder(self).file_build(filepath, modname)
elif fallback and modname:
return self.astng_from_module_name(modname)
raise ASTNGBuildingException('unable to get astng for file %s' %
@@ -139,8 +125,8 @@ class ASTNGManager(OptionsProviderMixIn):
def astng_from_module_name(self, modname, context_file=None):
"""given a module name, return the astng object"""
- if modname in self._cache:
- return self._cache[modname]
+ if modname in self.astng_cache:
+ return self.astng_cache[modname]
old_cwd = os.getcwd()
if context_file:
os.chdir(dirname(context_file))
@@ -153,9 +139,7 @@ class ASTNGManager(OptionsProviderMixIn):
if filepath is None or not is_python_source(filepath):
try:
module = load_module_from_name(modname)
- # catch SystemError as well, we may get that on badly
- # initialized C-module
- except (SystemError, ImportError), ex:
+ except Exception, ex:
msg = 'Unable to load module %s (%s)' % (modname, ex)
raise ASTNGBuildingException(msg)
return self.astng_from_module(module, modname)
@@ -203,8 +187,8 @@ class ASTNGManager(OptionsProviderMixIn):
def astng_from_module(self, module, modname=None):
"""given an imported module, return the astng object"""
modname = modname or module.__name__
- if modname in self._cache:
- return self._cache[modname]
+ if modname in self.astng_cache:
+ return self.astng_cache[modname]
try:
# some builtin modules don't have __file__ attribute
filepath = module.__file__
@@ -227,22 +211,21 @@ class ASTNGManager(OptionsProviderMixIn):
return modastng.getattr(klass.__name__)[0] # XXX
- def infer_astng_from_something(self, obj, modname=None, context=None):
+ def infer_astng_from_something(self, obj, context=None):
"""infer astng for the given class"""
if hasattr(obj, '__class__') and not isinstance(obj, type):
klass = obj.__class__
else:
klass = obj
- if modname is None:
- try:
- modname = klass.__module__
- except AttributeError:
- raise ASTNGBuildingException(
- 'Unable to get module for %s' % safe_repr(klass))
- except Exception, ex:
- raise ASTNGBuildingException(
- 'Unexpected error while retrieving module for %s: %s'
- % (safe_repr(klass), ex))
+ try:
+ modname = klass.__module__
+ except AttributeError:
+ raise ASTNGBuildingException(
+ 'Unable to get module for %s' % safe_repr(klass))
+ except Exception, ex:
+ raise ASTNGBuildingException(
+ 'Unexpected error while retrieving module for %s: %s'
+ % (safe_repr(klass), ex))
try:
name = klass.__name__
except AttributeError:
@@ -278,6 +261,7 @@ class ASTNGManager(OptionsProviderMixIn):
astng = func_wrapper(self.astng_from_file, fpath)
if astng is None:
continue
+ # XXX why is first file defining the project.path ?
project.path = project.path or astng.file
project.add_module(astng)
base_name = astng.name
@@ -293,99 +277,6 @@ class ASTNGManager(OptionsProviderMixIn):
return project
-
-# FIXME : seems to be dead or unused code
-class Package:
- """a package using a dictionary like interface
-
- load submodules lazily, as they are needed
- """
-
- def __init__(self, path, name, manager):
- self.name = name
- self.path = abspath(path)
- self.manager = manager
- self.parent = None
- self.lineno = 0
- self.__keys = None
- self.__subobjects = None
-
- def fullname(self):
- """return the full name of the package (i.e. prefix by the full name
- of the parent package if any
- """
- if self.parent is None:
- return self.name
- return '%s.%s' % (self.parent.fullname(), self.name)
-
- def get_subobject(self, name):
- """method used to get sub-objects lazily : sub package or module are
- only build once they are requested
- """
- if self.__subobjects is None:
- try:
- self.__subobjects = dict.fromkeys(self.keys())
- except AttributeError:
- # python <= 2.3
- self.__subobjects = dict([(k, None) for k in self.keys()])
- obj = self.__subobjects[name]
- if obj is None:
- objpath = join(self.path, name)
- if isdir(objpath):
- obj = Package(objpath, name, self.manager)
- obj.parent = self
- else:
- modname = '%s.%s' % (self.fullname(), name)
- obj = self.manager.astng_from_file(objpath + '.py', modname)
- self.__subobjects[name] = obj
- return obj
-
- def get_module(self, modname):
- """return the Module or Package object with the given name if any
- """
- path = modname.split('.')
- if path[0] != self.name:
- raise KeyError(modname)
- obj = self
- for part in path[1:]:
- obj = obj.get_subobject(part)
- return obj
-
- def keys(self):
- if self.__keys is None:
- self.__keys = []
- for fname in os.listdir(self.path):
- if fname.endswith('.py'):
- self.__keys.append(fname[:-3])
- continue
- fpath = join(self.path, fname)
- if isdir(fpath) and exists(join(fpath, '__init__.py')):
- self.__keys.append(fname)
- self.__keys.sort()
- return self.__keys[:]
-
- def values(self):
- return [self.get_subobject(name) for name in self.keys()]
-
- def items(self):
- return zip(self.keys(), self.values())
-
- def get(self, name, default=None):
- try:
- return self.get_subobject(name)
- except KeyError:
- return default
-
- def __getitem__(self, name):
- return self.get_subobject(name)
-
- def __contains__(self, name):
- return bool(self.get(name))
-
- def __iter__(self):
- return iter(self.keys())
-
-
class Project:
"""a project handle a set of modules / packages"""
def __init__(self, name=''):
@@ -413,3 +304,4 @@ class Project:
return '<Project %r at %s (%s modules)>' % (self.name, id(self),
len(self.modules))
+
diff --git a/mixins.py b/mixins.py
index 7ca4a921..e09afc7a 100644
--- a/mixins.py
+++ b/mixins.py
@@ -30,44 +30,13 @@
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
"""This module contains some mixins for the different nodes.
-
"""
-from logilab.astng import ASTNGBuildingException, InferenceError, NotFoundError
-from logilab.astng.bases import BaseClass
-
-# /!\ We cannot build a StmtNode(NodeNG) class since modifying "__bases__"
-# in "nodes.py" has to work *both* for old-style and new-style classes,
-# but we need the StmtMixIn for scoped nodes
-
-class StmtMixIn(BaseClass):
- """StmtMixIn used only for a adding a few attributes"""
- is_statement = True
-
- def replace(self, child, newchild):
- sequence = self.child_sequence(child)
- newchild.parent = self
- child.parent = None
- sequence[sequence.index(child)] = newchild
-
- def next_sibling(self):
- """return the next sibling statement"""
- stmts = self.parent.child_sequence(self)
- index = stmts.index(self)
- try:
- return stmts[index +1]
- except IndexError:
- pass
-
- def previous_sibling(self):
- """return the previous sibling statement"""
- stmts = self.parent.child_sequence(self)
- index = stmts.index(self)
- if index >= 1:
- return stmts[index -1]
+from logilab.astng.exceptions import (ASTNGBuildingException, InferenceError,
+ NotFoundError)
-class BlockRangeMixIn(BaseClass):
+class BlockRangeMixIn(object):
"""override block range """
def set_line_info(self, lastchild):
self.fromlineno = self.lineno
@@ -124,7 +93,7 @@ class ParentAssignTypeMixin(AssignTypeMixin):
-class FromImportMixIn(BaseClass, FilterStmtsMixin):
+class FromImportMixIn(FilterStmtsMixin):
"""MixIn for From and Import Nodes"""
def _infer_name(self, frame, name):
@@ -146,7 +115,7 @@ class FromImportMixIn(BaseClass, FilterStmtsMixin):
return mymodule
try:
return mymodule.import_module(modname, level=level)
- except (ASTNGBuildingException, SyntaxError):
+ except ASTNGBuildingException:
raise InferenceError(modname)
def real_name(self, asname):
diff --git a/node_classes.py b/node_classes.py
index c41cba00..9bc6c568 100644
--- a/node_classes.py
+++ b/node_classes.py
@@ -22,10 +22,10 @@
import sys
-from logilab.astng import NoDefault
-from logilab.astng.bases import NodeNG, BaseClass, Instance, copy_context, \
- _infer_stmts, YES
-from logilab.astng.mixins import StmtMixIn, BlockRangeMixIn, AssignTypeMixin, \
+from logilab.astng.exceptions import NoDefault
+from logilab.astng.bases import (NodeNG, Statement, Instance, InferenceContext,
+ _infer_stmts, YES)
+from logilab.astng.mixins import BlockRangeMixIn, AssignTypeMixin, \
ParentAssignTypeMixin, FromImportMixIn
@@ -98,7 +98,7 @@ def are_exclusive(stmt1, stmt2, exceptions=None):
return False
-class LookupMixIn(BaseClass):
+class LookupMixIn(object):
"""Mixin looking up a name in the right scope
"""
@@ -115,15 +115,14 @@ class LookupMixIn(BaseClass):
"""
return self.scope().scope_lookup(self, name)
- def ilookup(self, name, context=None):
+ def ilookup(self, name):
"""infered lookup
return an iterator on infered values of the statements returned by
the lookup method
"""
frame, stmts = self.lookup(name)
- context = copy_context(context)
- context.lookupname = name
+ context = InferenceContext()
return _infer_stmts(stmts, context, frame)
def _filter_stmts(self, stmts, frame, offset):
@@ -333,19 +332,19 @@ class AssAttr(NodeNG, ParentAssignTypeMixin):
_astng_fields = ('expr',)
expr = None
-class Assert(StmtMixIn, NodeNG):
+class Assert(Statement):
"""class representing an Assert node"""
_astng_fields = ('test', 'fail',)
test = None
fail = None
-class Assign(StmtMixIn, NodeNG, AssignTypeMixin):
+class Assign(Statement, AssignTypeMixin):
"""class representing an Assign node"""
_astng_fields = ('targets', 'value',)
targets = None
value = None
-class AugAssign(StmtMixIn, NodeNG, AssignTypeMixin):
+class AugAssign(Statement, AssignTypeMixin):
"""class representing an AugAssign node"""
_astng_fields = ('target', 'value',)
target = None
@@ -367,7 +366,7 @@ class BoolOp(NodeNG):
_astng_fields = ('values',)
values = None
-class Break(StmtMixIn, NodeNG):
+class Break(Statement):
"""class representing a Break node"""
@@ -428,7 +427,7 @@ class Comprehension(NodeNG):
class Const(NodeNG, Instance):
- """represent a Str or Num node"""
+ """represent a constant node like num, str, bool, None, bytes"""
def __init__(self, value=None):
self.value = value
@@ -446,7 +445,11 @@ class Const(NodeNG, Instance):
return self.value
raise TypeError()
-class Continue(StmtMixIn, NodeNG):
+ def pytype(self):
+ return self._proxied.qname()
+
+
+class Continue(Statement):
"""class representing a Continue node"""
@@ -468,7 +471,7 @@ class DelAttr(NodeNG, ParentAssignTypeMixin):
expr = None
-class Delete(StmtMixIn, NodeNG, AssignTypeMixin):
+class Delete(Statement, AssignTypeMixin):
"""class representing a Delete node"""
_astng_fields = ('targets',)
targets = None
@@ -477,7 +480,13 @@ class Delete(StmtMixIn, NodeNG, AssignTypeMixin):
class Dict(NodeNG, Instance):
"""class representing a Dict node"""
_astng_fields = ('items',)
- items = None
+
+ def __init__(self, items=None):
+ if items is None:
+ self.items = []
+ else:
+ self.items = [(const_factory(k), const_factory(v))
+ for k,v in items.iteritems()]
def pytype(self):
return '__builtin__.dict'
@@ -508,7 +517,7 @@ class Dict(NodeNG, Instance):
raise IndexError(key)
-class Discard(StmtMixIn, NodeNG):
+class Discard(Statement):
"""class representing a Discard node"""
_astng_fields = ('value',)
value = None
@@ -522,7 +531,7 @@ class EmptyNode(NodeNG):
"""class representing an EmptyNode node"""
-class ExceptHandler(StmtMixIn, NodeNG, AssignTypeMixin):
+class ExceptHandler(Statement, AssignTypeMixin):
"""class representing an ExceptHandler node"""
_astng_fields = ('type', 'name', 'body',)
type = None
@@ -550,7 +559,7 @@ class ExceptHandler(StmtMixIn, NodeNG, AssignTypeMixin):
return True
-class Exec(StmtMixIn, NodeNG):
+class Exec(Statement):
"""class representing an Exec node"""
_astng_fields = ('expr', 'globals', 'locals',)
expr = None
@@ -563,8 +572,7 @@ class ExtSlice(NodeNG):
_astng_fields = ('dims',)
dims = None
-
-class For(BlockRangeMixIn, StmtMixIn, AssignTypeMixin, NodeNG):
+class For(BlockRangeMixIn, AssignTypeMixin, Statement):
"""class representing a For node"""
_astng_fields = ('target', 'iter', 'body', 'orelse',)
target = None
@@ -577,7 +585,7 @@ class For(BlockRangeMixIn, StmtMixIn, AssignTypeMixin, NodeNG):
return self.iter.tolineno
-class From(FromImportMixIn, StmtMixIn, NodeNG):
+class From(FromImportMixIn, Statement):
"""class representing a From node"""
def __init__(self, fromname, names, level=0):
@@ -591,7 +599,7 @@ class Getattr(NodeNG):
expr = None
-class Global(StmtMixIn, NodeNG):
+class Global(Statement):
"""class representing a Global node"""
def __init__(self, names):
@@ -601,7 +609,7 @@ class Global(StmtMixIn, NodeNG):
return name
-class If(BlockRangeMixIn, StmtMixIn, NodeNG):
+class If(BlockRangeMixIn, Statement):
"""class representing an If node"""
_astng_fields = ('test', 'body', 'orelse')
test = None
@@ -629,7 +637,7 @@ class IfExp(NodeNG):
orelse = None
-class Import(FromImportMixIn, StmtMixIn, NodeNG):
+class Import(FromImportMixIn, Statement):
"""class representing an Import node"""
@@ -647,8 +655,13 @@ class Keyword(NodeNG):
class List(NodeNG, Instance, ParentAssignTypeMixin):
"""class representing a List node"""
- _astng_fields = ('elts',)
- elts = None
+ _astng_fields = ('elts',)
+
+ def __init__(self, elts=None):
+ if elts is None:
+ self.elts = []
+ else:
+ self.elts = [const_factory(e) for e in elts]
def pytype(self):
return '__builtin__.list'
@@ -660,7 +673,7 @@ class List(NodeNG, Instance, ParentAssignTypeMixin):
return self.elts
-class Nonlocal(StmtMixIn, NodeNG):
+class Nonlocal(Statement):
"""class representing a Nonlocal node"""
def __init__(self, names):
@@ -670,19 +683,18 @@ class Nonlocal(StmtMixIn, NodeNG):
return name
-class Pass(StmtMixIn, NodeNG):
+class Pass(Statement):
"""class representing a Pass node"""
-class Print(StmtMixIn, NodeNG):
+class Print(Statement):
"""class representing a Print node"""
_astng_fields = ('dest', 'values',)
dest = None
values = None
-
-class Raise(StmtMixIn, NodeNG):
+class Raise(Statement):
"""class representing a Raise node"""
exc = None
if sys.version_info < (3, 0):
@@ -691,8 +703,8 @@ class Raise(StmtMixIn, NodeNG):
tback = None
else:
_astng_fields = ('exc', 'cause')
- inst = None
- tback = None
+ exc = None
+ cause = None
def raises_not_implemented(self):
if not self.exc:
@@ -702,7 +714,7 @@ class Raise(StmtMixIn, NodeNG):
return True
-class Return(StmtMixIn, NodeNG):
+class Return(Statement):
"""class representing a Return node"""
_astng_fields = ('value',)
value = None
@@ -711,7 +723,12 @@ class Return(StmtMixIn, NodeNG):
class Set(NodeNG, Instance, ParentAssignTypeMixin):
"""class representing a Set node"""
_astng_fields = ('elts',)
- elts = None
+
+ def __init__(self, elts=None):
+ if elts is None:
+ self.elts = []
+ else:
+ self.elts = [const_factory(e) for e in elts]
def pytype(self):
return '__builtin__.set' # XXX __builtin__ vs builtins
@@ -740,7 +757,7 @@ class Subscript(NodeNG):
slice = None
-class TryExcept(BlockRangeMixIn, StmtMixIn, NodeNG):
+class TryExcept(BlockRangeMixIn, Statement):
"""class representing a TryExcept node"""
_astng_fields = ('body', 'handlers', 'orelse',)
body = None
@@ -766,7 +783,7 @@ class TryExcept(BlockRangeMixIn, StmtMixIn, NodeNG):
return self._elsed_block_range(lineno, self.orelse, last)
-class TryFinally(BlockRangeMixIn, StmtMixIn, NodeNG):
+class TryFinally(BlockRangeMixIn, Statement):
"""class representing a TryFinally node"""
_astng_fields = ('body', 'finalbody',)
body = None
@@ -788,7 +805,12 @@ class TryFinally(BlockRangeMixIn, StmtMixIn, NodeNG):
class Tuple(NodeNG, Instance, ParentAssignTypeMixin):
"""class representing a Tuple node"""
_astng_fields = ('elts',)
- elts = None
+
+ def __init__(self, elts=None):
+ if elts is None:
+ self.elts = []
+ else:
+ self.elts = [const_factory(e) for e in elts]
def pytype(self):
return '__builtin__.tuple'
@@ -806,7 +828,7 @@ class UnaryOp(NodeNG):
operand = None
-class While(BlockRangeMixIn, StmtMixIn, NodeNG):
+class While(BlockRangeMixIn, Statement):
"""class representing a While node"""
_astng_fields = ('test', 'body', 'orelse',)
test = None
@@ -820,7 +842,8 @@ class While(BlockRangeMixIn, StmtMixIn, NodeNG):
"""handle block line numbers range for for and while statements"""
return self. _elsed_block_range(lineno, self.orelse)
-class With(BlockRangeMixIn, StmtMixIn, AssignTypeMixin, NodeNG):
+
+class With(BlockRangeMixIn, AssignTypeMixin, Statement):
"""class representing a With node"""
_astng_fields = ('expr', 'vars', 'body')
expr = None
@@ -845,24 +868,30 @@ CONST_CLS = {
list: List,
tuple: Tuple,
dict: Dict,
+ set: Set,
+ type(None): Const,
}
+def _update_const_classes():
+ """update constant classes, so the keys of CONST_CLS can be reused"""
+ klasses = (bool, int, float, complex, str)
+ if sys.version_info < (3, 0):
+ klasses += (unicode, long)
+ if sys.version_info >= (2, 6):
+ klasses += (bytes,)
+ for kls in klasses:
+ CONST_CLS[kls] = Const
+_update_const_classes()
+
def const_factory(value):
"""return an astng node for a python value"""
try:
- # if value is of class list, tuple, dict use specific class, not Const
- cls = CONST_CLS[value.__class__]
- node = cls()
- if isinstance(node, Dict):
- node.items = ()
- else:
- node.elts = ()
- except KeyError:
- # why was value in (None, False, True) not OK?
- assert isinstance(value, (int, long, complex, float, basestring)) or value in (None, False, True)
- node = Const()
- node.value = value
- return node
-
-
-
+ return CONST_CLS[value.__class__](value)
+ except (KeyError, AttributeError):
+ # some constants (like from gtk._gtk) don't have their class in
+ # CONST_CLS, though we can "assert isinstance(value, tuple(CONST_CLS))"
+ if isinstance(value, tuple(CONST_CLS)):
+ return Const(value)
+ node = EmptyNode()
+ node.object = value
+ return None
diff --git a/protocols.py b/protocols.py
index 301ef0fc..34bbe759 100644
--- a/protocols.py
+++ b/protocols.py
@@ -19,14 +19,11 @@
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
"""this module contains a set of functions to handle python protocols for nodes
where it makes sense.
-
"""
-from __future__ import generators
-
__doctype__ = "restructuredtext en"
-from logilab.astng import InferenceError, NoDefault
+from logilab.astng.exceptions import InferenceError, NoDefault
from logilab.astng.node_classes import unpack_infer
from logilab.astng.bases import copy_context, \
raise_if_nothing_infered, yes_if_nothing_infered, Instance, Generator, YES
@@ -198,7 +195,7 @@ def for_assigned_stmts(self, node, context=None, asspath=None):
yield item
else:
for infered in _resolve_looppart(self.iter.infer(context),
- asspath, context):
+ asspath, context):
yield infered
nodes.For.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts)
diff --git a/raw_building.py b/raw_building.py
index 534f58c5..f6da03ed 100644
--- a/raw_building.py
+++ b/raw_building.py
@@ -1,15 +1,3 @@
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser 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 Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser 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.
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
# copyright 2003-2010 Sylvain Thenault, all rights reserved.
@@ -36,10 +24,18 @@
__docformat__ = "restructuredtext en"
import sys
-from inspect import getargspec
+from os.path import abspath
+from inspect import (getargspec, isdatadescriptor, isfunction, ismethod,
+ ismethoddescriptor, isclass, isbuiltin)
-from logilab.astng import nodes
+from logilab.astng.node_classes import CONST_CLS
+from logilab.astng.nodes import (Module, Class, Const, const_factory, From,
+ Function, EmptyNode, Name, Arguments, Dict, List, Set, Tuple)
+from logilab.astng.bases import Generator
+from logilab.astng.manager import ASTNGManager
+MANAGER = ASTNGManager()
+_CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types
def _attach_local_node(parent, node, name):
node.name = name # needed by add_local_node
@@ -51,39 +47,39 @@ def attach_dummy_node(node, name, object=_marker):
"""create a dummy node and register it in the locals of the given
node with the specified name
"""
- enode = nodes.EmptyNode()
+ enode = EmptyNode()
enode.object = object
_attach_local_node(node, enode, name)
-nodes.EmptyNode.has_underlying_object = lambda self: self.object is not _marker
+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
"""
if not name in node.special_attributes:
- _attach_local_node(node, nodes.const_factory(value), name)
+ _attach_local_node(node, const_factory(value), name)
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
"""
- from_node = nodes.From(modname, [(membername, None)])
+ from_node = From(modname, [(membername, None)])
_attach_local_node(node, from_node, membername)
def build_module(name, doc=None):
"""create and initialize a astng Module node"""
- node = nodes.Module(name, doc, pure_python=False)
+ node = Module(name, doc, pure_python=False)
node.package = False
node.parent = None
return node
def build_class(name, basenames=(), doc=None):
"""create and initialize a astng Class node"""
- node = nodes.Class(name, doc)
+ node = Class(name, doc)
for base in basenames:
- basenode = nodes.Name()
+ basenode = Name()
basenode.name = base
node.bases.append(basenode)
basenode.parent = node
@@ -93,16 +89,16 @@ 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(name, doc)
- func.args = argsnode = nodes.Arguments()
+ func = Function(name, doc)
+ func.args = argsnode = Arguments()
argsnode.args = []
for arg in args:
- argsnode.args.append(nodes.Name())
+ argsnode.args.append(Name())
argsnode.args[-1].name = arg
argsnode.args[-1].parent = argsnode
argsnode.defaults = []
for default in defaults:
- argsnode.defaults.append(nodes.const_factory(default))
+ argsnode.defaults.append(const_factory(default))
argsnode.defaults[-1].parent = argsnode
argsnode.kwarg = None
argsnode.vararg = None
@@ -112,19 +108,9 @@ def build_function(name, args=None, defaults=None, flag=0, doc=None):
return func
-# def build_name_assign(name, value):
-# """create and initialize an astng Assign for a name assignment"""
-# return nodes.Assign([nodes.AssName(name, 'OP_ASSIGN')], nodes.Const(value))
-
-# def build_attr_assign(name, value, attr='self'):
-# """create and initialize an astng Assign for an attribute assignment"""
-# return nodes.Assign([nodes.AssAttr(nodes.Name(attr), name, 'OP_ASSIGN')],
-# nodes.Const(value))
-
-
def build_from_import(fromname, names):
"""create and initialize an astng From import statement"""
- return nodes.From(fromname, [(name, None) for name in names])
+ return From(fromname, [(name, None) for name in names])
def register_arguments(func, args=None):
"""add given arguments to local
@@ -139,7 +125,7 @@ def register_arguments(func, args=None):
if func.args.kwarg:
func.set_local(func.args.kwarg, func.args)
for arg in args:
- if isinstance(arg, nodes.Name):
+ if isinstance(arg, Name):
func.set_local(arg.name, arg)
else:
register_arguments(func, arg.elts)
@@ -196,7 +182,7 @@ def _base_class_object_build(node, member, basenames, name=None, localname=None)
pass
else:
for name, obj in instdict.items():
- valnode = nodes.EmptyNode()
+ valnode = EmptyNode()
valnode.object = obj
valnode.parent = klass
valnode.lineno = 1
@@ -204,8 +190,153 @@ def _base_class_object_build(node, member, basenames, name=None, localname=None)
return klass
-__all__ = ('register_arguments', 'build_module',
- 'object_build_class', 'object_build_function',
- 'object_build_datadescriptor', 'object_build_methoddescriptor',
- 'attach_dummy_node',
- 'attach_const_node', 'attach_import_node')
+
+
+class InspectBuilder(object):
+ """class for building nodes from living object
+
+ this is actually a really minimal representation, including only Module,
+ Function and Class nodes and some others as guessed.
+ """
+
+ # astng from living objects ###############################################
+
+ def __init__(self):
+ self._done = {}
+ self._module = None
+
+ def inspect_build(self, module, modname=None, path=None):
+ """build astng from a living module (i.e. using inspect)
+ this is used when there is no python source code available (either
+ because it's a built-in module or because the .py is not available)
+ """
+ self._module = module
+ if modname is None:
+ modname = module.__name__
+ node = build_module(modname, module.__doc__)
+ node.file = node.path = path and abspath(path) or path
+ MANAGER.astng_cache[modname] = node
+ node.package = hasattr(module, '__path__')
+ self._done = {}
+ self.object_build(node, module)
+ return node
+
+ def object_build(self, node, obj):
+ """recursive method which create a partial ast from real objects
+ (only function, class, and method are handled)
+ """
+ if obj in self._done:
+ return self._done[obj]
+ self._done[obj] = node
+ for name in dir(obj):
+ try:
+ member = getattr(obj, name)
+ except AttributeError:
+ # damned ExtensionClass.Base, I know you're there !
+ attach_dummy_node(node, name)
+ continue
+ if ismethod(member):
+ member = member.im_func
+ if isfunction(member):
+ # verify this is not an imported function
+ if member.func_code.co_filename != getattr(self._module, '__file__', None):
+ attach_dummy_node(node, name, member)
+ continue
+ object_build_function(node, member, name)
+ elif isbuiltin(member):
+ if self.imported_member(node, member, name):
+ continue
+ object_build_methoddescriptor(node, member, name)
+ elif isclass(member):
+ if self.imported_member(node, member, name):
+ continue
+ if member in self._done:
+ class_node = self._done[member]
+ if not class_node in node.locals.get(name, ()):
+ node.add_local_node(class_node, name)
+ else:
+ class_node = object_build_class(node, member, name)
+ # recursion
+ self.object_build(class_node, member)
+ if name == '__class__' and class_node.parent is None:
+ class_node.parent = self._done[self._module]
+ elif ismethoddescriptor(member):
+ assert isinstance(member, object)
+ object_build_methoddescriptor(node, member, name)
+ elif isdatadescriptor(member):
+ assert isinstance(member, object)
+ object_build_datadescriptor(node, member, name)
+ elif isinstance(member, _CONSTANTS):
+ attach_const_node(node, name, member)
+ else:
+ # create an empty node so that the name is actually defined
+ attach_dummy_node(node, name, member)
+
+ def imported_member(self, node, member, name):
+ """verify this is not an imported class or handle it"""
+ # /!\ some classes like ExtensionClass doesn't have a __module__
+ # attribute ! Also, this may trigger an exception on badly built module
+ # (see http://www.logilab.org/ticket/57299 for instance)
+ try:
+ modname = getattr(member, '__module__', None)
+ except:
+ # XXX use logging
+ print 'unexpected error while building astng from living object'
+ import traceback
+ traceback.print_exc()
+ modname = None
+ if modname is None:
+ if name in ('__new__', '__subclasshook__'):
+ # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14)
+ # >>> print object.__new__.__module__
+ # None
+ modname = '__builtin__'
+ else:
+ attach_dummy_node(node, name, member)
+ return True
+ if {'gtk': 'gtk._gtk'}.get(modname, modname) != self._module.__name__:
+ # check if it sounds valid and then add an import node, else use a
+ # dummy node
+ try:
+ getattr(sys.modules[modname], name)
+ except (KeyError, AttributeError):
+ attach_dummy_node(node, name, member)
+ else:
+ attach_import_node(node, modname, name)
+ return True
+ return False
+
+
+### astng boot strapping ################################################### ###
+
+_CONST_PROXY = {}
+def astng_boot_strapping():
+ """astng boot strapping the builtins module"""
+ # this boot strapping is necessary since we need the Const nodes to
+ # inspect_build builtins, and then we can proxy Const
+ builder = InspectBuilder()
+ from logilab.common.compat import builtins
+ astng_builtin = builder.inspect_build(builtins)
+ for cls, node_cls in CONST_CLS.items():
+ if cls is type(None):
+ proxy = build_class('NoneType')
+ proxy.parent = astng_builtin
+ else:
+ proxy = astng_builtin.getattr(cls.__name__)[0] # XXX
+ if cls in (dict, list, set, tuple):
+ node_cls._proxied = proxy
+ else:
+ _CONST_PROXY[cls] = proxy
+
+astng_boot_strapping()
+
+# TODO : find a nicer way to handle this situation;
+# However __proxied introduced an
+# infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870)
+def _set_proxied(const):
+ return _CONST_PROXY[const.value.__class__]
+Const._proxied = property(_set_proxied)
+
+# FIXME : is it alright that Generator._proxied is not a astng node?
+Generator._proxied = MANAGER.infer_astng_from_something(type(a for a in ()))
+
diff --git a/rebuilder.py b/rebuilder.py
index de7ca375..d86013e3 100644
--- a/rebuilder.py
+++ b/rebuilder.py
@@ -29,14 +29,62 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-"""this module contains utilities for rebuilding a compiler.ast or _ast tree in
+"""this module contains utilities for rebuilding a _ast tree in
order to get a single ASTNG representation
-
"""
-from logilab.astng import ASTNGBuildingException, InferenceError
-from logilab.astng import nodes
-from logilab.astng.bases import YES, Instance
+import sys
+from _ast import (Expr as Discard, Str,
+ # binary operators
+ Add, Div, FloorDiv, Mod, Mult, Pow, Sub, BitAnd, BitOr, BitXor,
+ LShift, RShift,
+ # logical operators
+ And, Or,
+ # unary operators
+ UAdd, USub, Not, Invert,
+ # comparison operators
+ Eq, Gt, GtE, In, Is, IsNot, Lt, LtE, NotEq, NotIn,
+ )
+
+from logilab.astng.exceptions import ASTNGBuildingException
+from logilab.astng import nodes as new
+
+
+_BIN_OP_CLASSES = {Add: '+',
+ BitAnd: '&',
+ BitOr: '|',
+ BitXor: '^',
+ Div: '/',
+ FloorDiv: '//',
+ Mod: '%',
+ Mult: '*',
+ Pow: '**',
+ Sub: '-',
+ LShift: '<<',
+ RShift: '>>'}
+
+_BOOL_OP_CLASSES = {And: 'and',
+ Or: 'or'}
+
+_UNARY_OP_CLASSES = {UAdd: '+',
+ USub: '-',
+ Not: 'not',
+ Invert: '~'}
+
+_CMP_OP_CLASSES = {Eq: '==',
+ Gt: '>',
+ GtE: '>=',
+ In: 'in',
+ Is: 'is',
+ IsNot: 'is not',
+ Lt: '<',
+ LtE: '<=',
+ NotEq: '!=',
+ NotIn: 'not in'}
+
+CONST_NAME_TRANSFORMS = {'None': None,
+ 'True': True,
+ 'False': False}
REDIRECT = {'arguments': 'Arguments',
'Attribute': 'Getattr',
@@ -52,109 +100,212 @@ REDIRECT = {'arguments': 'Arguments',
'ImportFrom': 'From',
'keyword': 'Keyword',
'Repr': 'Backquote',
-
- 'Add': 'BinOp',
- 'Bitand': 'BinOp',
- 'Bitor': 'BinOp',
- 'Bitxor': 'BinOp',
- 'Div': 'BinOp',
- 'FloorDiv': 'BinOp',
- 'LeftShift': 'BinOp',
- 'Mod': 'BinOp',
- 'Mul': 'BinOp',
- 'Power': 'BinOp',
- 'RightShift': 'BinOp',
- 'Sub': 'BinOp',
-
- 'And': 'BoolOp',
- 'Or': 'BoolOp',
-
- 'UnaryAdd': 'UnaryOp',
- 'UnarySub': 'UnaryOp',
- 'Not': 'UnaryOp',
- 'Invert': 'UnaryOp'
}
-_key_func = lambda node: node.fromlineno
-def sort_locals(my_list):
- my_list.sort(key=_key_func)
+def _init_set_doc(node, newnode):
+ newnode.doc = None
+ try:
+ if isinstance(node.body[0], Discard) and isinstance(node.body[0].value, Str):
+ newnode.tolineno = node.body[0].lineno
+ newnode.doc = node.body[0].value.s
+ node.body = node.body[1:]
-class RebuildVisitor(object):
- """Visitor to transform an AST to an ASTNG
- """
- def __init__(self, manager):
- self._manager = manager
- self.asscontext = None
- self._metaclass = None
- self._global_names = None
- self._delayed_assattr = []
+ except IndexError:
+ pass # ast built from scratch
- def visit(self, node, parent):
- if node is None: # some attributes of some nodes are just None
- return None
- cls_name = node.__class__.__name__
- visit_name = 'visit_' + REDIRECT.get(cls_name, cls_name).lower()
- visit_method = getattr(self, visit_name)
- return visit_method(node, parent)
-
- def build(self, node, modname, module_file, package):
- """rebuild the tree starting with an Module node;
- return an astng.Module node
- """
+def _lineno_parent(oldnode, newnode, parent):
+ newnode.parent = parent
+ if hasattr(oldnode, 'lineno'):
+ newnode.lineno = oldnode.lineno
+
+def _set_infos(oldnode, newnode, parent):
+ newnode.parent = parent
+ if hasattr(oldnode, 'lineno'):
+ newnode.lineno = oldnode.lineno
+ newnode.set_line_info(newnode.last_child()) # set_line_info accepts None
+
+
+
+
+class TreeRebuilder(object):
+ """Rebuilds the _ast tree to become an ASTNG tree"""
+
+ _visit_meths = {}
+ def __init__(self):
+ self.init()
+
+ def init(self):
+ self.asscontext = None
self._metaclass = ['']
self._global_names = []
self._from_nodes = []
- module = self.visit_module(node, modname, package)
- module.file = module.path = module_file
- # init module cache here else we may get some infinite recursion
- # errors while infering delayed assignments
- if self._manager is not None:
- self._manager._cache[module.name] = module
- # handle delayed assattr nodes
- for from_node in self._from_nodes:
- self._add_from_names_to_locals(from_node, delayed=True)
- delay_assattr = self.delayed_assattr
- for delayed in self._delayed_assattr:
- delay_assattr(delayed)
- return module
-
- def _save_argument_name(self, node):
- """save argument names in locals"""
+ self._delayed_assattr = []
+
+ def visit(self, node, parent):
+ cls = node.__class__
+ if cls in self._visit_meths:
+ return self._visit_meths[cls](node, parent)
+ else:
+ cls_name = cls.__name__
+ visit_name = 'visit_' + REDIRECT.get(cls_name, cls_name).lower()
+ visit_method = getattr(self, visit_name)
+ self._visit_meths[cls] = visit_method
+ return visit_method(node, parent)
+
+ def _save_assignment(self, node, name=None):
+ """save assignement situation since node.parent is not available yet"""
+ if self._global_names and node.name in self._global_names[-1]:
+ node.root().set_local(node.name, node)
+ else:
+ node.parent.set_local(node.name, node)
+
+
+ def visit_arguments(self, node, parent):
+ """visit a Arguments node by returning a fresh instance of it"""
+ newnode = new.Arguments()
+ _lineno_parent(node, newnode, parent)
+ self.asscontext = "Ass"
+ newnode.args = [self.visit(child, newnode) for child in node.args]
+ self.asscontext = None
+ newnode.defaults = [self.visit(child, newnode) for child in node.defaults]
+ newnode.vararg = node.vararg
+ newnode.kwarg = node.kwarg
+ # save argument names in locals:
if node.vararg:
- node.parent.set_local(node.vararg, node)
+ newnode.parent.set_local(newnode.vararg, newnode)
if node.kwarg:
- node.parent.set_local(node.kwarg, node)
+ newnode.parent.set_local(newnode.kwarg, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+ def visit_assattr(self, node, parent):
+ """visit a AssAttr node by returning a fresh instance of it"""
+ assc, self.asscontext = self.asscontext, None
+ newnode = new.AssAttr()
+ _lineno_parent(node, newnode, parent)
+ newnode.expr = self.visit(node.expr, newnode)
+ self.asscontext = assc
+ self._delayed_assattr.append(newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
- # visit_<node> and delayed_<node> methods #################################
+ def visit_assert(self, node, parent):
+ """visit a Assert node by returning a fresh instance of it"""
+ newnode = new.Assert()
+ _lineno_parent(node, newnode, parent)
+ newnode.test = self.visit(node.test, newnode)
+ if node.msg is not None:
+ newnode.fail = self.visit(node.msg, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
- def _set_assign_infos(self, newnode):
- """set some function or metaclass infos""" # XXX right ?
+ def visit_assign(self, node, parent):
+ """visit a Assign node by returning a fresh instance of it"""
+ newnode = new.Assign()
+ _lineno_parent(node, newnode, parent)
+ self.asscontext = "Ass"
+ newnode.targets = [self.visit(child, newnode) for child in node.targets]
+ self.asscontext = None
+ newnode.value = self.visit(node.value, newnode)
+ # set some function or metaclass infos XXX explain ?
klass = newnode.parent.frame()
- if (isinstance(klass, nodes.Class)
- and isinstance(newnode.value, nodes.CallFunc)
- and isinstance(newnode.value.func, nodes.Name)):
+ if (isinstance(klass, new.Class)
+ and isinstance(newnode.value, new.CallFunc)
+ and isinstance(newnode.value.func, new.Name)):
func_name = newnode.value.func.name
for ass_node in newnode.targets:
try:
meth = klass[ass_node.name]
- if isinstance(meth, nodes.Function):
+ if isinstance(meth, new.Function):
if func_name in ('classmethod', 'staticmethod'):
meth.type = func_name
- try: # XXX use setdefault ?
- meth.extra_decorators.append(newnode.value)
- except AttributeError:
- meth.extra_decorators = [newnode.value]
+ meth.extra_decorators.append(newnode.value)
except (AttributeError, KeyError):
continue
elif getattr(newnode.targets[0], 'name', None) == '__metaclass__':
# XXX check more...
self._metaclass[-1] = 'type' # XXX get the actual metaclass
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_assname(self, node, parent, node_name=None):
+ '''visit a node and return a AssName node'''
+ newnode = new.AssName()
+ _set_infos(node, newnode, parent)
+ newnode.name = node_name
+ self._save_assignment(newnode)
+ return newnode
+
+ def visit_augassign(self, node, parent):
+ """visit a AugAssign node by returning a fresh instance of it"""
+ newnode = new.AugAssign()
+ _lineno_parent(node, newnode, parent)
+ newnode.op = _BIN_OP_CLASSES[node.op.__class__] + "="
+ self.asscontext = "Ass"
+ newnode.target = self.visit(node.target, newnode)
+ self.asscontext = None
+ newnode.value = self.visit(node.value, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_backquote(self, node, parent):
+ """visit a Backquote node by returning a fresh instance of it"""
+ newnode = new.Backquote()
+ _lineno_parent(node, newnode, parent)
+ newnode.value = self.visit(node.value, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_binop(self, node, parent):
+ """visit a BinOp node by returning a fresh instance of it"""
+ newnode = new.BinOp()
+ _lineno_parent(node, newnode, parent)
+ newnode.left = self.visit(node.left, newnode)
+ newnode.right = self.visit(node.right, newnode)
+ newnode.op = _BIN_OP_CLASSES[node.op.__class__]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_boolop(self, node, parent):
+ """visit a BoolOp node by returning a fresh instance of it"""
+ newnode = new.BoolOp()
+ _lineno_parent(node, newnode, parent)
+ newnode.values = [self.visit(child, newnode) for child in node.values]
+ newnode.op = _BOOL_OP_CLASSES[node.op.__class__]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_break(self, node, parent):
+ """visit a Break node by returning a fresh instance of it"""
+ newnode = new.Break()
+ _set_infos(node, newnode, parent)
+ return newnode
+
+ def visit_callfunc(self, node, parent):
+ """visit a CallFunc node by returning a fresh instance of it"""
+ newnode = new.CallFunc()
+ _lineno_parent(node, newnode, parent)
+ newnode.func = self.visit(node.func, newnode)
+ newnode.args = [self.visit(child, newnode) for child in node.args]
+ if node.starargs is not None:
+ newnode.starargs = self.visit(node.starargs, newnode)
+ if node.kwargs is not None:
+ newnode.kwargs = self.visit(node.kwargs, newnode)
+ newnode.args.extend(self.visit(child, newnode) for child in node.keywords)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
def visit_class(self, node, parent):
"""visit a Class node to become astng"""
self._metaclass.append(self._metaclass[-1])
- newnode = self._visit_class(node, parent)
+ newnode = new.Class(node.name, None)
+ _lineno_parent(node, newnode, parent)
+ _init_set_doc(node, newnode)
+ newnode.bases = [self.visit(child, newnode) for child in node.bases]
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ if 'decorator_list' in node._fields and node.decorator_list:# py >= 2.6
+ newnode.decorators = self.visit_decorators(node, newnode)
+ newnode.set_line_info(newnode.last_child())
metaclass = self._metaclass.pop()
if not newnode.bases:
# no base classes, detect new / style old style according to
@@ -163,148 +314,555 @@ class RebuildVisitor(object):
newnode.parent.frame().set_local(newnode.name, newnode)
return newnode
- def visit_break(self, node, parent):
- """visit a Break node by returning a fresh instance of it"""
- newnode = nodes.Break()
- self._set_infos(node, newnode, parent)
- return newnode
-
def visit_const(self, node, parent):
"""visit a Const node by returning a fresh instance of it"""
- newnode = nodes.Const(node.value)
- self._set_infos(node, newnode, parent)
+ newnode = new.Const(node.value)
+ _set_infos(node, newnode, parent)
return newnode
def visit_continue(self, node, parent):
"""visit a Continue node by returning a fresh instance of it"""
- newnode = nodes.Continue()
- self._set_infos(node, newnode, parent)
+ newnode = new.Continue()
+ _set_infos(node, newnode, parent)
+ return newnode
+
+ def visit_compare(self, node, parent):
+ """visit a Compare node by returning a fresh instance of it"""
+ newnode = new.Compare()
+ _lineno_parent(node, newnode, parent)
+ newnode.left = self.visit(node.left, newnode)
+ newnode.ops = [(_CMP_OP_CLASSES[op.__class__], self.visit(expr, newnode))
+ for (op, expr) in zip(node.ops, node.comparators)]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_comprehension(self, node, parent):
+ """visit a Comprehension node by returning a fresh instance of it"""
+ newnode = new.Comprehension()
+ _lineno_parent(node, newnode, parent)
+ self.asscontext = "Ass"
+ newnode.target = self.visit(node.target, newnode)
+ self.asscontext = None
+ newnode.iter = self.visit(node.iter, newnode)
+ newnode.ifs = [self.visit(child, newnode) for child in node.ifs]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_decorators(self, node, parent):
+ """visit a Decorators node by returning a fresh instance of it"""
+ # /!\ node is actually a _ast.Function node while
+ # parent is a astng.nodes.Function node
+ newnode = new.Decorators()
+ _lineno_parent(node, newnode, parent)
+ if 'decorators' in node._fields: # py < 2.6, i.e. 2.5
+ decorators = node.decorators
+ else:
+ decorators= node.decorator_list
+ newnode.nodes = [self.visit(child, newnode) for child in decorators]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_delete(self, node, parent):
+ """visit a Delete node by returning a fresh instance of it"""
+ newnode = new.Delete()
+ _lineno_parent(node, newnode, parent)
+ self.asscontext = "Del"
+ newnode.targets = [self.visit(child, newnode) for child in node.targets]
+ self.asscontext = None
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_dict(self, node, parent):
+ """visit a Dict node by returning a fresh instance of it"""
+ newnode = new.Dict()
+ _lineno_parent(node, newnode, parent)
+ newnode.items = [(self.visit(key, newnode), self.visit(value, newnode))
+ for key, value in zip(node.keys, node.values)]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_dictcomp(self, node, parent):
+ """visit a DictComp node by returning a fresh instance of it"""
+ newnode = new.DictComp()
+ _lineno_parent(node, newnode, parent)
+ newnode.key = self.visit(node.key, newnode)
+ newnode.value = self.visit(node.value, newnode)
+ newnode.generators = [self.visit(child, newnode)
+ for child in node.generators]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_discard(self, node, parent):
+ """visit a Discard node by returning a fresh instance of it"""
+ newnode = new.Discard()
+ _lineno_parent(node, newnode, parent)
+ newnode.value = self.visit(node.value, newnode)
+ newnode.set_line_info(newnode.last_child())
return newnode
def visit_ellipsis(self, node, parent):
"""visit an Ellipsis node by returning a fresh instance of it"""
- newnode = nodes.Ellipsis()
- self._set_infos(node, newnode, parent)
+ newnode = new.Ellipsis()
+ _set_infos(node, newnode, parent)
return newnode
def visit_emptynode(self, node, parent):
"""visit an EmptyNode node by returning a fresh instance of it"""
- newnode = nodes.EmptyNode()
- self._set_infos(node, newnode, parent)
+ newnode = new.EmptyNode()
+ _set_infos(node, newnode, parent)
return newnode
- def _store_from_node(self, node):
- """handle From names by adding them to locals now or after"""
- # we can not handle wildcard imports if the source module is not
- # in the cache since 'import_module' calls the MANAGER and we will
- # end up with infinite recursions working with unfinished trees
- if node.modname in self._manager._cache:
- self._add_from_names_to_locals(node)
- else:
- self._from_nodes.append(node)
-
- def _add_from_names_to_locals(self, node, delayed=False):
- """store imported names to the locals;
- resort the locals if coming from a delayed node
- """
- for (name, asname) in node.names:
- if name == '*':
- try:
- imported = node.root().import_module(node.modname)
- except ASTNGBuildingException:
- continue
- for name in imported.wildcard_import_names():
- node.parent.set_local(name, node)
- if delayed:
- sort_locals(node.parent.scope().locals[name])
+ def visit_excepthandler(self, node, parent):
+ """visit an ExceptHandler node by returning a fresh instance of it"""
+ newnode = new.ExceptHandler()
+ _lineno_parent(node, newnode, parent)
+ if node.type is not None:
+ newnode.type = self.visit(node.type, newnode)
+ if node.name is not None:
+ # /!\ node.name can be a tuple
+ self.asscontext = "Ass"
+ newnode.name = self.visit(node.name, newnode)
+ self.asscontext = None
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
- else:
- node.parent.set_local(asname or name, node)
- if delayed:
- sort_locals(node.parent.scope().locals[asname or name])
+ def visit_exec(self, node, parent):
+ """visit an Exec node by returning a fresh instance of it"""
+ newnode = new.Exec()
+ _lineno_parent(node, newnode, parent)
+ newnode.expr = self.visit(node.body, newnode)
+ if node.globals is not None:
+ newnode.globals = self.visit(node.globals, newnode)
+ if node.locals is not None:
+ newnode.locals = self.visit(node.locals, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_extslice(self, node, parent):
+ """visit an ExtSlice node by returning a fresh instance of it"""
+ newnode = new.ExtSlice()
+ _lineno_parent(node, newnode, parent)
+ newnode.dims = [self.visit(dim, newnode) for dim in node.dims]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_for(self, node, parent):
+ """visit a For node by returning a fresh instance of it"""
+ newnode = new.For()
+ _lineno_parent(node, newnode, parent)
+ self.asscontext = "Ass"
+ newnode.target = self.visit(node.target, newnode)
+ self.asscontext = None
+ newnode.iter = self.visit(node.iter, newnode)
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+ def visit_from(self, node, parent):
+ """visit a From node by returning a fresh instance of it"""
+ names = [(alias.name, alias.asname) for alias in node.names]
+ newnode = new.From(node.module or '', names, node.level)
+ _set_infos(node, newnode, parent)
+ # store From names to add them to locals after building
+ self._from_nodes.append(newnode)
+ return newnode
def visit_function(self, node, parent):
"""visit an Function node to become astng"""
self._global_names.append({})
- newnode = self._visit_function(node, parent)
+ newnode = new.Function(node.name, None)
+ _lineno_parent(node, newnode, parent)
+ _init_set_doc(node, newnode)
+ newnode.args = self.visit(node.args, newnode)
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ if 'decorators' in node._fields: # py < 2.6
+ attr = 'decorators'
+ else:
+ attr = 'decorator_list'
+ decorators = getattr(node, attr)
+ if decorators:
+ newnode.decorators = self.visit_decorators(node, newnode)
+ newnode.set_line_info(newnode.last_child())
self._global_names.pop()
frame = newnode.parent.frame()
- if isinstance(frame, nodes.Class):
+ if isinstance(frame, new.Class):
if newnode.name == '__new__':
newnode.type = 'classmethod'
else:
newnode.type = 'method'
if newnode.decorators is not None:
for decorator_expr in newnode.decorators.nodes:
- if isinstance(decorator_expr, nodes.Name) and \
+ if isinstance(decorator_expr, new.Name) and \
decorator_expr.name in ('classmethod', 'staticmethod'):
newnode.type = decorator_expr.name
frame.set_local(newnode.name, newnode)
return newnode
+ def visit_genexpr(self, node, parent):
+ """visit a GenExpr node by returning a fresh instance of it"""
+ newnode = new.GenExpr()
+ _lineno_parent(node, newnode, parent)
+ newnode.elt = self.visit(node.elt, newnode)
+ newnode.generators = [self.visit(child, newnode) for child in node.generators]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_getattr(self, node, parent):
+ """visit a Getattr node by returning a fresh instance of it"""
+ if self.asscontext == "Del":
+ # FIXME : maybe we should reintroduce and visit_delattr ?
+ # for instance, deactivating asscontext
+ newnode = new.DelAttr()
+ elif self.asscontext == "Ass":
+ # FIXME : maybe we should call visit_assattr ?
+ newnode = new.AssAttr()
+ self._delayed_assattr.append(newnode)
+ else:
+ newnode = new.Getattr()
+ _lineno_parent(node, newnode, parent)
+ asscontext, self.asscontext = self.asscontext, None
+ newnode.expr = self.visit(node.value, newnode)
+ self.asscontext = asscontext
+ newnode.attrname = node.attr
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
def visit_global(self, node, parent):
"""visit an Global node to become astng"""
- newnode = nodes.Global(node.names)
- self._set_infos(node, newnode, parent)
+ newnode = new.Global(node.names)
+ _set_infos(node, newnode, parent)
if self._global_names: # global at the module level, no effect
for name in node.names:
self._global_names[-1].setdefault(name, []).append(newnode)
return newnode
- def _save_import_locals(self, newnode):
- """save import names in parent's locals"""
+ def visit_if(self, node, parent):
+ """visit a If node by returning a fresh instance of it"""
+ newnode = new.If()
+ _lineno_parent(node, newnode, parent)
+ newnode.test = self.visit(node.test, newnode)
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_ifexp(self, node, parent):
+ """visit a IfExp node by returning a fresh instance of it"""
+ newnode = new.IfExp()
+ _lineno_parent(node, newnode, parent)
+ newnode.test = self.visit(node.test, newnode)
+ newnode.body = self.visit(node.body, newnode)
+ newnode.orelse = self.visit(node.orelse, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_import(self, node, parent):
+ """visit a Import node by returning a fresh instance of it"""
+ newnode = new.Import()
+ _set_infos(node, newnode, parent)
+ newnode.names = [(alias.name, alias.asname) for alias in node.names]
+ # save import names in parent's locals:
for (name, asname) in newnode.names:
name = asname or name
newnode.parent.set_local(name.split('.')[0], newnode)
+ return newnode
+
+ def visit_index(self, node, parent):
+ """visit a Index node by returning a fresh instance of it"""
+ newnode = new.Index()
+ _lineno_parent(node, newnode, parent)
+ newnode.value = self.visit(node.value, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_keyword(self, node, parent):
+ """visit a Keyword node by returning a fresh instance of it"""
+ newnode = new.Keyword()
+ _lineno_parent(node, newnode, parent)
+ newnode.arg = node.arg
+ newnode.value = self.visit(node.value, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_lambda(self, node, parent):
+ """visit a Lambda node by returning a fresh instance of it"""
+ newnode = new.Lambda()
+ _lineno_parent(node, newnode, parent)
+ newnode.args = self.visit(node.args, newnode)
+ newnode.body = self.visit(node.body, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_list(self, node, parent):
+ """visit a List node by returning a fresh instance of it"""
+ newnode = new.List()
+ _lineno_parent(node, newnode, parent)
+ newnode.elts = [self.visit(child, newnode) for child in node.elts]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_listcomp(self, node, parent):
+ """visit a ListComp node by returning a fresh instance of it"""
+ newnode = new.ListComp()
+ _lineno_parent(node, newnode, parent)
+ newnode.elt = self.visit(node.elt, newnode)
+ newnode.generators = [self.visit(child, newnode)
+ for child in node.generators]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_module(self, node, modname, package):
+ """visit a Module node by returning a fresh instance of it"""
+ newnode = new.Module(modname, None)
+ newnode.package = package
+ _lineno_parent(node, newnode, parent=None)
+ _init_set_doc(node, newnode)
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_name(self, node, parent):
+ """visit a Name node by returning a fresh instance of it"""
+ # True and False can be assigned to something in py2x, so we have to
+ # check first the asscontext
+ if self.asscontext == "Del":
+ newnode = new.DelName()
+ elif self.asscontext is not None: # Ass
+ assert self.asscontext == "Ass"
+ newnode = new.AssName()
+ elif node.id in CONST_NAME_TRANSFORMS:
+ newnode = new.Const(CONST_NAME_TRANSFORMS[node.id])
+ _set_infos(node, newnode, parent)
+ return newnode
+ else:
+ newnode = new.Name()
+ _lineno_parent(node, newnode, parent)
+ newnode.name = node.id
+ # XXX REMOVE me :
+ if self.asscontext in ('Del', 'Ass'): # 'Aug' ??
+ self._save_assignment(newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_bytes(self, node, parent):
+ """visit a Bytes node by returning a fresh instance of Const"""
+ newnode = new.Const(node.s)
+ _set_infos(node, newnode, parent)
+ return newnode
+
+ def visit_num(self, node, parent):
+ """visit a Num node by returning a fresh instance of Const"""
+ newnode = new.Const(node.n)
+ _set_infos(node, newnode, parent)
+ return newnode
def visit_pass(self, node, parent):
"""visit a Pass node by returning a fresh instance of it"""
- newnode = nodes.Pass()
- self._set_infos(node, newnode, parent)
+ newnode = new.Pass()
+ _set_infos(node, newnode, parent)
return newnode
- def _save_assignment(self, node, name=None):
- """save assignement situation since node.parent is not available yet"""
- if self._global_names and node.name in self._global_names[-1]:
- node.root().set_local(node.name, node)
- else:
- node.parent.set_local(node.name, node)
+ def visit_str(self, node, parent):
+ """visit a Str node by returning a fresh instance of Const"""
+ newnode = new.Const(node.s)
+ _set_infos(node, newnode, parent)
+ return newnode
- def delayed_assattr(self, node):
- """visit a AssAttr node -> add name to locals, handle members
- definition
- """
- try:
- frame = node.frame()
- for infered in node.expr.infer():
- if infered is YES:
- continue
- try:
- if infered.__class__ is Instance:
- infered = infered._proxied
- iattrs = infered.instance_attrs
- elif isinstance(infered, Instance):
- # Const, Tuple, ... we may be wrong, may be not, but
- # anyway we don't want to pollute builtin's namespace
- continue
- else:
- iattrs = infered.locals
- except AttributeError:
- # XXX log error
- #import traceback
- #traceback.print_exc()
- continue
- values = iattrs.setdefault(node.attrname, [])
- if node in values:
- continue
- # get assign in __init__ first XXX useful ?
- if frame.name == '__init__' and values and not \
- values[0].frame().name == '__init__':
- values.insert(0, node)
- else:
- values.append(node)
- except InferenceError:
- pass
+ def visit_print(self, node, parent):
+ """visit a Print node by returning a fresh instance of it"""
+ newnode = new.Print()
+ _lineno_parent(node, newnode, parent)
+ newnode.nl = node.nl
+ if node.dest is not None:
+ newnode.dest = self.visit(node.dest, newnode)
+ newnode.values = [self.visit(child, newnode) for child in node.values]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_raise(self, node, parent):
+ """visit a Raise node by returning a fresh instance of it"""
+ newnode = new.Raise()
+ _lineno_parent(node, newnode, parent)
+ if node.type is not None:
+ newnode.exc = self.visit(node.type, newnode)
+ if node.inst is not None:
+ newnode.inst = self.visit(node.inst, newnode)
+ if node.tback is not None:
+ newnode.tback = self.visit(node.tback, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_return(self, node, parent):
+ """visit a Return node by returning a fresh instance of it"""
+ newnode = new.Return()
+ _lineno_parent(node, newnode, parent)
+ if node.value is not None:
+ newnode.value = self.visit(node.value, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_set(self, node, parent):
+ """visit a Tuple node by returning a fresh instance of it"""
+ newnode = new.Set()
+ _lineno_parent(node, newnode, parent)
+ newnode.elts = [self.visit(child, newnode) for child in node.elts]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_setcomp(self, node, parent):
+ """visit a SetComp node by returning a fresh instance of it"""
+ newnode = new.SetComp()
+ _lineno_parent(node, newnode, parent)
+ newnode.elt = self.visit(node.elt, newnode)
+ newnode.generators = [self.visit(child, newnode)
+ for child in node.generators]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_slice(self, node, parent):
+ """visit a Slice node by returning a fresh instance of it"""
+ newnode = new.Slice()
+ _lineno_parent(node, newnode, parent)
+ if node.lower is not None:
+ newnode.lower = self.visit(node.lower, newnode)
+ if node.upper is not None:
+ newnode.upper = self.visit(node.upper, newnode)
+ if node.step is not None:
+ newnode.step = self.visit(node.step, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_subscript(self, node, parent):
+ """visit a Subscript node by returning a fresh instance of it"""
+ newnode = new.Subscript()
+ _lineno_parent(node, newnode, parent)
+ subcontext, self.asscontext = self.asscontext, None
+ newnode.value = self.visit(node.value, newnode)
+ newnode.slice = self.visit(node.slice, newnode)
+ self.asscontext = subcontext
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_tryexcept(self, node, parent):
+ """visit a TryExcept node by returning a fresh instance of it"""
+ newnode = new.TryExcept()
+ _lineno_parent(node, newnode, parent)
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ newnode.handlers = [self.visit(child, newnode) for child in node.handlers]
+ newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_tryfinally(self, node, parent):
+ """visit a TryFinally node by returning a fresh instance of it"""
+ newnode = new.TryFinally()
+ _lineno_parent(node, newnode, parent)
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_tuple(self, node, parent):
+ """visit a Tuple node by returning a fresh instance of it"""
+ newnode = new.Tuple()
+ _lineno_parent(node, newnode, parent)
+ newnode.elts = [self.visit(child, newnode) for child in node.elts]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_unaryop(self, node, parent):
+ """visit a UnaryOp node by returning a fresh instance of it"""
+ newnode = new.UnaryOp()
+ _lineno_parent(node, newnode, parent)
+ newnode.operand = self.visit(node.operand, newnode)
+ newnode.op = _UNARY_OP_CLASSES[node.op.__class__]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_while(self, node, parent):
+ """visit a While node by returning a fresh instance of it"""
+ newnode = new.While()
+ _lineno_parent(node, newnode, parent)
+ newnode.test = self.visit(node.test, newnode)
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_with(self, node, parent):
+ """visit a With node by returning a fresh instance of it"""
+ newnode = new.With()
+ _lineno_parent(node, newnode, parent)
+ newnode.expr = self.visit(node.context_expr, newnode)
+ self.asscontext = "Ass"
+ if node.optional_vars is not None:
+ newnode.vars = self.visit(node.optional_vars, newnode)
+ self.asscontext = None
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_yield(self, node, parent):
+ """visit a Yield node by returning a fresh instance of it"""
+ newnode = new.Yield()
+ _lineno_parent(node, newnode, parent)
+ if node.value is not None:
+ newnode.value = self.visit(node.value, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+
+class TreeRebuilder3k(TreeRebuilder):
+ """extend and overwrite TreeRebuilder for python3k"""
+
+ def visit_arg(self, node, parent):
+ """visit a arg node by returning a fresh AssName instance"""
+ # the <arg> node is coming from py>=3.0, but we use AssName in py2.x
+ # XXX or we should instead introduce a Arg node in astng ?
+ return self.visit_assname(node, parent, node.arg)
+
+ def visit_excepthandler(self, node, parent):
+ """visit an ExceptHandler node by returning a fresh instance of it"""
+ newnode = new.ExceptHandler()
+ _lineno_parent(node, newnode, parent)
+ if node.type is not None:
+ newnode.type = self.visit(node.type, newnode)
+ if node.name is not None:
+ newnode.name = self.visit_assname(node, newnode, node.name)
+ newnode.body = [self.visit(child, newnode) for child in node.body]
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_nonlocal(self, node, parent):
+ """visit a Nonlocal node and return a new instance of it"""
+ newnode = new.Nonlocal(node.names)
+ _set_infos(node, newnode, parent)
+ return newnode
+
+ def visit_raise(self, node, parent):
+ """visit a Raise node by returning a fresh instance of it"""
+ newnode = new.Raise()
+ _lineno_parent(node, newnode, parent)
+ # no traceback; anyway it is not used in Pylint
+ if node.exc is not None:
+ newnode.exc = self.visit(node.exc, newnode)
+ if node.cause is not None:
+ newnode.cause = self.visit(node.cause, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+ def visit_starred(self, node, parent):
+ """visit a Starred node and return a new instance of it"""
+ newnode = new.Starred()
+ _lineno_parent(node, newnode, parent)
+ newnode.value = self.visit(node.value, newnode)
+ newnode.set_line_info(newnode.last_child())
+ return newnode
+
+
+if sys.version_info >= (3, 0):
+ TreeRebuilder = TreeRebuilder3k
diff --git a/scoped_nodes.py b/scoped_nodes.py
index b5acd364..42f6248a 100644
--- a/scoped_nodes.py
+++ b/scoped_nodes.py
@@ -20,9 +20,7 @@
"""This module contains the classes for "scoped" node, i.e. which are opening a
new local scope in the language definition : Module, Class, Function (and
Lambda, GenExpr, DictComp and SetComp to some extent).
-
"""
-from __future__ import generators
__doctype__ = "restructuredtext en"
@@ -32,15 +30,16 @@ from itertools import chain
from logilab.common.compat import builtins
from logilab.common.decorators import cached
-from logilab.astng import NotFoundError, NoDefault, \
+from logilab.astng.exceptions import NotFoundError, NoDefault, \
ASTNGBuildingException, InferenceError
from logilab.astng.node_classes import Const, DelName, DelAttr, \
Dict, From, List, Name, Pass, Raise, Return, Tuple, Yield, \
are_exclusive, LookupMixIn, const_factory as cf, unpack_infer
-from logilab.astng.bases import NodeNG, BaseClass, InferenceContext, Instance,\
+from logilab.astng.bases import NodeNG, InferenceContext, Instance,\
YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, copy_context, \
BUILTINS_NAME
-from logilab.astng.mixins import StmtMixIn, FilterStmtsMixin
+from logilab.astng.mixins import FilterStmtsMixin
+from logilab.astng.bases import Statement
from logilab.astng.manager import ASTNGManager
@@ -242,13 +241,6 @@ class Module(LocalsDictNodeNG):
self.locals = self.globals = {}
self.body = []
- # Module is not a Statement node but needs the replace method (see StmtMixIn)
- def replace(self, child, newchild):
- sequence = self.child_sequence(child)
- newchild.parent = self
- child.parent = None
- sequence[sequence.index(child)] = newchild
-
def block_range(self, lineno):
"""return block line numbers.
@@ -271,25 +263,22 @@ class Module(LocalsDictNodeNG):
return 'Module'
def getattr(self, name, context=None):
- if not name in self.special_attributes:
- try:
- return self.locals[name]
- except KeyError:
- pass
- else:
+ if name in self.special_attributes:
if name == '__file__':
return [cf(self.file)] + self.locals.get(name, [])
- if name == '__path__':
- if self.package:
- return [List()] + self.locals.get(name, [])
+ if name == '__path__' and self.package:
+ return [List()] + self.locals.get(name, [])
return std_special_attributes(self, name)
+ if name in self.locals:
+ return self.locals[name]
if self.package:
try:
return [self.import_module(name, relative_only=True)]
- except (KeyboardInterrupt, SystemExit):
- raise
- except:
- pass
+ except ASTNGBuildingException:
+ raise NotFoundError(name)
+ except Exception:# XXX pylint tests never pass here; do we need it?
+ import traceback
+ traceback.print_exc()
raise NotFoundError(name)
getattr = remove_nodes(getattr, DelName)
@@ -324,11 +313,14 @@ class Module(LocalsDictNodeNG):
"""module has no sibling"""
return
- def absolute_import_activated(self):
- for stmt in self.locals.get('absolute_import', ()):
- if isinstance(stmt, From) and stmt.modname == '__future__':
- return True
- return False
+ if sys.version_info < (2, 7):
+ def absolute_import_activated(self):
+ for stmt in self.locals.get('absolute_import', ()):
+ if isinstance(stmt, From) and stmt.modname == '__future__':
+ return True
+ return False
+ else:
+ absolute_import_activated = lambda self: True
def import_module(self, modname, relative_only=False, level=None):
"""import the given module considering self as context"""
@@ -349,15 +341,15 @@ class Module(LocalsDictNodeNG):
"""
# XXX this returns non sens when called on an absolute import
# like 'pylint.checkers.logilab.astng.utils'
+ # XXX doesn't return absolute name if self.name isn't absolute name
if level:
- parts = self.name.split('.')
if self.package:
- parts.append('__init__')
- package_name = '.'.join(parts[:-level])
+ level = level - 1
+ package_name = self.name.rsplit('.', level)[0]
elif self.package:
package_name = self.name
else:
- package_name = '.'.join(self.name.split('.')[:-1])
+ package_name = self.name.rsplit('.', 1)[0]
if package_name:
if not modname:
return package_name
@@ -511,7 +503,7 @@ class Lambda(LocalsDictNodeNG, FilterStmtsMixin):
frame = self
return frame._scope_lookup(node, name, offset)
-class Function(StmtMixIn, Lambda):
+class Function(Statement, Lambda):
_astng_fields = ('decorators', 'args', 'body')
special_attributes = set(('__name__', '__doc__', '__dict__'))
@@ -527,6 +519,8 @@ class Function(StmtMixIn, Lambda):
self.decorators = None
self.name = name
self.doc = doc
+ self.extra_decorators = []
+ self.instance_attrs = {}
def set_line_info(self, lastchild):
self.fromlineno = self.lineno
@@ -549,6 +543,8 @@ class Function(StmtMixIn, Lambda):
"""
if name == '__module__':
return [cf(self.root().qname())]
+ if name in self.instance_attrs:
+ return self.instance_attrs[name]
return std_special_attributes(self, name, False)
def is_method(self):
@@ -563,7 +559,7 @@ class Function(StmtMixIn, Lambda):
decoratornodes = []
if self.decorators is not None:
decoratornodes += self.decorators.nodes
- decoratornodes += getattr(self, 'extra_decorators', [])
+ decoratornodes += self.extra_decorators
for decnode in decoratornodes:
for infnode in decnode.infer():
result.add(infnode.qname())
@@ -666,7 +662,7 @@ def _iface_hdlr(iface_node):
return True
-class Class(StmtMixIn, LocalsDictNodeNG, FilterStmtsMixin):
+class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin):
# some of the attributes below are set by the builder module or
# by a raw factories
@@ -675,10 +671,8 @@ class Class(StmtMixIn, LocalsDictNodeNG, FilterStmtsMixin):
_astng_fields = ('decorators', 'bases', 'body') # name
decorators = None
- instance_attrs = None
special_attributes = set(('__name__', '__doc__', '__dict__', '__module__',
'__bases__', '__mro__', '__subclasses__'))
-
blockstart_tolineno = None
_type = None
@@ -859,6 +853,9 @@ class Class(StmtMixIn, LocalsDictNodeNG, FilterStmtsMixin):
if name in self.special_attributes:
if name == '__module__':
return [cf(self.root().qname())] + values
+ # FIXME : what is expected by passing the list of ancestors to cf:
+ # you can just do [cf(tuple())] + values without breaking any test
+ # this is ticket http://www.logilab.org/ticket/52785
if name == '__bases__':
return [cf(tuple(self.ancestors(recurs=False, context=context)))] + values
# XXX need proper meta class handling + MRO implementation
diff --git a/test/data/module2.py b/test/data/module2.py
index c8eba5e6..2b729c0d 100644
--- a/test/data/module2.py
+++ b/test/data/module2.py
@@ -81,6 +81,7 @@ exec 'c = 3' in {}, {}
def raise_string(a=2, *args, **kwargs):
raise Exception, 'yo'
yield 'coucou'
+ yield
a = (b) + (2)
c = (b) * (2)
c = (b) / (2)
diff --git a/test/unittest_builder.py b/test/unittest_builder.py
index cbb017a7..54a8dc58 100644
--- a/test/unittest_builder.py
+++ b/test/unittest_builder.py
@@ -43,8 +43,10 @@ from logilab.astng.nodes import Module
from logilab.astng.bases import YES, BUILTINS_NAME
from logilab.astng.as_string import as_string
from logilab.astng.manager import ASTNGManager
+
MANAGER = ASTNGManager()
+
from unittest_inference import get_name_node
import data
@@ -114,11 +116,15 @@ class FromToLineNoTC(TestCase):
self.assertIsInstance(function, nodes.Function)
self.assertEqual(function.fromlineno, 15)
self.assertEqual(function.tolineno, 18)
- self.assertEqual(function.blockstart_tolineno, 17)
return_ = function.body[0]
self.assertIsInstance(return_, nodes.Return)
self.assertEqual(return_.fromlineno, 18)
self.assertEqual(return_.tolineno, 18)
+ if sys.version_info < (3, 0):
+ self.assertEqual(function.blockstart_tolineno, 17)
+ else:
+ self.skipTest('FIXME http://bugs.python.org/issue10445 '
+ '(no line number on function args)')
def test_decorated_function_lineno(self):
astng = builder.ASTNGBuilder().string_build('''
@@ -130,9 +136,13 @@ def function(
function = astng['function']
self.assertEqual(function.fromlineno, 3) # XXX discussable, but that's what is expected by pylint right now
self.assertEqual(function.tolineno, 5)
- self.assertEqual(function.blockstart_tolineno, 4)
self.assertEqual(function.decorators.fromlineno, 2)
self.assertEqual(function.decorators.tolineno, 2)
+ if sys.version_info < (3, 0):
+ self.assertEqual(function.blockstart_tolineno, 4)
+ else:
+ self.skipTest('FIXME http://bugs.python.org/issue10445 '
+ '(no line number on function args)')
def test_class_lineno(self):
@@ -294,8 +304,12 @@ class BuilderTC(TestCase):
self.assertIsInstance(builtin_astng['None'], nodes.Const)
self.assertIsInstance(builtin_astng['True'], nodes.Const)
self.assertIsInstance(builtin_astng['False'], nodes.Const)
- self.assertIsInstance(builtin_astng['Exception'], nodes.From)
- self.assertIsInstance(builtin_astng['NotImplementedError'], nodes.From)
+ if sys.version_info < (3, 0):
+ self.assertIsInstance(builtin_astng['Exception'], nodes.From)
+ self.assertIsInstance(builtin_astng['NotImplementedError'], nodes.From)
+ else:
+ self.assertIsInstance(builtin_astng['Exception'], nodes.Class)
+ self.assertIsInstance(builtin_astng['NotImplementedError'], nodes.Class)
def test_inspect_build1(self):
time_astng = MANAGER.astng_from_module_name('time')
@@ -315,7 +329,7 @@ class BuilderTC(TestCase):
#dt_astng.getattr('DateTimeType')
def test_inspect_build3(self):
- unittest_astng = self.builder.inspect_build(unittest)
+ self.builder.inspect_build(unittest)
def test_inspect_build_instance(self):
"""test astng tree build from a living object"""
@@ -479,8 +493,7 @@ class FileBuildTC(TestCase):
keys = sorted(_locals.keys())
should = ['MY_DICT', 'YO', 'YOUPI',
'__revision__', 'global_access','modutils', 'four_args',
- 'os', 'redirect', 'spawn', 'LocalsVisitor',
- 'ASTWalker', 'ASTVisitor']
+ 'os', 'redirect', 'spawn', 'LocalsVisitor', 'ASTWalker']
should.sort()
self.assertEqual(keys, should)
@@ -671,5 +684,44 @@ def func():
self.assertEqual(chain.value, 'None')
+if sys.version_info < (3, 0):
+ guess_encoding = builder._guess_encoding
+
+ class TestGuessEncoding(TestCase):
+
+ def testEmacs(self):
+ e = guess_encoding('# -*- coding: UTF-8 -*-')
+ self.failUnlessEqual(e, 'UTF-8')
+ e = guess_encoding('# -*- coding:UTF-8 -*-')
+ self.failUnlessEqual(e, 'UTF-8')
+ e = guess_encoding('''
+ ### -*- coding: ISO-8859-1 -*-
+ ''')
+ self.failUnlessEqual(e, 'ISO-8859-1')
+ e = guess_encoding('''
+
+ ### -*- coding: ISO-8859-1 -*-
+ ''')
+ self.failUnlessEqual(e, None)
+
+ def testVim(self):
+ e = guess_encoding('# vim:fileencoding=UTF-8')
+ self.failUnlessEqual(e, 'UTF-8')
+ e = guess_encoding('''
+ ### vim:fileencoding=ISO-8859-1
+ ''')
+ self.failUnlessEqual(e, 'ISO-8859-1')
+ e = guess_encoding('''
+
+ ### vim:fileencoding= ISO-8859-1
+ ''')
+ self.failUnlessEqual(e, None)
+
+ def testUTF8(self):
+ e = guess_encoding('\xef\xbb\xbf any UTF-8 data')
+ self.failUnlessEqual(e, 'UTF-8')
+ e = guess_encoding(' any UTF-8 data \xef\xbb\xbf')
+ self.failUnlessEqual(e, None)
+
if __name__ == '__main__':
unittest_main()
diff --git a/test/unittest_inference.py b/test/unittest_inference.py
index c10660d9..cf970f76 100644
--- a/test/unittest_inference.py
+++ b/test/unittest_inference.py
@@ -22,7 +22,8 @@ import sys
from StringIO import StringIO
from logilab.common.testlib import TestCase, unittest_main
-from logilab.astng import InferenceError, builder, nodes, inference
+from logilab.astng import InferenceError, builder, nodes
+from logilab.astng.inference import infer_end as inference_infer_end
from logilab.astng.bases import YES, Instance, BoundMethod, UnboundMethod,\
path_wrapper, BUILTINS_NAME
@@ -40,7 +41,7 @@ class InferenceUtilsTC(TestCase):
def infer_default(self, *args):
raise InferenceError
infer_default = path_wrapper(infer_default)
- infer_end = path_wrapper(inference.infer_end)
+ infer_end = path_wrapper(inference_infer_end)
self.failUnlessRaises(InferenceError,
infer_default(1).next)
self.failUnlessEqual(infer_end(1).next(), 1)
@@ -1099,5 +1100,22 @@ Z = test()
self.assertIsInstance(infered[0]._proxied, nodes.Class)
self.assertEqual(infered[0]._proxied.name, 'list')
+ def test__new__(self):
+ code = '''
+class NewTest(object):
+ "doc"
+ def __new__(cls, arg):
+ self = object.__new__(cls)
+ self.arg = arg
+ return self
+
+n = NewTest()
+ '''
+ astng = builder.string_build(code, __name__, __file__)
+ self.assertRaises(InferenceError, list, astng['NewTest'].igetattr('arg'))
+ n = astng['n'].infer().next()
+ infered = list(n.igetattr('arg'))
+ self.assertEqual(len(infered), 1, infered)
+
if __name__ == '__main__':
unittest_main()
diff --git a/test/unittest_inspector.py b/test/unittest_inspector.py
index c149343d..12d8368e 100644
--- a/test/unittest_inspector.py
+++ b/test/unittest_inspector.py
@@ -43,7 +43,7 @@ from os.path import join, abspath, dirname
from logilab.astng import nodes, inspector
from logilab.astng.bases import Instance, YES
-from logilab.astng.manager import ASTNGManager
+from logilab.astng.manager import ASTNGManager, _silent_no_wrap
MANAGER = ASTNGManager()
def astng_wrapper(func, modname):
@@ -100,7 +100,7 @@ class LinkerTC(unittest.TestCase):
class LinkerTC2(LinkerTC):
def setUp(self):
- self.project = MANAGER.from_directory(DATA2)
+ self.project = MANAGER.project_from_files([DATA2], func_wrapper=_silent_no_wrap)
self.linker = inspector.Linker(self.project)
self.linker.visit(self.project)
diff --git a/test/unittest_lookup.py b/test/unittest_lookup.py
index e0d66904..388c2b2c 100644
--- a/test/unittest_lookup.py
+++ b/test/unittest_lookup.py
@@ -232,7 +232,19 @@ var
var = astng.body[1].value
self.assertRaises(UnresolvableName, var.infered)
-
+ def test_generator_attributes(self):
+ tree = builder.string_build("""
+def count():
+ "ntesxt"
+ yield 0
+
+iterer = count()
+num = iterer.next()
+ """)
+ next = tree.body[2].value.func # Getattr
+ gener = next.expr.infered()[0] # Genrator
+ # XXX gener._proxied is a Function which has no instance_attr
+ self.assertRaises(AttributeError, gener.getattr, 'next')
def test_explicit___name__(self):
code = '''
diff --git a/test/unittest_manager.py b/test/unittest_manager.py
index 6d9388fa..f505c7af 100644
--- a/test/unittest_manager.py
+++ b/test/unittest_manager.py
@@ -17,25 +17,28 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
-import unittest
+from logilab.common.testlib import TestCase, unittest_main
+
import sys
from os.path import join, abspath, dirname
-from logilab.astng.manager import ASTNGManager
+from logilab.astng.manager import ASTNGManager, _silent_no_wrap
from logilab.astng.bases import BUILTINS_NAME
DATA = join(dirname(abspath(__file__)), 'data')
-class ASTNGManagerTC(unittest.TestCase):
+class ASTNGManagerTC(TestCase):
def setUp(self):
self.manager = ASTNGManager()
-
+ self.manager.astng_cache.clear()
+
def test_astng_from_module(self):
+ import unittest
astng = self.manager.astng_from_module(unittest)
self.assertEqual(astng.pure_python, True)
import time
astng = self.manager.astng_from_module(time)
self.assertEqual(astng.pure_python, False)
-
+
def test_astng_from_class(self):
astng = self.manager.astng_from_class(int)
self.assertEqual(astng.name, 'int')
@@ -45,59 +48,49 @@ class ASTNGManagerTC(unittest.TestCase):
self.assertEqual(astng.name, 'object')
self.assertEqual(astng.parent.frame().name, BUILTINS_NAME)
self.failUnless('__setattr__' in astng)
-
+
def _test_astng_from_zip(self, archive):
origpath = sys.path[:]
sys.modules.pop('mypypa', None)
- sys.path.insert(0, join(DATA, archive))
+ archive_path = join(DATA, archive)
+ sys.path.insert(0, archive_path)
try:
module = self.manager.astng_from_module_name('mypypa')
self.assertEqual(module.name, 'mypypa')
self.failUnless(module.file.endswith('%s/mypypa' % archive),
module.file)
finally:
- sys.path = origpath
# remove the module, else after importing egg, we don't get the zip
- del self.manager._cache['mypypa']
- del self.manager._mod_file_cache[('mypypa', None)]
+ if 'mypypa' in self.manager.astng_cache:
+ del self.manager.astng_cache['mypypa']
+ del self.manager._mod_file_cache[('mypypa', None)]
+ if archive_path in sys.path_importer_cache:
+ del sys.path_importer_cache[archive_path]
+ sys.path = origpath
def test_astng_from_module_name_egg(self):
self._test_astng_from_zip('MyPyPa-0.1.0-py2.5.egg')
def test_astng_from_module_name_zip(self):
- self._test_astng_from_zip('MyPyPa-0.1.0-py2.5.zip')
-
+ self._test_astng_from_zip('MyPyPa-0.1.0-py2.5.zip')
+
def test_from_directory(self):
- obj = self.manager.from_directory(DATA)
+ obj = self.manager.project_from_files([DATA], _silent_no_wrap, 'data')
self.assertEqual(obj.name, 'data')
- self.assertEqual(obj.path, DATA)
-
- def test_package_node(self):
- obj = self.manager.from_directory(DATA)
- expected_short = ['SSL1', '__init__', 'all', 'appl', 'format', 'module', 'module2',
- 'noendingnewline', 'nonregr', 'notall']
- expected_long = ['SSL1', 'data', 'data.all', 'appl', 'data.format', 'data.module',
- 'data.module2', 'data.noendingnewline', 'data.nonregr',
- 'data.notall']
- self.assertEqual(obj.keys(), expected_short)
- self.assertEqual([m.name for m in obj.values()], expected_long)
- self.assertEqual([m for m in list(obj)], expected_short)
- self.assertEqual([(name, m.name) for name, m in obj.items()],
- zip(expected_short, expected_long))
- self.assertEqual([(name, m.name) for name, m in obj.items()],
- zip(expected_short, expected_long))
-
- self.assertEqual('module' in obj, True)
- self.assertTrue(obj.get('module'))
- self.assertEqual(obj.get('module').name, 'data.module')
- self.assertEqual(obj['module'].name, 'data.module')
- self.assertEqual(obj.get('whatever'), None)
- self.assertEqual(obj.fullname(), 'data')
- # FIXME: test fullname on a subpackage
-
-
-
-class BorgASTNGManagerTC(unittest.TestCase):
+ self.assertEqual(obj.path, join(DATA, '__init__.py'))
+
+ def test_project_node(self):
+ obj = self.manager.project_from_files([DATA], _silent_no_wrap, 'data')
+ expected = set(['SSL1', '__init__', 'all', 'appl', 'format', 'module',
+ 'module2', 'noendingnewline', 'nonregr', 'notall'])
+ expected = ['data', 'data.SSL1', 'data.SSL1.Connection1', 'data.all',
+ 'data.appl', 'data.appl.myConnection', 'data.format',
+ 'data.module', 'data.module2', 'data.noendingnewline',
+ 'data.nonregr', 'data.notall']
+ self.assertListEqual(sorted(k for k in obj.keys()), expected)
+
+
+class BorgASTNGManagerTC(TestCase):
def test_borg(self):
"""test that the ASTNGManager is really a borg, i.e. that two different
@@ -111,6 +104,6 @@ class BorgASTNGManagerTC(unittest.TestCase):
if __name__ == '__main__':
- unittest.main()
+ unittest_main()
+
-
diff --git a/test/unittest_nodes.py b/test/unittest_nodes.py
index 6a9e2dde..8af5f790 100644
--- a/test/unittest_nodes.py
+++ b/test/unittest_nodes.py
@@ -34,7 +34,8 @@
import sys
from logilab.common import testlib
-from logilab.astng import builder, nodes, NotFoundError
+from logilab.astng.exceptions import ASTNGBuildingException, NotFoundError
+from logilab.astng import builder, nodes
from logilab.astng.as_string import as_string
from data import module as test_module
@@ -45,6 +46,56 @@ DATA = join(dirname(abspath(__file__)), 'data')
abuilder = builder.ASTNGBuilder()
+class AsString(testlib.TestCase):
+
+ def test_varargs_kwargs_as_string(self):
+ ast = abuilder.string_build( 'raise_string(*args, **kwargs)').body[0]
+ self.assertEqual(as_string(ast), 'raise_string(*args, **kwargs)')
+
+ def test_module_as_string(self):
+ """check as_string on a whole module prepared to be returned identically
+ """
+ data = open(join(DATA, 'module.py')).read()
+ self.assertMultiLineEqual(as_string(MODULE), data)
+ data = open(join(DATA, 'module2.py')).read()
+ self.assertMultiLineEqual(as_string(MODULE2), data)
+
+ def test_2_7_as_string(self):
+ """check as_string for python syntax >= 2.7"""
+ if sys.version_info < (2, 7):
+ self.skipTest("test python >= 2.7 specific")
+ code = '''one_two = {1, 2}
+b = {v: k for (k, v) in enumerate('string')}
+cdd = {k for k in b}\n\n'''
+ ast = abuilder.string_build(code)
+ self.assertMultiLineEqual(as_string(ast), code)
+
+ def test_3k_as_string(self):
+ """check as_string for python 3k syntax"""
+ if sys.version_info < (3, 0):
+ self.skipTest("test python 3k specific")
+ code = '''print()
+
+def function(var):
+ nonlocal counter
+ try:
+ hello
+ except NameError as nexc:
+ (*hell, o) = b'hello'
+ raise AttributeError from nexc
+\n'''
+ # TODO : annotations and keywords for class definition are not yet implemented
+ _todo = '''
+def function(var:int):
+ nonlocal counter
+
+class Language(metaclass=Natural):
+ """natural language"""
+ '''
+ ast = abuilder.string_build(code)
+ self.assertEqual(as_string(ast), code)
+
+
class _NodeTC(testlib.TestCase):
"""test transformation of If Node"""
CODE = None
@@ -214,59 +265,18 @@ class ImportNodeTC(testlib.TestCase):
self.assertRaises(NotFoundError, imp_.real_name, 'data')
def test_as_string(self):
-
ast = MODULE['modutils']
self.assertEqual(as_string(ast), "from logilab.common import modutils")
ast = MODULE['spawn']
self.assertEqual(as_string(ast), "from logilab.common.shellutils import Execute as spawn")
ast = MODULE['os']
self.assertEqual(as_string(ast), "import os.path")
- ast = abuilder.string_build( 'raise_string(*args, **kwargs)').body[0]
- self.assertEqual(as_string(ast), 'raise_string(*args, **kwargs)')
-
- def test_module_as_string(self):
- """check as_string on a whole module prepared to be returned identically
- """
- data = open(join(DATA, 'module.py')).read()
- self.assertMultiLineEqual(as_string(MODULE), data)
- data = open(join(DATA, 'module2.py')).read()
- self.assertMultiLineEqual(as_string(MODULE2), data)
-
- def test_2_7_as_string(self):
- """check as_string for python syntax >= 2.7"""
- if sys.version_info < (2, 7):
- self.skipTest("test python >= 2.7 specific")
- code = '''one_two = {1, 2}
-b = {v: k for (k, v) in enumerate('string')}
-cdd = {k for k in b}\n\n'''
+ code = """from . import here
+from .. import door
+from .store import bread
+from ..cave import wine\n\n"""
ast = abuilder.string_build(code)
- self.assertMultiLineEqual(as_string(ast), code)
-
- def test_3k_as_string(self):
- """check as_string for python 3k syntax"""
- if sys.version_info < (3, 0):
- self.skipTest("test python 3k specific")
- code = '''print()
-
-def function(var):
- nonlocal counter
- try:
- hello
- except NameError as nexc:
- (*hell, o) = b'hello'
- raise AttributeError from nexc
-\n'''
- # TODO : annotations and keywords for class definition are not yet implemented
- _todo = '''
-def function(var:int):
- nonlocal counter
-
-class Language(metaclass=Natural):
- """natural language"""
- '''
- ast = abuilder.string_build(code)
- self.assertEqual(as_string(ast), code)
-
+ self.assertMultiLineEqual(ast.as_string(), code)
class CmpNodeTC(testlib.TestCase):
def test_as_string(self):
@@ -306,6 +316,27 @@ class ConstNodeTC(testlib.TestCase):
self._test(u'a')
+class NameNodeTC(testlib.TestCase):
+ def test_assign_to_True(self):
+ """test that True and False assignements don't crash"""
+ code = """True = False
+def hello(False):
+ pass
+del True
+ """
+ if sys.version_info >= (3, 0):
+ self.assertRaises(SyntaxError,#might become ASTNGBuildingException
+ abuilder.string_build, code)
+ else:
+ ast = abuilder.string_build(code)
+ ass_true = ast['True']
+ self.assertIsInstance(ass_true, nodes.AssName)
+ self.assertEqual(ass_true.name, "True")
+ del_true = ast.body[2].targets[0]
+ self.assertIsInstance(del_true, nodes.DelName)
+ self.assertEqual(del_true.name, "True")
+
+
class ArgumentsNodeTC(testlib.TestCase):
def test_linenumbering(self):
ast = abuilder.string_build('''
@@ -314,12 +345,16 @@ def func(a,
x = lambda x: None
''')
self.assertEqual(ast['func'].args.fromlineno, 2)
- self.assertEqual(ast['func'].args.tolineno, 3)
self.failIf(ast['func'].args.is_statement)
xlambda = ast['x'].infer().next()
self.assertEqual(xlambda.args.fromlineno, 4)
self.assertEqual(xlambda.args.tolineno, 4)
self.failIf(xlambda.args.is_statement)
+ if sys.version_info < (3, 0):
+ self.assertEqual(ast['func'].args.tolineno, 3)
+ else:
+ self.skipTest('FIXME http://bugs.python.org/issue10445 '
+ '(no line number on function args)')
class SliceNodeTC(testlib.TestCase):
diff --git a/test/unittest_regrtest.py b/test/unittest_regrtest.py
index 3a169333..dd6d3b66 100644
--- a/test/unittest_regrtest.py
+++ b/test/unittest_regrtest.py
@@ -21,17 +21,16 @@
from logilab.common.testlib import unittest_main, TestCase
from logilab.astng import ResolveError, MANAGER, Instance, nodes, YES, InferenceError
-from logilab.astng.builder import ASTNGBuilder, build_module
+from logilab.astng.builder import ASTNGBuilder
+from logilab.astng.raw_building import build_module
from logilab.astng.manager import ASTNGManager
import sys
from os.path import join, abspath, dirname
class NonRegressionTC(TestCase):
-
def setUp(self):
-
sys.path.insert(0, join(dirname(abspath(__file__)), 'regrtest_data'))
def tearDown(self):
@@ -42,7 +41,7 @@ class NonRegressionTC(TestCase):
# avoid caching into the ASTNGManager borg since we get problems
# with other tests :
manager.__dict__ = {}
- manager._cache = {}
+ manager.astng_cache = {}
manager._mod_file_cache = {}
return manager
@@ -61,7 +60,7 @@ class NonRegressionTC(TestCase):
def test_package_sidepackage(self):
manager = self.brainless_manager()
- assert 'package.sidepackage' not in MANAGER._cache
+ assert 'package.sidepackage' not in MANAGER.astng_cache
package = manager.astng_from_module_name('absimp')
self.assertIsInstance(package, nodes.Module)
self.assertTrue(package.package)
diff --git a/test/unittest_scoped_nodes.py b/test/unittest_scoped_nodes.py
index f1502ecc..382a0f57 100644
--- a/test/unittest_scoped_nodes.py
+++ b/test/unittest_scoped_nodes.py
@@ -112,6 +112,26 @@ del appli
self.assertEqual(len(astng.getattr('appli')), 2,
astng.getattr('appli'))
+ def test_relative_to_absolute_name(self):
+ mod = nodes.Module('very.multi.module', 'doc')
+ # package
+ mod.package = True
+ modname = mod.relative_to_absolute_name('utils', 1)
+ self.assertEqual(modname, 'very.multi.module.utils')
+ modname = mod.relative_to_absolute_name('utils', 2)
+ self.assertEqual(modname, 'very.multi.utils')
+ modname = mod.relative_to_absolute_name('utils', 0)
+ self.assertEqual(modname, 'very.multi.module.utils')
+ # non package
+ mod.package = False
+ modname = mod.relative_to_absolute_name('utils', 0)
+ self.assertEqual(modname, 'very.multi.utils')
+ modname = mod.relative_to_absolute_name('utils', 1)
+ self.assertEqual(modname, 'very.multi.utils')
+ modname = mod.relative_to_absolute_name('utils', 2)
+ self.assertEqual(modname, 'very.utils')
+
+
class FunctionNodeTC(TestCase):
@@ -262,6 +282,24 @@ a = func()
self.assertIsInstance(func_vals[0], nodes.Const)
self.assertEqual(func_vals[0].value, None)
+ def test_func_instance_attr(self):
+ """test instance attributes for functions"""
+ data= """
+def test():
+ print(test.bar)
+
+test.bar = 1
+test()
+ """
+ astng = abuilder.string_build(data, 'mod', __file__)
+ func = astng.body[2].value.func.infered()[0]
+ self.assertIsInstance(func, nodes.Function)
+ self.assertEqual(func.name, 'test')
+ one = func.getattr('bar')[0].infered()[0]
+ self.assertIsInstance(one, nodes.Const)
+ self.assertEqual(one.value, 1)
+
+
class ClassNodeTC(TestCase):
def test_dict_interface(self):
diff --git a/utils.py b/utils.py
index d7a174c7..745c975a 100644
--- a/utils.py
+++ b/utils.py
@@ -36,196 +36,7 @@ extract information from it
__docformat__ = "restructuredtext en"
-from logilab.astng._exceptions import IgnoreChild, ASTNGBuildingException
-
-class ASTVisitor(object):
- """Abstract Base Class for Python AST Visitors.
-
- Visitors inheriting from ASTVisitors could visit
- compiler.ast, _ast or astng trees.
-
- Not all methods will have to be implemented;
- so some methods are just empty interfaces for catching
- cases where we don't want to do anything on the
- concerned node.
- """
-
- def visit_arguments(self, node):
- """dummy method for visiting an Arguments node"""
-
- def visit_assattr(self, node):
- """dummy method for visiting an AssAttr node"""
-
- def visit_assert(self, node):
- """dummy method for visiting an Assert node"""
-
- def visit_assign(self, node):
- """dummy method for visiting an Assign node"""
-
- def visit_assname(self, node):
- """dummy method for visiting an AssName node"""
-
- def visit_augassign(self, node):
- """dummy method for visiting an AugAssign node"""
-
- def visit_backquote(self, node):
- """dummy method for visiting an Backquote node"""
-
- def visit_binop(self, node):
- """dummy method for visiting an BinOp node"""
-
- def visit_boolop(self, node):
- """dummy method for visiting an BoolOp node"""
-
- def visit_break(self, node):
- """dummy method for visiting an Break node"""
-
- def visit_callfunc(self, node):
- """dummy method for visiting an CallFunc node"""
-
- def visit_class(self, node):
- """dummy method for visiting an Class node"""
-
- def visit_compare(self, node):
- """dummy method for visiting an Compare node"""
-
- def visit_comprehension(self, node):
- """dummy method for visiting an Comprehension node"""
-
- def visit_const(self, node):
- """dummy method for visiting an Const node"""
-
- def visit_continue(self, node):
- """dummy method for visiting an Continue node"""
-
- def visit_decorators(self, node):
- """dummy method for visiting an Decorators node"""
-
- def visit_delattr(self, node):
- """dummy method for visiting an DelAttr node"""
-
- def visit_delete(self, node):
- """dummy method for visiting an Delete node"""
-
- def visit_delname(self, node):
- """dummy method for visiting an DelName node"""
-
- def visit_dict(self, node):
- """dummy method for visiting an Dict node"""
-
- def visit_dictcomp(self, node):
- """dummy method for visiting an DictComp node"""
-
- def visit_discard(self, node):
- """dummy method for visiting an Discard node"""
-
- def visit_emptynode(self, node):
- """dummy method for visiting an EmptyNode node"""
-
- def visit_excepthandler(self, node):
- """dummy method for visiting an ExceptHandler node"""
-
- def visit_ellipsis(self, node):
- """dummy method for visiting an Ellipsis node"""
-
- def visit_empty(self, node):
- """dummy method for visiting an Empty node"""
-
- def visit_exec(self, node):
- """dummy method for visiting an Exec node"""
-
- def visit_extslice(self, node):
- """dummy method for visiting an ExtSlice node"""
-
- def visit_for(self, node):
- """dummy method for visiting an For node"""
-
- def visit_from(self, node):
- """dummy method for visiting an From node"""
-
- def visit_function(self, node):
- """dummy method for visiting an Function node"""
-
- def visit_genexpr(self, node):
- """dummy method for visiting an ListComp node"""
-
- def visit_getattr(self, node):
- """dummy method for visiting an Getattr node"""
-
- def visit_global(self, node):
- """dummy method for visiting an Global node"""
-
- def visit_if(self, node):
- """dummy method for visiting an If node"""
-
- def visit_ifexp(self, node):
- """dummy method for visiting an IfExp node"""
-
- def visit_import(self, node):
- """dummy method for visiting an Import node"""
-
- def visit_index(self, node):
- """dummy method for visiting an Index node"""
-
- def visit_keyword(self, node):
- """dummy method for visiting an Keyword node"""
-
- def visit_lambda(self, node):
- """dummy method for visiting an Lambda node"""
-
- def visit_list(self, node):
- """dummy method for visiting an List node"""
-
- def visit_listcomp(self, node):
- """dummy method for visiting an ListComp node"""
-
- def visit_module(self, node):
- """dummy method for visiting an Module node"""
-
- def visit_name(self, node):
- """dummy method for visiting an Name node"""
-
- def visit_pass(self, node):
- """dummy method for visiting an Pass node"""
-
- def visit_print(self, node):
- """dummy method for visiting an Print node"""
-
- def visit_raise(self, node):
- """dummy method for visiting an Raise node"""
-
- def visit_return(self, node):
- """dummy method for visiting an Return node"""
-
- def visit_setcomp(self, node):
- """dummy method for visiting an SetComp node"""
-
- def visit_slice(self, node):
- """dummy method for visiting an Slice node"""
-
- def visit_subscript(self, node):
- """dummy method for visiting an Subscript node"""
-
- def visit_tryexcept(self, node):
- """dummy method for visiting an TryExcept node"""
-
- def visit_tryfinally(self, node):
- """dummy method for visiting an TryFinally node"""
-
- def visit_tuple(self, node):
- """dummy method for visiting an Tuple node"""
-
- def visit_unaryop(self, node):
- """dummy method for visiting an UnaryOp node"""
-
- def visit_while(self, node):
- """dummy method for visiting an While node"""
-
- def visit_with(self, node):
- """dummy method for visiting an With node"""
-
- def visit_yield(self, node):
- """dummy method for visiting an Yield node"""
+from logilab.astng.exceptions import ASTNGBuildingException
class ASTWalker:
@@ -249,19 +60,11 @@ class ASTWalker:
if node in _done:
raise AssertionError((id(node), node, node.parent))
_done.add(node)
- try:
- self.visit(node)
- except IgnoreChild:
- pass
- else:
- try:
- for child_node in node.get_children():
- self.handler.set_context(node, child_node)
- assert child_node is not node
- self.walk(child_node, _done)
- except AttributeError:
- print node.__class__, id(node.__class__)
- raise
+ self.visit(node)
+ for child_node in node.get_children():
+ self.handler.set_context(node, child_node)
+ assert child_node is not node
+ self.walk(child_node, _done)
self.leave(node)
assert node.parent is not node
@@ -306,16 +109,11 @@ class LocalsVisitor(ASTWalker):
return
self._visited[node] = 1 # FIXME: use set ?
methods = self.get_callbacks(node)
- recurse = 1
if methods[0] is not None:
- try:
- methods[0](node)
- except IgnoreChild:
- recurse = 0
- if recurse:
- if 'locals' in node.__dict__: # skip Instance and other proxy
- for name, local_node in node.items():
- self.visit(local_node)
+ methods[0](node)
+ if 'locals' in node.__dict__: # skip Instance and other proxy
+ for name, local_node in node.items():
+ self.visit(local_node)
if methods[1] is not None:
return methods[1](node)
@@ -343,8 +141,10 @@ def _check_children(node):
_check_children(child)
-# XXX This class don't support 'compiler' for now.
-# If we want it, there is a "_native_repr_tree" method in _nodes_compiler.py
+from _ast import PyCF_ONLY_AST
+def parse(string):
+ return compile(string, "<string>", 'exec', PyCF_ONLY_AST)
+
class TreeTester(object):
'''A helper class to see _ast tree and compare with astng tree
@@ -380,9 +180,6 @@ class TreeTester(object):
def build_ast(self):
"""build the _ast tree from the source code"""
- from _ast import PyCF_ONLY_AST
- def parse(string):
- return compile(string, "<string>", 'exec', PyCF_ONLY_AST)
self._ast_node = parse(self.sourcecode)
def native_tree_repr(self, node=None, indent=''):
@@ -444,7 +241,7 @@ class TreeTester(object):
"""build astng tree from the _ast tree
"""
from logilab.astng.builder import ASTNGBuilder
- tree = ASTNGBuilder().ast_build(self._ast_node)
+ tree = ASTNGBuilder().string_build(self.sourcecode)
return tree
def astng_tree_repr(self, ids=False):
@@ -453,5 +250,5 @@ class TreeTester(object):
return mod.repr_tree(ids)
-__all__ = ('LocalsVisitor', 'ASTWalker', 'ASTVisitor',)
+__all__ = ('LocalsVisitor', 'ASTWalker',)