From 0c7327e7599c69843ffdb884d313863ec36cb6dd Mon Sep 17 00:00:00 2001 From: Torsten Marek Date: Sat, 8 Nov 2014 23:47:14 +0100 Subject: Move all astroid modules into a its own directory, which is now the package. python setup.py develop now works. --- MANIFEST.in | 10 +- __init__.py | 121 -- __pkginfo__.py | 42 - as_string.py | 499 ------- astroid/__init__.py | 121 ++ astroid/__pkginfo__.py | 42 + astroid/as_string.py | 499 +++++++ astroid/bases.py | 665 ++++++++++ astroid/brain/py2gi.py | 169 +++ astroid/brain/py2mechanize.py | 20 + astroid/brain/py2pytest.py | 50 + astroid/brain/py2qt4.py | 25 + astroid/brain/py2stdlib.py | 350 +++++ astroid/builder.py | 237 ++++ astroid/exceptions.py | 51 + astroid/inference.py | 395 ++++++ astroid/inspector.py | 273 ++++ astroid/manager.py | 348 +++++ astroid/mixins.py | 124 ++ astroid/modutils.py | 638 +++++++++ astroid/node_classes.py | 965 ++++++++++++++ astroid/nodes.py | 73 ++ astroid/protocols.py | 361 +++++ astroid/raw_building.py | 364 ++++++ astroid/rebuilder.py | 905 +++++++++++++ astroid/scoped_nodes.py | 1304 ++++++++++++++++++ astroid/test_utils.py | 213 +++ astroid/tests/data/MyPyPa-0.1.0-py2.5.egg | Bin 0 -> 1222 bytes astroid/tests/data/MyPyPa-0.1.0-py2.5.zip | Bin 0 -> 1222 bytes astroid/tests/data/SSL1/Connection1.py | 14 + astroid/tests/data/SSL1/__init__.py | 1 + astroid/tests/data/__init__.py | 1 + astroid/tests/data/absimport.py | 3 + astroid/tests/data/all.py | 9 + astroid/tests/data/appl/__init__.py | 3 + astroid/tests/data/appl/myConnection.py | 11 + astroid/tests/data/email.py | 1 + astroid/tests/data/find_test/__init__.py | 0 astroid/tests/data/find_test/module.py | 0 astroid/tests/data/find_test/module2.py | 0 astroid/tests/data/find_test/noendingnewline.py | 0 astroid/tests/data/find_test/nonregr.py | 0 astroid/tests/data/format.py | 34 + astroid/tests/data/lmfp/__init__.py | 2 + astroid/tests/data/lmfp/foo.py | 6 + astroid/tests/data/module.py | 89 ++ astroid/tests/data/module1abs/__init__.py | 4 + astroid/tests/data/module1abs/core.py | 1 + astroid/tests/data/module2.py | 143 ++ astroid/tests/data/noendingnewline.py | 36 + astroid/tests/data/nonregr.py | 57 + astroid/tests/data/notall.py | 8 + astroid/tests/data2/__init__.py | 0 astroid/tests/data2/clientmodule_test.py | 32 + astroid/tests/data2/suppliermodule_test.py | 13 + astroid/tests/data_py3/MyPyPa-0.1.0-py2.5.egg | Bin 0 -> 1222 bytes astroid/tests/data_py3/MyPyPa-0.1.0-py2.5.zip | Bin 0 -> 1222 bytes astroid/tests/data_py3/SSL1/Connection1.py | 14 + astroid/tests/data_py3/SSL1/__init__.py | 1 + astroid/tests/data_py3/__init__.py | 1 + astroid/tests/data_py3/absimport.py | 3 + astroid/tests/data_py3/all.py | 9 + astroid/tests/data_py3/appl/__init__.py | 3 + astroid/tests/data_py3/appl/myConnection.py | 11 + astroid/tests/data_py3/email.py | 1 + astroid/tests/data_py3/find_test/__init__.py | 0 astroid/tests/data_py3/find_test/module.py | 0 astroid/tests/data_py3/find_test/module2.py | 0 .../tests/data_py3/find_test/noendingnewline.py | 0 astroid/tests/data_py3/find_test/nonregr.py | 0 astroid/tests/data_py3/format.py | 34 + astroid/tests/data_py3/lmfp/__init__.py | 2 + astroid/tests/data_py3/lmfp/foo.py | 6 + astroid/tests/data_py3/module.py | 89 ++ astroid/tests/data_py3/module1abs/__init__.py | 4 + astroid/tests/data_py3/module1abs/core.py | 1 + astroid/tests/data_py3/module2.py | 143 ++ astroid/tests/data_py3/noendingnewline.py | 36 + astroid/tests/data_py3/nonregr.py | 57 + astroid/tests/data_py3/notall.py | 8 + astroid/tests/regrtest_data/absimp/__init__.py | 5 + .../regrtest_data/absimp/sidepackage/__init__.py | 3 + astroid/tests/regrtest_data/absimp/string.py | 3 + astroid/tests/regrtest_data/descriptor_crash.py | 11 + astroid/tests/regrtest_data/package/__init__.py | 4 + astroid/tests/regrtest_data/package/absimport.py | 6 + astroid/tests/regrtest_data/package/hello.py | 2 + .../package/import_package_subpackage_module.py | 49 + .../regrtest_data/package/subpackage/__init__.py | 1 + .../regrtest_data/package/subpackage/module.py | 1 + astroid/tests/regrtest_data/recursion.py | 3 + .../regrtest_data/unicode_package/__init__.py | 1 + .../regrtest_data/unicode_package/core/__init__.py | 0 astroid/tests/unittest_brain.py | 99 ++ astroid/tests/unittest_builder.py | 781 +++++++++++ astroid/tests/unittest_inference.py | 1383 ++++++++++++++++++++ astroid/tests/unittest_inspector.py | 93 ++ astroid/tests/unittest_lookup.py | 369 ++++++ astroid/tests/unittest_manager.py | 231 ++++ astroid/tests/unittest_modutils.py | 280 ++++ astroid/tests/unittest_nodes.py | 441 +++++++ astroid/tests/unittest_python3.py | 220 ++++ astroid/tests/unittest_raw_building.py | 48 + astroid/tests/unittest_regrtest.py | 224 ++++ astroid/tests/unittest_scoped_nodes.py | 1008 ++++++++++++++ astroid/tests/unittest_utils.py | 102 ++ astroid/utils.py | 239 ++++ bases.py | 665 ---------- brain/py2gi.py | 169 --- brain/py2mechanize.py | 20 - brain/py2pytest.py | 50 - brain/py2qt4.py | 25 - brain/py2stdlib.py | 350 ----- builder.py | 237 ---- exceptions.py | 51 - inference.py | 395 ------ inspector.py | 273 ---- manager.py | 348 ----- mixins.py | 124 -- modutils.py | 638 --------- node_classes.py | 965 -------------- nodes.py | 73 -- protocols.py | 361 ----- raw_building.py | 364 ------ rebuilder.py | 905 ------------- scoped_nodes.py | 1304 ------------------ setup.py | 56 +- test/data/MyPyPa-0.1.0-py2.5.egg | Bin 1222 -> 0 bytes test/data/MyPyPa-0.1.0-py2.5.zip | Bin 1222 -> 0 bytes test/data/SSL1/Connection1.py | 14 - test/data/SSL1/__init__.py | 1 - test/data/__init__.py | 1 - test/data/absimport.py | 3 - test/data/all.py | 9 - test/data/appl/__init__.py | 3 - test/data/appl/myConnection.py | 11 - test/data/email.py | 1 - test/data/find_test/__init__.py | 0 test/data/find_test/module.py | 0 test/data/find_test/module2.py | 0 test/data/find_test/noendingnewline.py | 0 test/data/find_test/nonregr.py | 0 test/data/format.py | 34 - test/data/lmfp/__init__.py | 2 - test/data/lmfp/foo.py | 6 - test/data/module.py | 89 -- test/data/module1abs/__init__.py | 4 - test/data/module1abs/core.py | 1 - test/data/module2.py | 143 -- test/data/noendingnewline.py | 36 - test/data/nonregr.py | 57 - test/data/notall.py | 8 - test/data2/__init__.py | 0 test/data2/clientmodule_test.py | 32 - test/data2/suppliermodule_test.py | 13 - test/data_py3/MyPyPa-0.1.0-py2.5.egg | Bin 1222 -> 0 bytes test/data_py3/MyPyPa-0.1.0-py2.5.zip | Bin 1222 -> 0 bytes test/data_py3/SSL1/Connection1.py | 14 - test/data_py3/SSL1/__init__.py | 1 - test/data_py3/__init__.py | 1 - test/data_py3/absimport.py | 3 - test/data_py3/all.py | 9 - test/data_py3/appl/__init__.py | 3 - test/data_py3/appl/myConnection.py | 11 - test/data_py3/email.py | 1 - test/data_py3/find_test/__init__.py | 0 test/data_py3/find_test/module.py | 0 test/data_py3/find_test/module2.py | 0 test/data_py3/find_test/noendingnewline.py | 0 test/data_py3/find_test/nonregr.py | 0 test/data_py3/format.py | 34 - test/data_py3/lmfp/__init__.py | 2 - test/data_py3/lmfp/foo.py | 6 - test/data_py3/module.py | 89 -- test/data_py3/module1abs/__init__.py | 4 - test/data_py3/module1abs/core.py | 1 - test/data_py3/module2.py | 143 -- test/data_py3/noendingnewline.py | 36 - test/data_py3/nonregr.py | 57 - test/data_py3/notall.py | 8 - test/regrtest_data/absimp/__init__.py | 5 - test/regrtest_data/absimp/sidepackage/__init__.py | 3 - test/regrtest_data/absimp/string.py | 3 - test/regrtest_data/descriptor_crash.py | 11 - test/regrtest_data/package/__init__.py | 4 - test/regrtest_data/package/absimport.py | 6 - test/regrtest_data/package/hello.py | 2 - .../package/import_package_subpackage_module.py | 49 - test/regrtest_data/package/subpackage/__init__.py | 1 - test/regrtest_data/package/subpackage/module.py | 1 - test/regrtest_data/recursion.py | 3 - test/regrtest_data/unicode_package/__init__.py | 1 - .../regrtest_data/unicode_package/core/__init__.py | 0 test/unittest_brain.py | 99 -- test/unittest_builder.py | 781 ----------- test/unittest_inference.py | 1383 -------------------- test/unittest_inspector.py | 93 -- test/unittest_lookup.py | 369 ------ test/unittest_manager.py | 231 ---- test/unittest_modutils.py | 280 ---- test/unittest_nodes.py | 441 ------- test/unittest_python3.py | 220 ---- test/unittest_raw_building.py | 48 - test/unittest_regrtest.py | 224 ---- test/unittest_scoped_nodes.py | 1008 -------------- test/unittest_utils.py | 102 -- test_utils.py | 213 --- tox.ini | 2 +- utils.py | 239 ---- 209 files changed, 14706 insertions(+), 14742 deletions(-) delete mode 100644 __init__.py delete mode 100644 __pkginfo__.py delete mode 100644 as_string.py create mode 100644 astroid/__init__.py create mode 100644 astroid/__pkginfo__.py create mode 100644 astroid/as_string.py create mode 100644 astroid/bases.py create mode 100644 astroid/brain/py2gi.py create mode 100644 astroid/brain/py2mechanize.py create mode 100644 astroid/brain/py2pytest.py create mode 100644 astroid/brain/py2qt4.py create mode 100644 astroid/brain/py2stdlib.py create mode 100644 astroid/builder.py create mode 100644 astroid/exceptions.py create mode 100644 astroid/inference.py create mode 100644 astroid/inspector.py create mode 100644 astroid/manager.py create mode 100644 astroid/mixins.py create mode 100644 astroid/modutils.py create mode 100644 astroid/node_classes.py create mode 100644 astroid/nodes.py create mode 100644 astroid/protocols.py create mode 100644 astroid/raw_building.py create mode 100644 astroid/rebuilder.py create mode 100644 astroid/scoped_nodes.py create mode 100644 astroid/test_utils.py create mode 100644 astroid/tests/data/MyPyPa-0.1.0-py2.5.egg create mode 100644 astroid/tests/data/MyPyPa-0.1.0-py2.5.zip create mode 100644 astroid/tests/data/SSL1/Connection1.py create mode 100644 astroid/tests/data/SSL1/__init__.py create mode 100644 astroid/tests/data/__init__.py create mode 100644 astroid/tests/data/absimport.py create mode 100644 astroid/tests/data/all.py create mode 100644 astroid/tests/data/appl/__init__.py create mode 100644 astroid/tests/data/appl/myConnection.py create mode 100644 astroid/tests/data/email.py create mode 100644 astroid/tests/data/find_test/__init__.py create mode 100644 astroid/tests/data/find_test/module.py create mode 100644 astroid/tests/data/find_test/module2.py create mode 100644 astroid/tests/data/find_test/noendingnewline.py create mode 100644 astroid/tests/data/find_test/nonregr.py create mode 100644 astroid/tests/data/format.py create mode 100644 astroid/tests/data/lmfp/__init__.py create mode 100644 astroid/tests/data/lmfp/foo.py create mode 100644 astroid/tests/data/module.py create mode 100644 astroid/tests/data/module1abs/__init__.py create mode 100644 astroid/tests/data/module1abs/core.py create mode 100644 astroid/tests/data/module2.py create mode 100644 astroid/tests/data/noendingnewline.py create mode 100644 astroid/tests/data/nonregr.py create mode 100644 astroid/tests/data/notall.py create mode 100644 astroid/tests/data2/__init__.py create mode 100644 astroid/tests/data2/clientmodule_test.py create mode 100644 astroid/tests/data2/suppliermodule_test.py create mode 100644 astroid/tests/data_py3/MyPyPa-0.1.0-py2.5.egg create mode 100644 astroid/tests/data_py3/MyPyPa-0.1.0-py2.5.zip create mode 100644 astroid/tests/data_py3/SSL1/Connection1.py create mode 100644 astroid/tests/data_py3/SSL1/__init__.py create mode 100644 astroid/tests/data_py3/__init__.py create mode 100644 astroid/tests/data_py3/absimport.py create mode 100644 astroid/tests/data_py3/all.py create mode 100644 astroid/tests/data_py3/appl/__init__.py create mode 100644 astroid/tests/data_py3/appl/myConnection.py create mode 100644 astroid/tests/data_py3/email.py create mode 100644 astroid/tests/data_py3/find_test/__init__.py create mode 100644 astroid/tests/data_py3/find_test/module.py create mode 100644 astroid/tests/data_py3/find_test/module2.py create mode 100644 astroid/tests/data_py3/find_test/noendingnewline.py create mode 100644 astroid/tests/data_py3/find_test/nonregr.py create mode 100644 astroid/tests/data_py3/format.py create mode 100644 astroid/tests/data_py3/lmfp/__init__.py create mode 100644 astroid/tests/data_py3/lmfp/foo.py create mode 100644 astroid/tests/data_py3/module.py create mode 100644 astroid/tests/data_py3/module1abs/__init__.py create mode 100644 astroid/tests/data_py3/module1abs/core.py create mode 100644 astroid/tests/data_py3/module2.py create mode 100644 astroid/tests/data_py3/noendingnewline.py create mode 100644 astroid/tests/data_py3/nonregr.py create mode 100644 astroid/tests/data_py3/notall.py create mode 100644 astroid/tests/regrtest_data/absimp/__init__.py create mode 100644 astroid/tests/regrtest_data/absimp/sidepackage/__init__.py create mode 100644 astroid/tests/regrtest_data/absimp/string.py create mode 100644 astroid/tests/regrtest_data/descriptor_crash.py create mode 100644 astroid/tests/regrtest_data/package/__init__.py create mode 100644 astroid/tests/regrtest_data/package/absimport.py create mode 100644 astroid/tests/regrtest_data/package/hello.py create mode 100644 astroid/tests/regrtest_data/package/import_package_subpackage_module.py create mode 100644 astroid/tests/regrtest_data/package/subpackage/__init__.py create mode 100644 astroid/tests/regrtest_data/package/subpackage/module.py create mode 100644 astroid/tests/regrtest_data/recursion.py create mode 100644 astroid/tests/regrtest_data/unicode_package/__init__.py create mode 100644 astroid/tests/regrtest_data/unicode_package/core/__init__.py create mode 100644 astroid/tests/unittest_brain.py create mode 100644 astroid/tests/unittest_builder.py create mode 100644 astroid/tests/unittest_inference.py create mode 100644 astroid/tests/unittest_inspector.py create mode 100644 astroid/tests/unittest_lookup.py create mode 100644 astroid/tests/unittest_manager.py create mode 100644 astroid/tests/unittest_modutils.py create mode 100644 astroid/tests/unittest_nodes.py create mode 100644 astroid/tests/unittest_python3.py create mode 100644 astroid/tests/unittest_raw_building.py create mode 100644 astroid/tests/unittest_regrtest.py create mode 100644 astroid/tests/unittest_scoped_nodes.py create mode 100644 astroid/tests/unittest_utils.py create mode 100644 astroid/utils.py delete mode 100644 bases.py delete mode 100644 brain/py2gi.py delete mode 100644 brain/py2mechanize.py delete mode 100644 brain/py2pytest.py delete mode 100644 brain/py2qt4.py delete mode 100644 brain/py2stdlib.py delete mode 100644 builder.py delete mode 100644 exceptions.py delete mode 100644 inference.py delete mode 100644 inspector.py delete mode 100644 manager.py delete mode 100644 mixins.py delete mode 100644 modutils.py delete mode 100644 node_classes.py delete mode 100644 nodes.py delete mode 100644 protocols.py delete mode 100644 raw_building.py delete mode 100644 rebuilder.py delete mode 100644 scoped_nodes.py delete mode 100644 test/data/MyPyPa-0.1.0-py2.5.egg delete mode 100644 test/data/MyPyPa-0.1.0-py2.5.zip delete mode 100644 test/data/SSL1/Connection1.py delete mode 100644 test/data/SSL1/__init__.py delete mode 100644 test/data/__init__.py delete mode 100644 test/data/absimport.py delete mode 100644 test/data/all.py delete mode 100644 test/data/appl/__init__.py delete mode 100644 test/data/appl/myConnection.py delete mode 100644 test/data/email.py delete mode 100644 test/data/find_test/__init__.py delete mode 100644 test/data/find_test/module.py delete mode 100644 test/data/find_test/module2.py delete mode 100644 test/data/find_test/noendingnewline.py delete mode 100644 test/data/find_test/nonregr.py delete mode 100644 test/data/format.py delete mode 100644 test/data/lmfp/__init__.py delete mode 100644 test/data/lmfp/foo.py delete mode 100644 test/data/module.py delete mode 100644 test/data/module1abs/__init__.py delete mode 100644 test/data/module1abs/core.py delete mode 100644 test/data/module2.py delete mode 100644 test/data/noendingnewline.py delete mode 100644 test/data/nonregr.py delete mode 100644 test/data/notall.py delete mode 100644 test/data2/__init__.py delete mode 100644 test/data2/clientmodule_test.py delete mode 100644 test/data2/suppliermodule_test.py delete mode 100644 test/data_py3/MyPyPa-0.1.0-py2.5.egg delete mode 100644 test/data_py3/MyPyPa-0.1.0-py2.5.zip delete mode 100644 test/data_py3/SSL1/Connection1.py delete mode 100644 test/data_py3/SSL1/__init__.py delete mode 100644 test/data_py3/__init__.py delete mode 100644 test/data_py3/absimport.py delete mode 100644 test/data_py3/all.py delete mode 100644 test/data_py3/appl/__init__.py delete mode 100644 test/data_py3/appl/myConnection.py delete mode 100644 test/data_py3/email.py delete mode 100644 test/data_py3/find_test/__init__.py delete mode 100644 test/data_py3/find_test/module.py delete mode 100644 test/data_py3/find_test/module2.py delete mode 100644 test/data_py3/find_test/noendingnewline.py delete mode 100644 test/data_py3/find_test/nonregr.py delete mode 100644 test/data_py3/format.py delete mode 100644 test/data_py3/lmfp/__init__.py delete mode 100644 test/data_py3/lmfp/foo.py delete mode 100644 test/data_py3/module.py delete mode 100644 test/data_py3/module1abs/__init__.py delete mode 100644 test/data_py3/module1abs/core.py delete mode 100644 test/data_py3/module2.py delete mode 100644 test/data_py3/noendingnewline.py delete mode 100644 test/data_py3/nonregr.py delete mode 100644 test/data_py3/notall.py delete mode 100644 test/regrtest_data/absimp/__init__.py delete mode 100644 test/regrtest_data/absimp/sidepackage/__init__.py delete mode 100644 test/regrtest_data/absimp/string.py delete mode 100644 test/regrtest_data/descriptor_crash.py delete mode 100644 test/regrtest_data/package/__init__.py delete mode 100644 test/regrtest_data/package/absimport.py delete mode 100644 test/regrtest_data/package/hello.py delete mode 100644 test/regrtest_data/package/import_package_subpackage_module.py delete mode 100644 test/regrtest_data/package/subpackage/__init__.py delete mode 100644 test/regrtest_data/package/subpackage/module.py delete mode 100644 test/regrtest_data/recursion.py delete mode 100644 test/regrtest_data/unicode_package/__init__.py delete mode 100644 test/regrtest_data/unicode_package/core/__init__.py delete mode 100644 test/unittest_brain.py delete mode 100644 test/unittest_builder.py delete mode 100644 test/unittest_inference.py delete mode 100644 test/unittest_inspector.py delete mode 100644 test/unittest_lookup.py delete mode 100644 test/unittest_manager.py delete mode 100644 test/unittest_modutils.py delete mode 100644 test/unittest_nodes.py delete mode 100644 test/unittest_python3.py delete mode 100644 test/unittest_raw_building.py delete mode 100644 test/unittest_regrtest.py delete mode 100644 test/unittest_scoped_nodes.py delete mode 100644 test/unittest_utils.py delete mode 100644 test_utils.py delete mode 100644 utils.py diff --git a/MANIFEST.in b/MANIFEST.in index e9b7008..6b77986 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,8 +2,8 @@ include ChangeLog include README include COPYING include COPYING.LESSER -recursive-include test/data *.py *.zip *.egg -recursive-include test/data_py3 *.py *.zip *.egg -recursive-include test/data2 *.py -recursive-include test/regrtest_data *.py -recursive-include brain/ *.py +recursive-include astroid/tests/data *.py *.zip *.egg +recursive-include astroid/tests/data_py3 *.py *.zip *.egg +recursive-include astroid/tests/data2 *.py +recursive-include astroid/tests/regrtest_data *.py +recursive-include astroid/brain *.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index 6df06b1..0000000 --- a/__init__.py +++ /dev/null @@ -1,121 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""Python Abstract Syntax Tree New Generation - -The aim of this module is to provide a common base representation of -python source code for projects such as pychecker, pyreverse, -pylint... Well, actually the development of this library is essentially -governed by pylint's needs. - -It extends class defined in the python's _ast module with some -additional methods and attributes. Instance attributes are added by a -builder object, which can either generate extended ast (let's call -them astroid ;) by visiting an existent ast tree or by inspecting living -object. Methods are added by monkey patching ast classes. - -Main modules are: - -* nodes and scoped_nodes for more information about methods and - attributes added to different node classes - -* the manager contains a high level object to get astroid trees from - source files and living objects. It maintains a cache of previously - constructed tree for quick access - -* builder contains the class responsible to build astroid trees -""" -__doctype__ = "restructuredtext en" - -import sys -import re -from operator import attrgetter - -# WARNING: internal imports order matters ! - -# make all exception classes accessible from astroid package -from astroid.exceptions import * - -# make all node classes accessible from astroid package -from astroid.nodes import * - -# trigger extra monkey-patching -from astroid import inference - -# more stuff available -from astroid import raw_building -from astroid.bases import YES, Instance, BoundMethod, UnboundMethod -from astroid.node_classes import are_exclusive, unpack_infer -from astroid.scoped_nodes import builtin_lookup - -# make a manager instance (borg) as well as Project and Package classes -# accessible from astroid package -from astroid.manager import AstroidManager, Project -MANAGER = AstroidManager() -del AstroidManager - -# transform utilities (filters and decorator) - -class AsStringRegexpPredicate(object): - """Class to be used as predicate that may be given to `register_transform` - - First argument is a regular expression that will be searched against the `as_string` - representation of the node onto which it's applied. - - If specified, the second argument is an `attrgetter` expression that will be - applied on the node first to get the actual node on which `as_string` should - be called. - - WARNING: This can be fairly slow, as it has to convert every AST node back - to Python code; you should consider examining the AST directly instead. - """ - def __init__(self, regexp, expression=None): - self.regexp = re.compile(regexp) - self.expression = expression - - def __call__(self, node): - if self.expression is not None: - node = attrgetter(self.expression)(node) - return self.regexp.search(node.as_string()) - -def inference_tip(infer_function): - """Given an instance specific inference function, return a function to be - given to MANAGER.register_transform to set this inference function. - - Typical usage - - .. sourcecode:: python - - MANAGER.register_transform(CallFunc, inference_tip(infer_named_tuple), - predicate) - """ - def transform(node, infer_function=infer_function): - node._explicit_inference = infer_function - return node - return transform - -# load brain plugins -from os import listdir -from os.path import join, dirname -BRAIN_MODULES_DIR = join(dirname(__file__), 'brain') -if BRAIN_MODULES_DIR not in sys.path: - # add it to the end of the list so user path take precedence - sys.path.append(BRAIN_MODULES_DIR) -# load modules in this directory -for module in listdir(BRAIN_MODULES_DIR): - if module.endswith('.py'): - __import__(module[:-3]) diff --git a/__pkginfo__.py b/__pkginfo__.py deleted file mode 100644 index a93173f..0000000 --- a/__pkginfo__.py +++ /dev/null @@ -1,42 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""astroid packaging information""" -distname = 'astroid' - -modname = 'astroid' - -numversion = (1, 2, 1) -version = '.'.join([str(num) for num in numversion]) - -install_requires = ['logilab-common >= 0.60.0', 'six'] - -license = 'LGPL' - -author = 'Logilab' -author_email = 'python-projects@lists.logilab.org' -mailinglist = "mailto://%s" % author_email -web = 'http://bitbucket.org/logilab/astroid' - -description = "rebuild a new abstract syntax tree from Python's ast" - -classifiers = ["Topic :: Software Development :: Libraries :: Python Modules", - "Topic :: Software Development :: Quality Assurance", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 3", - ] diff --git a/as_string.py b/as_string.py deleted file mode 100644 index f19713d..0000000 --- a/as_string.py +++ /dev/null @@ -1,499 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""This module renders Astroid nodes as string: - -* :func:`to_code` function return equivalent (hopefuly valid) python string - -* :func:`dump` function return an internal representation of nodes found - in the tree, useful for debugging or understanding the tree structure -""" - -import sys - -INDENT = ' ' # 4 spaces ; keep indentation variable - - -def dump(node, ids=False): - """print a nice astroid tree representation. - - :param ids: if true, we also print the ids (usefull for debugging) - """ - result = [] - _repr_tree(node, result, ids=ids) - return "\n".join(result) - -def _repr_tree(node, result, indent='', _done=None, ids=False): - """built a tree representation of a node as a list of lines""" - if _done is None: - _done = set() - if not hasattr(node, '_astroid_fields'): # not a astroid node - return - if node in _done: - result.append(indent + 'loop in tree: %s' % node) - return - _done.add(node) - node_str = str(node) - if ids: - node_str += ' . \t%x' % id(node) - result.append(indent + node_str) - indent += INDENT - for field in node._astroid_fields: - value = getattr(node, field) - if isinstance(value, (list, tuple)): - result.append(indent + field + " = [") - for child in value: - if isinstance(child, (list, tuple)): - # special case for Dict # FIXME - _repr_tree(child[0], result, indent, _done, ids) - _repr_tree(child[1], result, indent, _done, ids) - result.append(indent + ',') - else: - _repr_tree(child, result, indent, _done, ids) - result.append(indent + "]") - else: - result.append(indent + field + " = ") - _repr_tree(value, result, indent, _done, ids) - - -class AsStringVisitor(object): - """Visitor to render an Astroid node as a valid python code string""" - - def __call__(self, node): - """Makes this visitor behave as a simple function""" - return node.accept(self) - - def _stmt_list(self, stmts): - """return a list of nodes to string""" - stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr]) - return INDENT + stmts.replace('\n', '\n'+INDENT) - - - ## visit_ methods ########################################### - - def visit_arguments(self, node): - """return an astroid.Function node as string""" - return node.format_args() - - def visit_assattr(self, node): - """return an astroid.AssAttr node as string""" - return self.visit_getattr(node) - - def visit_assert(self, node): - """return an astroid.Assert node as string""" - if node.fail: - return 'assert %s, %s' % (node.test.accept(self), - node.fail.accept(self)) - return 'assert %s' % node.test.accept(self) - - def visit_assname(self, node): - """return an astroid.AssName node as string""" - return node.name - - def visit_assign(self, node): - """return an astroid.Assign node as string""" - lhs = ' = '.join([n.accept(self) for n in node.targets]) - return '%s = %s' % (lhs, node.value.accept(self)) - - def visit_augassign(self, node): - """return an astroid.AugAssign node as string""" - return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self)) - - def visit_backquote(self, node): - """return an astroid.Backquote node as string""" - return '`%s`' % node.value.accept(self) - - def visit_binop(self, node): - """return an astroid.BinOp node as string""" - return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self)) - - def visit_boolop(self, node): - """return an astroid.BoolOp node as string""" - return (' %s ' % node.op).join(['(%s)' % n.accept(self) - for n in node.values]) - - def visit_break(self, node): - """return an astroid.Break node as string""" - return 'break' - - def visit_callfunc(self, node): - """return an astroid.CallFunc node as string""" - expr_str = node.func.accept(self) - args = [arg.accept(self) for arg in node.args] - if node.starargs: - args.append('*' + node.starargs.accept(self)) - if node.kwargs: - args.append('**' + node.kwargs.accept(self)) - return '%s(%s)' % (expr_str, ', '.join(args)) - - def visit_class(self, node): - """return an astroid.Class node as string""" - decorate = node.decorators and node.decorators.accept(self) or '' - bases = ', '.join([n.accept(self) for n in node.bases]) - if sys.version_info[0] == 2: - bases = bases and '(%s)' % bases or '' - else: - metaclass = node.metaclass() - if metaclass: - if bases: - bases = '(%s, metaclass=%s)' % (bases, metaclass.name) - else: - bases = '(metaclass=%s)' % metaclass.name - else: - bases = bases and '(%s)' % bases or '' - docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' - return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs, - self._stmt_list(node.body)) - - def visit_compare(self, node): - """return an astroid.Compare node as string""" - rhs_str = ' '.join(['%s %s' % (op, expr.accept(self)) - for op, expr in node.ops]) - return '%s %s' % (node.left.accept(self), rhs_str) - - def visit_comprehension(self, node): - """return an astroid.Comprehension node as string""" - ifs = ''.join([' if %s' % n.accept(self) for n in node.ifs]) - return 'for %s in %s%s' % (node.target.accept(self), - node.iter.accept(self), ifs) - - def visit_const(self, node): - """return an astroid.Const node as string""" - return repr(node.value) - - def visit_continue(self, node): - """return an astroid.Continue node as string""" - return 'continue' - - def visit_delete(self, node): # XXX check if correct - """return an astroid.Delete node as string""" - return 'del %s' % ', '.join([child.accept(self) - for child in node.targets]) - - def visit_delattr(self, node): - """return an astroid.DelAttr node as string""" - return self.visit_getattr(node) - - def visit_delname(self, node): - """return an astroid.DelName node as string""" - return node.name - - def visit_decorators(self, node): - """return an astroid.Decorators node as string""" - return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes]) - - def visit_dict(self, node): - """return an astroid.Dict node as string""" - return '{%s}' % ', '.join(['%s: %s' % (key.accept(self), - value.accept(self)) - for key, value in node.items]) - - def visit_dictcomp(self, node): - """return an astroid.DictComp node as string""" - return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_discard(self, node): - """return an astroid.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: - excs = 'except %s, %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_ellipsis(self, node): - """return an astroid.Ellipsis node as string""" - return '...' - - def visit_empty(self, node): - """return an Empty node as string""" - return '' - - def visit_exec(self, node): - """return an astroid.Exec node as string""" - if node.locals: - return 'exec %s in %s, %s' % (node.expr.accept(self), - node.locals.accept(self), - node.globals.accept(self)) - if node.globals: - return 'exec %s in %s' % (node.expr.accept(self), - node.globals.accept(self)) - return 'exec %s' % node.expr.accept(self) - - def visit_extslice(self, node): - """return an astroid.ExtSlice node as string""" - return ','.join([dim.accept(self) for dim in node.dims]) - - def visit_for(self, node): - """return an astroid.For node as string""" - fors = 'for %s in %s:\n%s' % (node.target.accept(self), - node.iter.accept(self), - self._stmt_list(node.body)) - if node.orelse: - fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse)) - return fors - - def visit_from(self, node): - """return an astroid.From node as string""" - return 'from %s import %s' % ('.' * (node.level or 0) + node.modname, - _import_string(node.names)) - - def visit_function(self, node): - """return an astroid.Function node as string""" - decorate = node.decorators and node.decorators.accept(self) or '' - docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' - return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self), - docs, self._stmt_list(node.body)) - - def visit_genexpr(self, node): - """return an astroid.GenExpr node as string""" - return '(%s %s)' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_getattr(self, node): - """return an astroid.Getattr node as string""" - return '%s.%s' % (node.expr.accept(self), node.attrname) - - def visit_global(self, node): - """return an astroid.Global node as string""" - return 'global %s' % ', '.join(node.names) - - def visit_if(self, node): - """return an astroid.If node as string""" - ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))] - if node.orelse:# XXX use elif ??? - ifs.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(ifs) - - def visit_ifexp(self, node): - """return an astroid.IfExp node as string""" - return '%s if %s else %s' % (node.body.accept(self), - node.test.accept(self), - node.orelse.accept(self)) - - def visit_import(self, node): - """return an astroid.Import node as string""" - return 'import %s' % _import_string(node.names) - - def visit_keyword(self, node): - """return an astroid.Keyword node as string""" - return '%s=%s' % (node.arg, node.value.accept(self)) - - def visit_lambda(self, node): - """return an astroid.Lambda node as string""" - return 'lambda %s: %s' % (node.args.accept(self), - node.body.accept(self)) - - def visit_list(self, node): - """return an astroid.List node as string""" - return '[%s]' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_listcomp(self, node): - """return an astroid.ListComp node as string""" - return '[%s %s]' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_module(self, node): - """return an astroid.Module node as string""" - docs = node.doc and '"""%s"""\n\n' % node.doc or '' - return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n' - - def visit_name(self, node): - """return an astroid.Name node as string""" - return node.name - - def visit_pass(self, node): - """return an astroid.Pass node as string""" - return 'pass' - - def visit_print(self, node): - """return an astroid.Print node as string""" - nodes = ', '.join([n.accept(self) for n in node.values]) - if not node.nl: - nodes = '%s,' % nodes - if node.dest: - return 'print >> %s, %s' % (node.dest.accept(self), nodes) - return 'print %s' % nodes - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.inst: - if node.tback: - return 'raise %s, %s, %s' % (node.exc.accept(self), - node.inst.accept(self), - node.tback.accept(self)) - return 'raise %s, %s' % (node.exc.accept(self), - node.inst.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_return(self, node): - """return an astroid.Return node as string""" - if node.value: - return 'return %s' % node.value.accept(self) - else: - return 'return' - - def visit_index(self, node): - """return a astroid.Index node as string""" - return node.value.accept(self) - - def visit_set(self, node): - """return an astroid.Set node as string""" - return '{%s}' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_setcomp(self, node): - """return an astroid.SetComp node as string""" - return '{%s %s}' % (node.elt.accept(self), - ' '.join([n.accept(self) for n in node.generators])) - - def visit_slice(self, node): - """return a astroid.Slice node as string""" - lower = node.lower and node.lower.accept(self) or '' - upper = node.upper and node.upper.accept(self) or '' - step = node.step and node.step.accept(self) or '' - if step: - return '%s:%s:%s' % (lower, upper, step) - return '%s:%s' % (lower, upper) - - def visit_subscript(self, node): - """return an astroid.Subscript node as string""" - return '%s[%s]' % (node.value.accept(self), node.slice.accept(self)) - - def visit_tryexcept(self, node): - """return an astroid.TryExcept node as string""" - trys = ['try:\n%s' % self._stmt_list(node.body)] - for handler in node.handlers: - trys.append(handler.accept(self)) - if node.orelse: - trys.append('else:\n%s' % self._stmt_list(node.orelse)) - return '\n'.join(trys) - - def visit_tryfinally(self, node): - """return an astroid.TryFinally node as string""" - return 'try:\n%s\nfinally:\n%s' % (self._stmt_list(node.body), - self._stmt_list(node.finalbody)) - - def visit_tuple(self, node): - """return an astroid.Tuple node as string""" - if len(node.elts) == 1: - return '(%s, )' % node.elts[0].accept(self) - return '(%s)' % ', '.join([child.accept(self) for child in node.elts]) - - def visit_unaryop(self, node): - """return an astroid.UnaryOp node as string""" - if node.op == 'not': - operator = 'not ' - else: - operator = node.op - return '%s%s' % (operator, node.operand.accept(self)) - - def visit_while(self, node): - """return an astroid.While node as string""" - whiles = 'while %s:\n%s' % (node.test.accept(self), - self._stmt_list(node.body)) - if node.orelse: - whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse)) - return whiles - - def visit_with(self, node): # 'with' without 'as' is possible - """return an astroid.With node as string""" - items = ', '.join(('(%s)' % expr.accept(self)) + - (vars and ' as (%s)' % (vars.accept(self)) or '') - for expr, vars in node.items) - return 'with %s:\n%s' % (items, self._stmt_list(node.body)) - - def visit_yield(self, node): - """yield an ast.Yield node as string""" - yi_val = node.value and (" " + node.value.accept(self)) or "" - expr = 'yield' + yi_val - if node.parent.is_statement: - return expr - else: - return "(%s)" % (expr,) - - -class AsStringVisitor3k(AsStringVisitor): - """AsStringVisitor3k overwrites some AsStringVisitor methods""" - - def visit_excepthandler(self, node): - if node.type: - if node.name: - excs = 'except %s as %s' % (node.type.accept(self), - node.name.accept(self)) - else: - excs = 'except %s' % node.type.accept(self) - else: - excs = 'except' - return '%s:\n%s' % (excs, self._stmt_list(node.body)) - - def visit_nonlocal(self, node): - """return an astroid.Nonlocal node as string""" - return 'nonlocal %s' % ', '.join(node.names) - - def visit_raise(self, node): - """return an astroid.Raise node as string""" - if node.exc: - if node.cause: - return 'raise %s from %s' % (node.exc.accept(self), - node.cause.accept(self)) - return 'raise %s' % node.exc.accept(self) - return 'raise' - - def visit_starred(self, node): - """return Starred node as string""" - return "*" + node.value.accept(self) - - def visit_yieldfrom(self, node): - """ Return an astroid.YieldFrom node as string. """ - yi_val = node.value and (" " + node.value.accept(self)) or "" - expr = 'yield from' + yi_val - if node.parent.is_statement: - return expr - else: - return "(%s)" % (expr,) - - -def _import_string(names): - """return a list of (name, asname) formatted as a string""" - _names = [] - for name, asname in names: - if asname is not None: - _names.append('%s as %s' % (name, asname)) - else: - _names.append(name) - return ', '.join(_names) - - -if sys.version_info >= (3, 0): - AsStringVisitor = AsStringVisitor3k - -# this visitor is stateless, thus it can be reused -to_code = AsStringVisitor() - diff --git a/astroid/__init__.py b/astroid/__init__.py new file mode 100644 index 0000000..6df06b1 --- /dev/null +++ b/astroid/__init__.py @@ -0,0 +1,121 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""Python Abstract Syntax Tree New Generation + +The aim of this module is to provide a common base representation of +python source code for projects such as pychecker, pyreverse, +pylint... Well, actually the development of this library is essentially +governed by pylint's needs. + +It extends class defined in the python's _ast module with some +additional methods and attributes. Instance attributes are added by a +builder object, which can either generate extended ast (let's call +them astroid ;) by visiting an existent ast tree or by inspecting living +object. Methods are added by monkey patching ast classes. + +Main modules are: + +* nodes and scoped_nodes for more information about methods and + attributes added to different node classes + +* the manager contains a high level object to get astroid trees from + source files and living objects. It maintains a cache of previously + constructed tree for quick access + +* builder contains the class responsible to build astroid trees +""" +__doctype__ = "restructuredtext en" + +import sys +import re +from operator import attrgetter + +# WARNING: internal imports order matters ! + +# make all exception classes accessible from astroid package +from astroid.exceptions import * + +# make all node classes accessible from astroid package +from astroid.nodes import * + +# trigger extra monkey-patching +from astroid import inference + +# more stuff available +from astroid import raw_building +from astroid.bases import YES, Instance, BoundMethod, UnboundMethod +from astroid.node_classes import are_exclusive, unpack_infer +from astroid.scoped_nodes import builtin_lookup + +# make a manager instance (borg) as well as Project and Package classes +# accessible from astroid package +from astroid.manager import AstroidManager, Project +MANAGER = AstroidManager() +del AstroidManager + +# transform utilities (filters and decorator) + +class AsStringRegexpPredicate(object): + """Class to be used as predicate that may be given to `register_transform` + + First argument is a regular expression that will be searched against the `as_string` + representation of the node onto which it's applied. + + If specified, the second argument is an `attrgetter` expression that will be + applied on the node first to get the actual node on which `as_string` should + be called. + + WARNING: This can be fairly slow, as it has to convert every AST node back + to Python code; you should consider examining the AST directly instead. + """ + def __init__(self, regexp, expression=None): + self.regexp = re.compile(regexp) + self.expression = expression + + def __call__(self, node): + if self.expression is not None: + node = attrgetter(self.expression)(node) + return self.regexp.search(node.as_string()) + +def inference_tip(infer_function): + """Given an instance specific inference function, return a function to be + given to MANAGER.register_transform to set this inference function. + + Typical usage + + .. sourcecode:: python + + MANAGER.register_transform(CallFunc, inference_tip(infer_named_tuple), + predicate) + """ + def transform(node, infer_function=infer_function): + node._explicit_inference = infer_function + return node + return transform + +# load brain plugins +from os import listdir +from os.path import join, dirname +BRAIN_MODULES_DIR = join(dirname(__file__), 'brain') +if BRAIN_MODULES_DIR not in sys.path: + # add it to the end of the list so user path take precedence + sys.path.append(BRAIN_MODULES_DIR) +# load modules in this directory +for module in listdir(BRAIN_MODULES_DIR): + if module.endswith('.py'): + __import__(module[:-3]) diff --git a/astroid/__pkginfo__.py b/astroid/__pkginfo__.py new file mode 100644 index 0000000..a93173f --- /dev/null +++ b/astroid/__pkginfo__.py @@ -0,0 +1,42 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""astroid packaging information""" +distname = 'astroid' + +modname = 'astroid' + +numversion = (1, 2, 1) +version = '.'.join([str(num) for num in numversion]) + +install_requires = ['logilab-common >= 0.60.0', 'six'] + +license = 'LGPL' + +author = 'Logilab' +author_email = 'python-projects@lists.logilab.org' +mailinglist = "mailto://%s" % author_email +web = 'http://bitbucket.org/logilab/astroid' + +description = "rebuild a new abstract syntax tree from Python's ast" + +classifiers = ["Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: Quality Assurance", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + ] diff --git a/astroid/as_string.py b/astroid/as_string.py new file mode 100644 index 0000000..f19713d --- /dev/null +++ b/astroid/as_string.py @@ -0,0 +1,499 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""This module renders Astroid nodes as string: + +* :func:`to_code` function return equivalent (hopefuly valid) python string + +* :func:`dump` function return an internal representation of nodes found + in the tree, useful for debugging or understanding the tree structure +""" + +import sys + +INDENT = ' ' # 4 spaces ; keep indentation variable + + +def dump(node, ids=False): + """print a nice astroid tree representation. + + :param ids: if true, we also print the ids (usefull for debugging) + """ + result = [] + _repr_tree(node, result, ids=ids) + return "\n".join(result) + +def _repr_tree(node, result, indent='', _done=None, ids=False): + """built a tree representation of a node as a list of lines""" + if _done is None: + _done = set() + if not hasattr(node, '_astroid_fields'): # not a astroid node + return + if node in _done: + result.append(indent + 'loop in tree: %s' % node) + return + _done.add(node) + node_str = str(node) + if ids: + node_str += ' . \t%x' % id(node) + result.append(indent + node_str) + indent += INDENT + for field in node._astroid_fields: + value = getattr(node, field) + if isinstance(value, (list, tuple)): + result.append(indent + field + " = [") + for child in value: + if isinstance(child, (list, tuple)): + # special case for Dict # FIXME + _repr_tree(child[0], result, indent, _done, ids) + _repr_tree(child[1], result, indent, _done, ids) + result.append(indent + ',') + else: + _repr_tree(child, result, indent, _done, ids) + result.append(indent + "]") + else: + result.append(indent + field + " = ") + _repr_tree(value, result, indent, _done, ids) + + +class AsStringVisitor(object): + """Visitor to render an Astroid node as a valid python code string""" + + def __call__(self, node): + """Makes this visitor behave as a simple function""" + return node.accept(self) + + def _stmt_list(self, stmts): + """return a list of nodes to string""" + stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr]) + return INDENT + stmts.replace('\n', '\n'+INDENT) + + + ## visit_ methods ########################################### + + def visit_arguments(self, node): + """return an astroid.Function node as string""" + return node.format_args() + + def visit_assattr(self, node): + """return an astroid.AssAttr node as string""" + return self.visit_getattr(node) + + def visit_assert(self, node): + """return an astroid.Assert node as string""" + if node.fail: + return 'assert %s, %s' % (node.test.accept(self), + node.fail.accept(self)) + return 'assert %s' % node.test.accept(self) + + def visit_assname(self, node): + """return an astroid.AssName node as string""" + return node.name + + def visit_assign(self, node): + """return an astroid.Assign node as string""" + lhs = ' = '.join([n.accept(self) for n in node.targets]) + return '%s = %s' % (lhs, node.value.accept(self)) + + def visit_augassign(self, node): + """return an astroid.AugAssign node as string""" + return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self)) + + def visit_backquote(self, node): + """return an astroid.Backquote node as string""" + return '`%s`' % node.value.accept(self) + + def visit_binop(self, node): + """return an astroid.BinOp node as string""" + return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self)) + + def visit_boolop(self, node): + """return an astroid.BoolOp node as string""" + return (' %s ' % node.op).join(['(%s)' % n.accept(self) + for n in node.values]) + + def visit_break(self, node): + """return an astroid.Break node as string""" + return 'break' + + def visit_callfunc(self, node): + """return an astroid.CallFunc node as string""" + expr_str = node.func.accept(self) + args = [arg.accept(self) for arg in node.args] + if node.starargs: + args.append('*' + node.starargs.accept(self)) + if node.kwargs: + args.append('**' + node.kwargs.accept(self)) + return '%s(%s)' % (expr_str, ', '.join(args)) + + def visit_class(self, node): + """return an astroid.Class node as string""" + decorate = node.decorators and node.decorators.accept(self) or '' + bases = ', '.join([n.accept(self) for n in node.bases]) + if sys.version_info[0] == 2: + bases = bases and '(%s)' % bases or '' + else: + metaclass = node.metaclass() + if metaclass: + if bases: + bases = '(%s, metaclass=%s)' % (bases, metaclass.name) + else: + bases = '(metaclass=%s)' % metaclass.name + else: + bases = bases and '(%s)' % bases or '' + docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' + return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs, + self._stmt_list(node.body)) + + def visit_compare(self, node): + """return an astroid.Compare node as string""" + rhs_str = ' '.join(['%s %s' % (op, expr.accept(self)) + for op, expr in node.ops]) + return '%s %s' % (node.left.accept(self), rhs_str) + + def visit_comprehension(self, node): + """return an astroid.Comprehension node as string""" + ifs = ''.join([' if %s' % n.accept(self) for n in node.ifs]) + return 'for %s in %s%s' % (node.target.accept(self), + node.iter.accept(self), ifs) + + def visit_const(self, node): + """return an astroid.Const node as string""" + return repr(node.value) + + def visit_continue(self, node): + """return an astroid.Continue node as string""" + return 'continue' + + def visit_delete(self, node): # XXX check if correct + """return an astroid.Delete node as string""" + return 'del %s' % ', '.join([child.accept(self) + for child in node.targets]) + + def visit_delattr(self, node): + """return an astroid.DelAttr node as string""" + return self.visit_getattr(node) + + def visit_delname(self, node): + """return an astroid.DelName node as string""" + return node.name + + def visit_decorators(self, node): + """return an astroid.Decorators node as string""" + return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes]) + + def visit_dict(self, node): + """return an astroid.Dict node as string""" + return '{%s}' % ', '.join(['%s: %s' % (key.accept(self), + value.accept(self)) + for key, value in node.items]) + + def visit_dictcomp(self, node): + """return an astroid.DictComp node as string""" + return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self), + ' '.join([n.accept(self) for n in node.generators])) + + def visit_discard(self, node): + """return an astroid.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: + excs = 'except %s, %s' % (node.type.accept(self), + node.name.accept(self)) + else: + excs = 'except %s' % node.type.accept(self) + else: + excs = 'except' + return '%s:\n%s' % (excs, self._stmt_list(node.body)) + + def visit_ellipsis(self, node): + """return an astroid.Ellipsis node as string""" + return '...' + + def visit_empty(self, node): + """return an Empty node as string""" + return '' + + def visit_exec(self, node): + """return an astroid.Exec node as string""" + if node.locals: + return 'exec %s in %s, %s' % (node.expr.accept(self), + node.locals.accept(self), + node.globals.accept(self)) + if node.globals: + return 'exec %s in %s' % (node.expr.accept(self), + node.globals.accept(self)) + return 'exec %s' % node.expr.accept(self) + + def visit_extslice(self, node): + """return an astroid.ExtSlice node as string""" + return ','.join([dim.accept(self) for dim in node.dims]) + + def visit_for(self, node): + """return an astroid.For node as string""" + fors = 'for %s in %s:\n%s' % (node.target.accept(self), + node.iter.accept(self), + self._stmt_list(node.body)) + if node.orelse: + fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse)) + return fors + + def visit_from(self, node): + """return an astroid.From node as string""" + return 'from %s import %s' % ('.' * (node.level or 0) + node.modname, + _import_string(node.names)) + + def visit_function(self, node): + """return an astroid.Function node as string""" + decorate = node.decorators and node.decorators.accept(self) or '' + docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or '' + return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self), + docs, self._stmt_list(node.body)) + + def visit_genexpr(self, node): + """return an astroid.GenExpr node as string""" + return '(%s %s)' % (node.elt.accept(self), + ' '.join([n.accept(self) for n in node.generators])) + + def visit_getattr(self, node): + """return an astroid.Getattr node as string""" + return '%s.%s' % (node.expr.accept(self), node.attrname) + + def visit_global(self, node): + """return an astroid.Global node as string""" + return 'global %s' % ', '.join(node.names) + + def visit_if(self, node): + """return an astroid.If node as string""" + ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))] + if node.orelse:# XXX use elif ??? + ifs.append('else:\n%s' % self._stmt_list(node.orelse)) + return '\n'.join(ifs) + + def visit_ifexp(self, node): + """return an astroid.IfExp node as string""" + return '%s if %s else %s' % (node.body.accept(self), + node.test.accept(self), + node.orelse.accept(self)) + + def visit_import(self, node): + """return an astroid.Import node as string""" + return 'import %s' % _import_string(node.names) + + def visit_keyword(self, node): + """return an astroid.Keyword node as string""" + return '%s=%s' % (node.arg, node.value.accept(self)) + + def visit_lambda(self, node): + """return an astroid.Lambda node as string""" + return 'lambda %s: %s' % (node.args.accept(self), + node.body.accept(self)) + + def visit_list(self, node): + """return an astroid.List node as string""" + return '[%s]' % ', '.join([child.accept(self) for child in node.elts]) + + def visit_listcomp(self, node): + """return an astroid.ListComp node as string""" + return '[%s %s]' % (node.elt.accept(self), + ' '.join([n.accept(self) for n in node.generators])) + + def visit_module(self, node): + """return an astroid.Module node as string""" + docs = node.doc and '"""%s"""\n\n' % node.doc or '' + return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n' + + def visit_name(self, node): + """return an astroid.Name node as string""" + return node.name + + def visit_pass(self, node): + """return an astroid.Pass node as string""" + return 'pass' + + def visit_print(self, node): + """return an astroid.Print node as string""" + nodes = ', '.join([n.accept(self) for n in node.values]) + if not node.nl: + nodes = '%s,' % nodes + if node.dest: + return 'print >> %s, %s' % (node.dest.accept(self), nodes) + return 'print %s' % nodes + + def visit_raise(self, node): + """return an astroid.Raise node as string""" + if node.exc: + if node.inst: + if node.tback: + return 'raise %s, %s, %s' % (node.exc.accept(self), + node.inst.accept(self), + node.tback.accept(self)) + return 'raise %s, %s' % (node.exc.accept(self), + node.inst.accept(self)) + return 'raise %s' % node.exc.accept(self) + return 'raise' + + def visit_return(self, node): + """return an astroid.Return node as string""" + if node.value: + return 'return %s' % node.value.accept(self) + else: + return 'return' + + def visit_index(self, node): + """return a astroid.Index node as string""" + return node.value.accept(self) + + def visit_set(self, node): + """return an astroid.Set node as string""" + return '{%s}' % ', '.join([child.accept(self) for child in node.elts]) + + def visit_setcomp(self, node): + """return an astroid.SetComp node as string""" + return '{%s %s}' % (node.elt.accept(self), + ' '.join([n.accept(self) for n in node.generators])) + + def visit_slice(self, node): + """return a astroid.Slice node as string""" + lower = node.lower and node.lower.accept(self) or '' + upper = node.upper and node.upper.accept(self) or '' + step = node.step and node.step.accept(self) or '' + if step: + return '%s:%s:%s' % (lower, upper, step) + return '%s:%s' % (lower, upper) + + def visit_subscript(self, node): + """return an astroid.Subscript node as string""" + return '%s[%s]' % (node.value.accept(self), node.slice.accept(self)) + + def visit_tryexcept(self, node): + """return an astroid.TryExcept node as string""" + trys = ['try:\n%s' % self._stmt_list(node.body)] + for handler in node.handlers: + trys.append(handler.accept(self)) + if node.orelse: + trys.append('else:\n%s' % self._stmt_list(node.orelse)) + return '\n'.join(trys) + + def visit_tryfinally(self, node): + """return an astroid.TryFinally node as string""" + return 'try:\n%s\nfinally:\n%s' % (self._stmt_list(node.body), + self._stmt_list(node.finalbody)) + + def visit_tuple(self, node): + """return an astroid.Tuple node as string""" + if len(node.elts) == 1: + return '(%s, )' % node.elts[0].accept(self) + return '(%s)' % ', '.join([child.accept(self) for child in node.elts]) + + def visit_unaryop(self, node): + """return an astroid.UnaryOp node as string""" + if node.op == 'not': + operator = 'not ' + else: + operator = node.op + return '%s%s' % (operator, node.operand.accept(self)) + + def visit_while(self, node): + """return an astroid.While node as string""" + whiles = 'while %s:\n%s' % (node.test.accept(self), + self._stmt_list(node.body)) + if node.orelse: + whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse)) + return whiles + + def visit_with(self, node): # 'with' without 'as' is possible + """return an astroid.With node as string""" + items = ', '.join(('(%s)' % expr.accept(self)) + + (vars and ' as (%s)' % (vars.accept(self)) or '') + for expr, vars in node.items) + return 'with %s:\n%s' % (items, self._stmt_list(node.body)) + + def visit_yield(self, node): + """yield an ast.Yield node as string""" + yi_val = node.value and (" " + node.value.accept(self)) or "" + expr = 'yield' + yi_val + if node.parent.is_statement: + return expr + else: + return "(%s)" % (expr,) + + +class AsStringVisitor3k(AsStringVisitor): + """AsStringVisitor3k overwrites some AsStringVisitor methods""" + + def visit_excepthandler(self, node): + if node.type: + if node.name: + excs = 'except %s as %s' % (node.type.accept(self), + node.name.accept(self)) + else: + excs = 'except %s' % node.type.accept(self) + else: + excs = 'except' + return '%s:\n%s' % (excs, self._stmt_list(node.body)) + + def visit_nonlocal(self, node): + """return an astroid.Nonlocal node as string""" + return 'nonlocal %s' % ', '.join(node.names) + + def visit_raise(self, node): + """return an astroid.Raise node as string""" + if node.exc: + if node.cause: + return 'raise %s from %s' % (node.exc.accept(self), + node.cause.accept(self)) + return 'raise %s' % node.exc.accept(self) + return 'raise' + + def visit_starred(self, node): + """return Starred node as string""" + return "*" + node.value.accept(self) + + def visit_yieldfrom(self, node): + """ Return an astroid.YieldFrom node as string. """ + yi_val = node.value and (" " + node.value.accept(self)) or "" + expr = 'yield from' + yi_val + if node.parent.is_statement: + return expr + else: + return "(%s)" % (expr,) + + +def _import_string(names): + """return a list of (name, asname) formatted as a string""" + _names = [] + for name, asname in names: + if asname is not None: + _names.append('%s as %s' % (name, asname)) + else: + _names.append(name) + return ', '.join(_names) + + +if sys.version_info >= (3, 0): + AsStringVisitor = AsStringVisitor3k + +# this visitor is stateless, thus it can be reused +to_code = AsStringVisitor() + diff --git a/astroid/bases.py b/astroid/bases.py new file mode 100644 index 0000000..f1f4cc4 --- /dev/null +++ b/astroid/bases.py @@ -0,0 +1,665 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""This module contains base classes and functions for the nodes and some +inference utils. +""" + +__docformat__ = "restructuredtext en" + +import sys +from contextlib import contextmanager + +from logilab.common.decorators import cachedproperty + +from astroid.exceptions import (InferenceError, AstroidError, NotFoundError, + UnresolvableName, UseInferenceDefault) + + +if sys.version_info >= (3, 0): + BUILTINS = 'builtins' +else: + BUILTINS = '__builtin__' + + +class Proxy(object): + """a simple proxy object""" + + _proxied = None # proxied object may be set by class or by instance + + def __init__(self, proxied=None): + if proxied is not None: + self._proxied = proxied + + def __getattr__(self, name): + if name == '_proxied': + return getattr(self.__class__, '_proxied') + if name in self.__dict__: + return self.__dict__[name] + return getattr(self._proxied, name) + + def infer(self, context=None): + yield self + + +# Inference ################################################################## + +MISSING = object() + + +class InferenceContext(object): + __slots__ = ('path', 'callcontext', 'boundnode', 'infered') + + def __init__(self, + path=None, callcontext=None, boundnode=None, infered=None): + if path is None: + self.path = frozenset() + else: + self.path = path + self.callcontext = callcontext + self.boundnode = boundnode + if infered is None: + self.infered = {} + else: + self.infered = infered + + def push(self, key): + # This returns a NEW context with the same attributes, but a new key + # added to `path`. The intention is that it's only passed to callees + # and then destroyed; otherwise scope() may not work correctly. + # The cache will be shared, since it's the same exact dict. + if key in self.path: + # End the containing generator + raise StopIteration + + return InferenceContext( + self.path.union([key]), + self.callcontext, + self.boundnode, + self.infered, + ) + + @contextmanager + def scope(self, callcontext=MISSING, boundnode=MISSING): + try: + orig = self.callcontext, self.boundnode + if callcontext is not MISSING: + self.callcontext = callcontext + if boundnode is not MISSING: + self.boundnode = boundnode + yield + finally: + self.callcontext, self.boundnode = orig + + def cache_generator(self, key, generator): + results = [] + for result in generator: + results.append(result) + yield result + + self.infered[key] = tuple(results) + return + + +def _infer_stmts(stmts, context, frame=None, lookupname=None): + """return an iterator on statements inferred by each statement in + """ + stmt = None + infered = False + if context is None: + context = InferenceContext() + for stmt in stmts: + if stmt is YES: + yield stmt + infered = True + continue + + kw = {} + infered_name = stmt._infer_name(frame, lookupname) + if infered_name is not None: + # only returns not None if .infer() accepts a lookupname kwarg + kw['lookupname'] = infered_name + + try: + for infered in stmt.infer(context, **kw): + yield infered + infered = True + except UnresolvableName: + continue + except InferenceError: + yield YES + infered = True + if not infered: + raise InferenceError(str(stmt)) + + +# special inference objects (e.g. may be returned as nodes by .infer()) ####### + +class _Yes(object): + """a yes object""" + def __repr__(self): + return 'YES' + def __getattribute__(self, name): + if name == 'next': + raise AttributeError('next method should not be called') + if name.startswith('__') and name.endswith('__'): + # to avoid inspection pb + return super(_Yes, self).__getattribute__(name) + return self + def __call__(self, *args, **kwargs): + return self + + +YES = _Yes() + + +class Instance(Proxy): + """a special node representing a class instance""" + def getattr(self, name, context=None, lookupclass=True): + try: + values = self._proxied.instance_attr(name, context) + except NotFoundError: + if name == '__class__': + return [self._proxied] + if lookupclass: + # class attributes not available through the instance + # unless they are explicitly defined + if name in ('__name__', '__bases__', '__mro__', '__subclasses__'): + return self._proxied.local_attr(name) + return self._proxied.getattr(name, context) + raise NotFoundError(name) + # since we've no context information, return matching class members as + # well + if lookupclass: + try: + return values + self._proxied.getattr(name, context) + except NotFoundError: + pass + return values + + def igetattr(self, name, context=None): + """inferred getattr""" + if not context: + context = InferenceContext() + try: + # avoid recursively inferring the same attr on the same class + new_context = context.push((self._proxied, name)) + # XXX frame should be self._proxied, or not ? + get_attr = self.getattr(name, new_context, lookupclass=False) + return _infer_stmts( + self._wrap_attr(get_attr, new_context), + new_context, + frame=self, + ) + except NotFoundError: + try: + # fallback to class'igetattr since it has some logic to handle + # descriptors + return self._wrap_attr(self._proxied.igetattr(name, context), + context) + except NotFoundError: + raise InferenceError(name) + + def _wrap_attr(self, attrs, context=None): + """wrap bound methods of attrs in a InstanceMethod proxies""" + for attr in attrs: + if isinstance(attr, UnboundMethod): + if BUILTINS + '.property' in attr.decoratornames(): + for infered in attr.infer_call_result(self, context): + yield infered + else: + yield BoundMethod(attr, self) + else: + yield attr + + def infer_call_result(self, caller, context=None): + """infer what a class instance is returning when called""" + infered = False + for node in self._proxied.igetattr('__call__', context): + if node is YES: + continue + for res in node.infer_call_result(caller, context): + infered = True + yield res + if not infered: + raise InferenceError() + + def __repr__(self): + return '' % (self._proxied.root().name, + self._proxied.name, + id(self)) + def __str__(self): + return 'Instance of %s.%s' % (self._proxied.root().name, + self._proxied.name) + + def callable(self): + try: + self._proxied.getattr('__call__') + return True + except NotFoundError: + return False + + def pytype(self): + return self._proxied.qname() + + def display_type(self): + return 'Instance of' + + +class UnboundMethod(Proxy): + """a special node representing a method not bound to an instance""" + def __repr__(self): + frame = self._proxied.parent.frame() + return '<%s %s of %s at 0x%s' % (self.__class__.__name__, + self._proxied.name, + frame.qname(), id(self)) + + def is_bound(self): + return False + + def getattr(self, name, context=None): + if name == 'im_func': + return [self._proxied] + return super(UnboundMethod, self).getattr(name, context) + + def igetattr(self, name, context=None): + if name == 'im_func': + 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() == '%s.object' % BUILTINS): + infer = caller.args[0].infer() if caller.args else [] + return ((x is YES and x or Instance(x)) for x in infer) + return self._proxied.infer_call_result(caller, context) + + +class BoundMethod(UnboundMethod): + """a special node representing a method bound to an instance""" + def __init__(self, proxy, bound): + UnboundMethod.__init__(self, proxy) + self.bound = bound + + def is_bound(self): + return True + + def infer_call_result(self, caller, context): + with context.scope(boundnode=self.bound): + for infered in self._proxied.infer_call_result(caller, context): + yield infered + + +class Generator(Instance): + """a special node representing a generator. + + Proxied class is set once for all in raw_building. + """ + def callable(self): + return False + + def pytype(self): + return '%s.generator' % BUILTINS + + def display_type(self): + return 'Generator' + + def __repr__(self): + return '' % (self._proxied.name, self.lineno, id(self)) + + def __str__(self): + return 'Generator(%s)' % (self._proxied.name) + + +# decorators ################################################################## + +def path_wrapper(func): + """return the given infer function wrapped to handle the path""" + def wrapped(node, context=None, _func=func, **kwargs): + """wrapper function handling context""" + if context is None: + context = InferenceContext() + context = context.push((node, kwargs.get('lookupname'))) + + yielded = set() + for res in _func(node, context, **kwargs): + # unproxy only true instance, not const, tuple, dict... + if res.__class__ is Instance: + ares = res._proxied + else: + ares = res + if not ares in yielded: + yield res + yielded.add(ares) + return wrapped + +def yes_if_nothing_infered(func): + def wrapper(*args, **kwargs): + infered = False + for node in func(*args, **kwargs): + infered = True + yield node + if not infered: + yield YES + return wrapper + +def raise_if_nothing_infered(func): + def wrapper(*args, **kwargs): + infered = False + for node in func(*args, **kwargs): + infered = True + yield node + if not infered: + raise InferenceError() + return wrapper + + +# Node ###################################################################### + +class NodeNG(object): + """Base Class for all Astroid node classes. + + It represents a node of the new abstract syntax tree. + """ + is_statement = False + optional_assign = False # True for For (and for Comprehension if py <3.0) + is_function = False # True for Function nodes + # attributes below are set by the builder module or by raw factories + lineno = None + fromlineno = None + tolineno = None + col_offset = None + # parent node in the tree + parent = None + # attributes containing child node(s) redefined in most concrete classes: + _astroid_fields = () + # instance specific inference function infer(node, context) + _explicit_inference = None + + def infer(self, context=None, **kwargs): + """main interface to the interface system, return a generator on infered + values. + + If the instance has some explicit inference function set, it will be + called instead of the default interface. + """ + if self._explicit_inference is not None: + # explicit_inference is not bound, give it self explicitly + try: + return self._explicit_inference(self, context, **kwargs) + except UseInferenceDefault: + pass + + if not context: + return self._infer(context, **kwargs) + + key = (self, kwargs.get('lookupname'), context.callcontext, context.boundnode) + if key in context.infered: + return iter(context.infered[key]) + + return context.cache_generator(key, self._infer(context, **kwargs)) + + def _repr_name(self): + """return self.name or self.attrname or '' for nice representation""" + return getattr(self, 'name', getattr(self, 'attrname', '')) + + def __str__(self): + return '%s(%s)' % (self.__class__.__name__, self._repr_name()) + + def __repr__(self): + return '<%s(%s) l.%s [%s] at 0x%x>' % (self.__class__.__name__, + self._repr_name(), + self.fromlineno, + self.root().name, + id(self)) + + + def accept(self, visitor): + func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) + return func(self) + + def get_children(self): + for field in self._astroid_fields: + attr = getattr(self, field) + if attr is None: + continue + if isinstance(attr, (list, tuple)): + for elt in attr: + yield elt + else: + yield attr + + def last_child(self): + """an optimized version of list(get_children())[-1]""" + for field in self._astroid_fields[::-1]: + attr = getattr(self, field) + if not attr: # None or empty listy / tuple + continue + if attr.__class__ in (list, tuple): + return attr[-1] + else: + return attr + return None + + def parent_of(self, node): + """return true if i'm a parent of the given node""" + parent = node.parent + while parent is not None: + if self is parent: + return True + parent = parent.parent + return False + + def statement(self): + """return the first parent node marked as statement node""" + if self.is_statement: + return self + return self.parent.statement() + + def frame(self): + """return the first parent frame node (i.e. Module, Function or Class) + """ + return self.parent.frame() + + def scope(self): + """return the first node defining a new scope (i.e. Module, Function, + Class, Lambda but also GenExpr) + """ + return self.parent.scope() + + def root(self): + """return the root node of the tree, (i.e. a Module)""" + if self.parent: + return self.parent.root() + return self + + def child_sequence(self, child): + """search for the right sequence where the child lies in""" + for field in self._astroid_fields: + node_or_sequence = getattr(self, field) + if node_or_sequence is child: + return [node_or_sequence] + # /!\ compiler.ast Nodes have an __iter__ walking over child nodes + if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: + return node_or_sequence + else: + msg = 'Could not find %s in %s\'s children' + raise AstroidError(msg % (repr(child), repr(self))) + + def locate_child(self, child): + """return a 2-uple (child attribute name, sequence or node)""" + for field in self._astroid_fields: + node_or_sequence = getattr(self, field) + # /!\ compiler.ast Nodes have an __iter__ walking over child nodes + if child is node_or_sequence: + return field, child + if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: + return field, node_or_sequence + msg = 'Could not find %s in %s\'s children' + raise AstroidError(msg % (repr(child), repr(self))) + # FIXME : should we merge child_sequence and locate_child ? locate_child + # is only used in are_exclusive, child_sequence one time in pylint. + + def next_sibling(self): + """return the next sibling statement""" + return self.parent.next_sibling() + + def previous_sibling(self): + """return the previous sibling statement""" + return self.parent.previous_sibling() + + def nearest(self, nodes): + """return the node which is the nearest before this one in the + given list of nodes + """ + myroot = self.root() + mylineno = self.fromlineno + nearest = None, 0 + for node in nodes: + assert node.root() is myroot, \ + 'nodes %s and %s are not from the same module' % (self, node) + lineno = node.fromlineno + if node.fromlineno > mylineno: + break + if lineno > nearest[1]: + nearest = node, lineno + # FIXME: raise an exception if nearest is None ? + return nearest[0] + + # these are lazy because they're relatively expensive to compute for every + # single node, and they rarely get looked at + + @cachedproperty + def fromlineno(self): + if self.lineno is None: + return self._fixed_source_line() + else: + return self.lineno + + @cachedproperty + def tolineno(self): + if not self._astroid_fields: + # can't have children + lastchild = None + else: + lastchild = self.last_child() + if lastchild is None: + return self.fromlineno + else: + return lastchild.tolineno + + # TODO / FIXME: + assert self.fromlineno is not None, self + assert self.tolineno is not None, self + + def _fixed_source_line(self): + """return the line number where the given node appears + + we need this method since not all nodes have the lineno attribute + correctly set... + """ + line = self.lineno + _node = self + try: + while line is None: + _node = next(_node.get_children()) + line = _node.lineno + except StopIteration: + _node = self.parent + while _node and line is None: + line = _node.lineno + _node = _node.parent + return line + + def block_range(self, lineno): + """handle block line numbers range for non block opening statements + """ + return lineno, self.tolineno + + def set_local(self, name, stmt): + """delegate to a scoped parent handling a locals dictionary""" + self.parent.set_local(name, stmt) + + def nodes_of_class(self, klass, skip_klass=None): + """return an iterator on nodes which are instance of the given class(es) + + klass may be a class object or a tuple of class objects + """ + if isinstance(self, klass): + yield self + for child_node in self.get_children(): + if skip_klass is not None and isinstance(child_node, skip_klass): + continue + for matching in child_node.nodes_of_class(klass, skip_klass): + yield matching + + def _infer_name(self, frame, name): + # overridden for From, Import, Global, TryExcept and Arguments + return None + + def _infer(self, context=None): + """we don't know how to resolve a statement by default""" + # this method is overridden by most concrete classes + raise InferenceError(self.__class__.__name__) + + def infered(self): + '''return list of infered values for a more simple inference usage''' + return list(self.infer()) + + def instanciate_class(self): + """instanciate a node if it is a Class node, else return self""" + return self + + def has_base(self, node): + return False + + def callable(self): + return False + + def eq(self, value): + return False + + def as_string(self): + from astroid.as_string import to_code + return to_code(self) + + def repr_tree(self, ids=False): + from astroid.as_string import dump + return dump(self) + + +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] diff --git a/astroid/brain/py2gi.py b/astroid/brain/py2gi.py new file mode 100644 index 0000000..20356c1 --- /dev/null +++ b/astroid/brain/py2gi.py @@ -0,0 +1,169 @@ +"""Astroid hooks for the Python 2 GObject introspection bindings. + +Helps with understanding everything imported from 'gi.repository' +""" + +import inspect +import itertools +import sys +import re + +from astroid import MANAGER, AstroidBuildingException +from astroid.builder import AstroidBuilder + + +_inspected_modules = {} + +_identifier_re = r'^[A-Za-z_]\w*$' + +def _gi_build_stub(parent): + """ + Inspect the passed module recursively and build stubs for functions, + classes, etc. + """ + classes = {} + functions = {} + constants = {} + methods = {} + for name in dir(parent): + if name.startswith("__"): + continue + + # Check if this is a valid name in python + if not re.match(_identifier_re, name): + continue + + try: + obj = getattr(parent, name) + except: + continue + + if inspect.isclass(obj): + classes[name] = obj + elif (inspect.isfunction(obj) or + inspect.isbuiltin(obj)): + functions[name] = obj + elif (inspect.ismethod(obj) or + inspect.ismethoddescriptor(obj)): + methods[name] = obj + elif type(obj) in [int, str]: + constants[name] = obj + elif (str(obj).startswith(" (3, 0) +PY33 = sys.version_info >= (3, 3) + +# general function + +def infer_func_form(node, base_type, context=None, enum=False): + """Specific inference function for namedtuple or Python 3 enum. """ + def infer_first(node): + try: + value = next(node.infer(context=context)) + if value is YES: + raise UseInferenceDefault() + else: + return value + except StopIteration: + raise InferenceError() + + # node is a CallFunc node, class name as first argument and generated class + # attributes as second argument + if len(node.args) != 2: + # something weird here, go back to class implementation + raise UseInferenceDefault() + # namedtuple or enums list of attributes can be a list of strings or a + # whitespace-separate string + try: + name = infer_first(node.args[0]).value + names = infer_first(node.args[1]) + try: + attributes = names.value.replace(',', ' ').split() + except AttributeError: + if not enum: + attributes = [infer_first(const).value for const in names.elts] + else: + # Enums supports either iterator of (name, value) pairs + # or mappings. + # TODO: support only list, tuples and mappings. + if hasattr(names, 'items') and isinstance(names.items, list): + attributes = [infer_first(const[0]).value + for const in names.items + if isinstance(const[0], nodes.Const)] + elif hasattr(names, 'elts'): + # Enums can support either ["a", "b", "c"] + # or [("a", 1), ("b", 2), ...], but they can't + # be mixed. + if all(isinstance(const, nodes.Tuple) + for const in names.elts): + attributes = [infer_first(const.elts[0]).value + for const in names.elts + if isinstance(const, nodes.Tuple)] + else: + attributes = [infer_first(const).value + for const in names.elts] + else: + raise AttributeError + if not attributes: + raise AttributeError + except (AttributeError, exceptions.InferenceError) as exc: + raise UseInferenceDefault() + # we want to return a Class node instance with proper attributes set + class_node = nodes.Class(name, 'docstring') + class_node.parent = node.parent + # set base class=tuple + class_node.bases.append(base_type) + # XXX add __init__(*attributes) method + for attr in attributes: + fake_node = nodes.EmptyNode() + fake_node.parent = class_node + class_node.instance_attrs[attr] = [fake_node] + return class_node, name, attributes + + +# module specific transformation functions ##################################### + +def transform(module): + try: + tr = MODULE_TRANSFORMS[module.name] + except KeyError: + pass + else: + tr(module) +MANAGER.register_transform(nodes.Module, transform) + +# module specific transformation functions ##################################### + +def hashlib_transform(module): + template = ''' + +class %(name)s(object): + def __init__(self, value=''): pass + def digest(self): + return u'' + def copy(self): + return self + def update(self, value): pass + def hexdigest(self): + return u'' + @property + def name(self): + return %(name)r +''' + + algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') + classes = "".join(template % {'name': hashfunc} + for hashfunc in algorithms) + + fake = AstroidBuilder(MANAGER).string_build(classes) + + for hashfunc in algorithms: + module.locals[hashfunc] = fake.locals[hashfunc] + +def collections_transform(module): + fake = AstroidBuilder(MANAGER).string_build(''' + +class defaultdict(dict): + default_factory = None + def __missing__(self, key): pass + +class deque(object): + maxlen = 0 + def __init__(self, iterable=None, maxlen=None): pass + def append(self, x): pass + def appendleft(self, x): pass + def clear(self): pass + def count(self, x): return 0 + def extend(self, iterable): pass + def extendleft(self, iterable): pass + def pop(self): pass + def popleft(self): pass + def remove(self, value): pass + def reverse(self): pass + def rotate(self, n): pass + def __iter__(self): return self + +''') + + for klass in ('deque', 'defaultdict'): + module.locals[klass] = fake.locals[klass] + +def pkg_resources_transform(module): + fake = AstroidBuilder(MANAGER).string_build(''' + +def resource_exists(package_or_requirement, resource_name): + pass + +def resource_isdir(package_or_requirement, resource_name): + pass + +def resource_filename(package_or_requirement, resource_name): + pass + +def resource_stream(package_or_requirement, resource_name): + pass + +def resource_string(package_or_requirement, resource_name): + pass + +def resource_listdir(package_or_requirement, resource_name): + pass + +def extraction_error(): + pass + +def get_cache_path(archive_name, names=()): + pass + +def postprocess(tempname, filename): + pass + +def set_extraction_path(path): + pass + +def cleanup_resources(force=False): + pass + +''') + + for func_name, func in fake.locals.items(): + module.locals[func_name] = func + + +def subprocess_transform(module): + if PY3K: + communicate = (bytes('string', 'ascii'), bytes('string', 'ascii')) + init = """ + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0, restore_signals=True, + start_new_session=False, pass_fds=()): + pass + """ + else: + communicate = ('string', 'string') + init = """ + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0): + pass + """ + if PY33: + wait_signature = 'def wait(self, timeout=None)' + else: + wait_signature = 'def wait(self)' + fake = AstroidBuilder(MANAGER).string_build(''' + +class Popen(object): + returncode = pid = 0 + stdin = stdout = stderr = file() + + %(init)s + + def communicate(self, input=None): + return %(communicate)r + %(wait_signature)s: + return self.returncode + def poll(self): + return self.returncode + def send_signal(self, signal): + pass + def terminate(self): + pass + def kill(self): + pass + ''' % {'init': init, + 'communicate': communicate, + 'wait_signature': wait_signature}) + + for func_name, func in fake.locals.items(): + module.locals[func_name] = func + + + +MODULE_TRANSFORMS['hashlib'] = hashlib_transform +MODULE_TRANSFORMS['collections'] = collections_transform +MODULE_TRANSFORMS['pkg_resources'] = pkg_resources_transform +MODULE_TRANSFORMS['subprocess'] = subprocess_transform + +# namedtuple support ########################################################### + +def looks_like_namedtuple(node): + func = node.func + if type(func) is nodes.Getattr: + return func.attrname == 'namedtuple' + if type(func) is nodes.Name: + return func.name == 'namedtuple' + return False + +def infer_named_tuple(node, context=None): + """Specific inference function for namedtuple CallFunc node""" + class_node, name, attributes = infer_func_form(node, nodes.Tuple._proxied, + context=context) + fake = AstroidBuilder(MANAGER).string_build(''' +class %(name)s(tuple): + _fields = %(fields)r + def _asdict(self): + return self.__dict__ + @classmethod + def _make(cls, iterable, new=tuple.__new__, len=len): + return new(cls, iterable) + def _replace(_self, **kwds): + result = _self._make(map(kwds.pop, %(fields)r, _self)) + if kwds: + raise ValueError('Got unexpected field names: %%r' %% list(kwds)) + return result + ''' % {'name': name, 'fields': attributes}) + class_node.locals['_asdict'] = fake.body[0].locals['_asdict'] + class_node.locals['_make'] = fake.body[0].locals['_make'] + class_node.locals['_replace'] = fake.body[0].locals['_replace'] + class_node.locals['_fields'] = fake.body[0].locals['_fields'] + # we use UseInferenceDefault, we can't be a generator so return an iterator + return iter([class_node]) + +def infer_enum(node, context=None): + """ Specific inference function for enum CallFunc node. """ + enum_meta = nodes.Class("EnumMeta", 'docstring') + class_node = infer_func_form(node, enum_meta, + context=context, enum=True)[0] + return iter([class_node.instanciate_class()]) + +def infer_enum_class(node, context=None): + """ Specific inference for enums. """ + names = set(('Enum', 'IntEnum', 'enum.Enum', 'enum.IntEnum')) + for basename in node.basenames: + # TODO: doesn't handle subclasses yet. This implementation + # is a hack to support enums. + if basename not in names: + continue + if node.root().name == 'enum': + # Skip if the class is directly from enum module. + break + for local, values in node.locals.items(): + if any(not isinstance(value, nodes.AssName) + for value in values): + continue + + stmt = values[0].statement() + if isinstance(stmt.targets[0], nodes.Tuple): + targets = stmt.targets[0].itered() + else: + targets = stmt.targets + + new_targets = [] + for target in targets: + # Replace all the assignments with our mocked class. + classdef = dedent(''' + class %(name)s(object): + @property + def value(self): + # Not the best return. + return None + @property + def name(self): + return %(name)r + ''' % {'name': target.name}) + fake = AstroidBuilder(MANAGER).string_build(classdef)[target.name] + fake.parent = target.parent + for method in node.mymethods(): + fake.locals[method.name] = [method] + new_targets.append(fake.instanciate_class()) + node.locals[local] = new_targets + break + return node + +MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_named_tuple), + looks_like_namedtuple) +MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_enum), + AsStringRegexpPredicate('Enum', 'func')) +MANAGER.register_transform(nodes.Class, infer_enum_class) diff --git a/astroid/builder.py b/astroid/builder.py new file mode 100644 index 0000000..4e0e225 --- /dev/null +++ b/astroid/builder.py @@ -0,0 +1,237 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""The AstroidBuilder makes astroid from living object and / or from _ast + +The builder is not thread safe and can't be used to parse different sources +at the same time. +""" +from __future__ import with_statement + +__docformat__ = "restructuredtext en" + +import sys +from os.path import splitext, basename, exists, abspath + +from astroid.exceptions import AstroidBuildingException, InferenceError +from astroid.raw_building import InspectBuilder +from astroid.rebuilder import TreeRebuilder +from astroid.manager import AstroidManager +from astroid.bases import YES, Instance +from astroid.modutils import modpath_from_file + +from _ast import PyCF_ONLY_AST +def parse(string): + return compile(string, "", 'exec', PyCF_ONLY_AST) + +if sys.version_info >= (3, 0): + from tokenize import detect_encoding + + def open_source_file(filename): + with open(filename, 'rb') as byte_stream: + encoding = detect_encoding(byte_stream.readline)[0] + stream = open(filename, 'rU', encoding=encoding) + try: + data = stream.read() + except UnicodeError: # wrong encodingg + # detect_encoding returns utf-8 if no encoding specified + msg = 'Wrong (%s) or no encoding specified' % encoding + raise AstroidBuildingException(msg) + return stream, encoding, data + +else: + import re + + _ENCODING_RGX = re.compile(r"\s*#+.*coding[:=]\s*([-\w.]+)") + + 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 ############################################################## + +MANAGER = AstroidManager() + +class AstroidBuilder(InspectBuilder): + """provide astroid building methods""" + + def __init__(self, manager=None): + InspectBuilder.__init__(self) + self._manager = manager or MANAGER + + def module_build(self, module, modname=None): + """build an astroid from a living module instance + """ + node = None + path = getattr(module, '__file__', None) + if path is not None: + path_, ext = splitext(module.__file__) + if ext in ('.py', '.pyc', '.pyo') and exists(path_ + '.py'): + node = self.file_build(path_ + '.py', modname) + if node is None: + # this is a built-in module + # get a partial representation by introspection + node = self.inspect_build(module, modname=modname, path=path) + # we have to handle transformation by ourselves since the rebuilder + # isn't called for builtin nodes + # + # XXX it's then only called for Module nodes, not for underlying + # nodes + node = self._manager.transform(node) + return node + + def file_build(self, path, modname=None): + """build astroid from a source code file (i.e. from an ast) + + path is expected to be a python source file + """ + try: + _, encoding, data = open_source_file(path) + except IOError as exc: + msg = 'Unable to load file %r (%s)' % (path, exc) + raise AstroidBuildingException(msg) + except SyntaxError as exc: # py3k encoding specification error + raise AstroidBuildingException(exc) + except LookupError as exc: # unknown encoding + raise AstroidBuildingException(exc) + # get module name if necessary + if modname is None: + try: + modname = '.'.join(modpath_from_file(path)) + except ImportError: + modname = splitext(basename(path))[0] + # build astroid representation + module = self._data_build(data, modname, path) + return self._post_build(module, encoding) + + def string_build(self, data, modname='', path=None): + """build astroid from source code string and return rebuilded astroid""" + module = self._data_build(data, modname, path) + module.file_bytes = data.encode('utf-8') + return self._post_build(module, 'utf-8') + + def _post_build(self, module, encoding): + """handles encoding and delayed nodes + after a module has been built + """ + module.file_encoding = encoding + self._manager.cache_module(module) + # post tree building steps after we stored the module in the cache: + for from_node in module._from_nodes: + if from_node.modname == '__future__': + for symbol, _ in from_node.names: + module.future_imports.add(symbol) + 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: + node_file = '' + if modname.endswith('.__init__'): + modname = modname[:-9] + package = True + else: + package = path and path.find('__init__.py') > -1 or False + rebuilder = TreeRebuilder(self._manager) + module = rebuilder.visit_module(node, modname, package) + module.file = module.path = node_file + module._from_nodes = rebuilder._from_nodes + module._delayed_assattr = 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 + """ + + _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.do_import_module() + except InferenceError: + 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 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 + elif infered.is_function: + iattrs = infered.instance_attrs + 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 + diff --git a/astroid/exceptions.py b/astroid/exceptions.py new file mode 100644 index 0000000..3889e2e --- /dev/null +++ b/astroid/exceptions.py @@ -0,0 +1,51 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""this module contains exceptions used in the astroid library + +""" + +__doctype__ = "restructuredtext en" + +class AstroidError(Exception): + """base exception class for all astroid related exceptions""" + +class AstroidBuildingException(AstroidError): + """exception class when we are unable to build an astroid representation""" + +class ResolveError(AstroidError): + """base class of astroid resolution/inference error""" + +class NotFoundError(ResolveError): + """raised when we are unable to resolve a name""" + +class InferenceError(ResolveError): + """raised when we are unable to infer a node""" + +class UseInferenceDefault(Exception): + """exception to be raised in custom inference function to indicate that it + should go back to the default behaviour + """ + +class UnresolvableName(InferenceError): + """raised when we are unable to resolve a name""" + +class NoDefault(AstroidError): + """raised by function's `default_value` method when an argument has + no default value + """ + diff --git a/astroid/inference.py b/astroid/inference.py new file mode 100644 index 0000000..6cd4b78 --- /dev/null +++ b/astroid/inference.py @@ -0,0 +1,395 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""this module contains a set of functions to handle inference on astroid trees +""" + +__doctype__ = "restructuredtext en" + +from itertools import chain + +from astroid import nodes + +from astroid.manager import AstroidManager +from astroid.exceptions import (AstroidError, InferenceError, NoDefault, + NotFoundError, UnresolvableName) +from astroid.bases import (YES, Instance, InferenceContext, + _infer_stmts, path_wrapper, + raise_if_nothing_infered) +from astroid.protocols import ( + _arguments_infer_argname, + BIN_OP_METHOD, UNARY_OP_METHOD) + +MANAGER = AstroidManager() + + +class CallContext(object): + """when inferring a function call, this class is used to remember values + given as argument + """ + def __init__(self, args, starargs, dstarargs): + self.args = [] + self.nargs = {} + for arg in args: + if isinstance(arg, nodes.Keyword): + self.nargs[arg.arg] = arg.value + else: + self.args.append(arg) + self.starargs = starargs + self.dstarargs = dstarargs + + def infer_argument(self, funcnode, name, context): + """infer a function argument value according to the call context""" + # 1. search in named keywords + try: + return self.nargs[name].infer(context) + except KeyError: + # Function.args.args can be None in astroid (means that we don't have + # information on argnames) + argindex = funcnode.args.find_argname(name)[0] + if argindex is not None: + # 2. first argument of instance/class method + if argindex == 0 and funcnode.type in ('method', 'classmethod'): + if context.boundnode is not None: + boundnode = context.boundnode + else: + # XXX can do better ? + boundnode = funcnode.parent.frame() + if funcnode.type == 'method': + if not isinstance(boundnode, Instance): + boundnode = Instance(boundnode) + return iter((boundnode,)) + if funcnode.type == 'classmethod': + return iter((boundnode,)) + # if we have a method, extract one position + # from the index, so we'll take in account + # the extra parameter represented by `self` or `cls` + if funcnode.type in ('method', 'classmethod'): + argindex -= 1 + # 2. search arg index + try: + return self.args[argindex].infer(context) + except IndexError: + pass + # 3. search in *args (.starargs) + if self.starargs is not None: + its = [] + for infered in self.starargs.infer(context): + if infered is YES: + its.append((YES,)) + continue + try: + its.append(infered.getitem(argindex, context).infer(context)) + except (InferenceError, AttributeError): + its.append((YES,)) + except (IndexError, TypeError): + continue + if its: + return chain(*its) + # 4. XXX search in **kwargs (.dstarargs) + if self.dstarargs is not None: + its = [] + for infered in self.dstarargs.infer(context): + if infered is YES: + its.append((YES,)) + continue + try: + its.append(infered.getitem(name, context).infer(context)) + except (InferenceError, AttributeError): + its.append((YES,)) + except (IndexError, TypeError): + continue + if its: + return chain(*its) + # 5. */** argument, (Tuple or Dict) + if name == funcnode.args.vararg: + return iter((nodes.const_factory(()))) + if name == funcnode.args.kwarg: + return iter((nodes.const_factory({}))) + # 6. return default value if any + try: + return funcnode.args.default_value(name).infer(context) + except NoDefault: + raise InferenceError(name) + + +# .infer method ############################################################### + + +def infer_end(self, context=None): + """inference's end for node such as Module, Class, Function, Const... + """ + yield self +nodes.Module._infer = infer_end +nodes.Class._infer = infer_end +nodes.Function._infer = infer_end +nodes.Lambda._infer = infer_end +nodes.Const._infer = infer_end +nodes.List._infer = infer_end +nodes.Tuple._infer = infer_end +nodes.Dict._infer = infer_end +nodes.Set._infer = infer_end + +def _higher_function_scope(node): + """ Search for the first function which encloses the given + scope. This can be used for looking up in that function's + scope, in case looking up in a lower scope for a particular + name fails. + + :param node: A scope node. + :returns: + ``None``, if no parent function scope was found, + otherwise an instance of :class:`astroid.scoped_nodes.Function`, + which encloses the given node. + """ + current = node + while current.parent and not isinstance(current.parent, nodes.Function): + current = current.parent + if current and current.parent: + return current.parent + +def infer_name(self, context=None): + """infer a Name: use name lookup rules""" + frame, stmts = self.lookup(self.name) + if not stmts: + # Try to see if the name is enclosed in a nested function + # and use the higher (first function) scope for searching. + # TODO: should this be promoted to other nodes as well? + parent_function = _higher_function_scope(self.scope()) + if parent_function: + _, stmts = parent_function.lookup(self.name) + + if not stmts: + raise UnresolvableName(self.name) + return _infer_stmts(stmts, context, frame, self.name) +nodes.Name._infer = path_wrapper(infer_name) +nodes.AssName.infer_lhs = infer_name # won't work with a path wrapper + + +def infer_callfunc(self, context=None): + """infer a CallFunc node by trying to guess what the function returns""" + if context is None: + context = InferenceContext() + for callee in self.func.infer(context): + with context.scope( + callcontext=CallContext(self.args, self.starargs, self.kwargs), + boundnode=None, + ): + if callee is YES: + yield callee + continue + try: + if hasattr(callee, 'infer_call_result'): + for infered in callee.infer_call_result(self, context): + yield infered + except InferenceError: + ## XXX log error ? + continue +nodes.CallFunc._infer = path_wrapper(raise_if_nothing_infered(infer_callfunc)) + + +def infer_import(self, context=None, asname=True, lookupname=None): + """infer an Import node: return the imported module/object""" + if lookupname is None: + raise InferenceError() + if asname: + yield self.do_import_module(self.real_name(lookupname)) + else: + yield self.do_import_module(lookupname) +nodes.Import._infer = path_wrapper(infer_import) + +def infer_name_module(self, name): + context = InferenceContext() + return self.infer(context, asname=False, lookupname=name) +nodes.Import.infer_name_module = infer_name_module + + +def infer_from(self, context=None, asname=True, lookupname=None): + """infer a From nodes: return the imported module/object""" + if lookupname is None: + raise InferenceError() + if asname: + lookupname = self.real_name(lookupname) + module = self.do_import_module() + try: + return _infer_stmts(module.getattr(lookupname, ignore_locals=module is self.root()), context, lookupname=lookupname) + except NotFoundError: + raise InferenceError(lookupname) +nodes.From._infer = path_wrapper(infer_from) + + +def infer_getattr(self, context=None): + """infer a Getattr node by using getattr on the associated object""" + if not context: + context = InferenceContext() + for owner in self.expr.infer(context): + if owner is YES: + yield owner + continue + try: + with context.scope(boundnode=owner): + for obj in owner.igetattr(self.attrname, context): + yield obj + except (NotFoundError, InferenceError): + pass + except AttributeError: + # XXX method / function + pass +nodes.Getattr._infer = path_wrapper(raise_if_nothing_infered(infer_getattr)) +nodes.AssAttr.infer_lhs = raise_if_nothing_infered(infer_getattr) # # won't work with a path wrapper + + +def infer_global(self, context=None, lookupname=None): + if lookupname is None: + raise InferenceError() + try: + return _infer_stmts(self.root().getattr(lookupname), context) + except NotFoundError: + raise InferenceError() +nodes.Global._infer = path_wrapper(infer_global) + + +def infer_subscript(self, context=None): + """infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]""" + value = next(self.value.infer(context)) + if value is YES: + yield YES + return + + index = next(self.slice.infer(context)) + if index is YES: + yield YES + return + + if isinstance(index, nodes.Const): + try: + assigned = value.getitem(index.value, context) + except AttributeError: + raise InferenceError() + except (IndexError, TypeError): + yield YES + return + for infered in assigned.infer(context): + yield infered + else: + raise InferenceError() +nodes.Subscript._infer = path_wrapper(infer_subscript) +nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript) + +def infer_unaryop(self, context=None): + for operand in self.operand.infer(context): + try: + yield operand.infer_unary_op(self.op) + except TypeError: + continue + except AttributeError: + meth = UNARY_OP_METHOD[self.op] + if meth is None: + yield YES + else: + try: + # XXX just suppose if the type implement meth, returned type + # will be the same + operand.getattr(meth) + yield operand + except GeneratorExit: + raise + except: + yield YES +nodes.UnaryOp._infer = path_wrapper(infer_unaryop) + +def _infer_binop(operator, operand1, operand2, context, failures=None): + if operand1 is YES: + yield operand1 + return + try: + for valnode in operand1.infer_binary_op(operator, operand2, context): + yield valnode + except AttributeError: + try: + # XXX just suppose if the type implement meth, returned type + # will be the same + operand1.getattr(BIN_OP_METHOD[operator]) + yield operand1 + except: + if failures is None: + yield YES + else: + failures.append(operand1) + +def infer_binop(self, context=None): + failures = [] + for lhs in self.left.infer(context): + for val in _infer_binop(self.op, lhs, self.right, context, failures): + yield val + for lhs in failures: + for rhs in self.right.infer(context): + for val in _infer_binop(self.op, rhs, lhs, context): + yield val +nodes.BinOp._infer = path_wrapper(infer_binop) + + +def infer_arguments(self, context=None, lookupname=None): + if lookupname is None: + raise InferenceError() + return _arguments_infer_argname(self, lookupname, context) +nodes.Arguments._infer = infer_arguments + + +def infer_ass(self, context=None): + """infer a AssName/AssAttr: need to inspect the RHS part of the + assign node + """ + stmt = self.statement() + if isinstance(stmt, nodes.AugAssign): + return stmt.infer(context) + stmts = list(self.assigned_stmts(context=context)) + return _infer_stmts(stmts, context) +nodes.AssName._infer = path_wrapper(infer_ass) +nodes.AssAttr._infer = path_wrapper(infer_ass) + +def infer_augassign(self, context=None): + failures = [] + for lhs in self.target.infer_lhs(context): + for val in _infer_binop(self.op, lhs, self.value, context, failures): + yield val + for lhs in failures: + for rhs in self.value.infer(context): + for val in _infer_binop(self.op, rhs, lhs, context): + yield val +nodes.AugAssign._infer = path_wrapper(infer_augassign) + + +# no infer method on DelName and DelAttr (expected InferenceError) + + +def infer_empty_node(self, context=None): + if not self.has_underlying_object(): + yield YES + else: + try: + for infered in MANAGER.infer_ast_from_something(self.object, + context=context): + yield infered + except AstroidError: + yield YES +nodes.EmptyNode._infer = path_wrapper(infer_empty_node) + + +def infer_index(self, context=None): + return self.value.infer(context) +nodes.Index._infer = infer_index diff --git a/astroid/inspector.py b/astroid/inspector.py new file mode 100644 index 0000000..1fc3192 --- /dev/null +++ b/astroid/inspector.py @@ -0,0 +1,273 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""visitor doing some postprocessing on the astroid tree. +Try to resolve definitions (namespace) dictionary, relationship... + +This module has been imported from pyreverse +""" + +__docformat__ = "restructuredtext en" + +from os.path import dirname + +import astroid +from astroid.exceptions import InferenceError +from astroid.utils import LocalsVisitor +from astroid.modutils import get_module_part, is_relative, is_standard_module + +class IdGeneratorMixIn(object): + """ + Mixin adding the ability to generate integer uid + """ + def __init__(self, start_value=0): + self.id_count = start_value + + def init_counter(self, start_value=0): + """init the id counter + """ + self.id_count = start_value + + def generate_id(self): + """generate a new identifier + """ + self.id_count += 1 + return self.id_count + + +class Linker(IdGeneratorMixIn, LocalsVisitor): + """ + walk on the project tree and resolve relationships. + + According to options the following attributes may be added to visited nodes: + + * uid, + a unique identifier for the node (on astroid.Project, astroid.Module, + astroid.Class and astroid.locals_type). Only if the linker has been instantiated + with tag=True parameter (False by default). + + * Function + a mapping from locals names to their bounded value, which may be a + constant like a string or an integer, or an astroid node (on astroid.Module, + astroid.Class and astroid.Function). + + * instance_attrs_type + as locals_type but for klass member attributes (only on astroid.Class) + + * implements, + list of implemented interface _objects_ (only on astroid.Class nodes) + """ + + def __init__(self, project, inherited_interfaces=0, tag=False): + IdGeneratorMixIn.__init__(self) + LocalsVisitor.__init__(self) + # take inherited interface in consideration or not + self.inherited_interfaces = inherited_interfaces + # tag nodes or not + self.tag = tag + # visited project + self.project = project + + + def visit_project(self, node): + """visit an astroid.Project node + + * optionally tag the node with a unique id + """ + if self.tag: + node.uid = self.generate_id() + for module in node.modules: + self.visit(module) + + def visit_package(self, node): + """visit an astroid.Package node + + * optionally tag the node with a unique id + """ + if self.tag: + node.uid = self.generate_id() + for subelmt in node.values(): + self.visit(subelmt) + + def visit_module(self, node): + """visit an astroid.Module node + + * set the locals_type mapping + * set the depends mapping + * optionally tag the node with a unique id + """ + if hasattr(node, 'locals_type'): + return + node.locals_type = {} + node.depends = [] + if self.tag: + node.uid = self.generate_id() + + def visit_class(self, node): + """visit an astroid.Class node + + * set the locals_type and instance_attrs_type mappings + * set the implements list and build it + * optionally tag the node with a unique id + """ + if hasattr(node, 'locals_type'): + return + node.locals_type = {} + if self.tag: + node.uid = self.generate_id() + # resolve ancestors + for baseobj in node.ancestors(recurs=False): + specializations = getattr(baseobj, 'specializations', []) + specializations.append(node) + baseobj.specializations = specializations + # resolve instance attributes + node.instance_attrs_type = {} + for assattrs in node.instance_attrs.values(): + for assattr in assattrs: + self.handle_assattr_type(assattr, node) + # resolve implemented interface + try: + node.implements = list(node.interfaces(self.inherited_interfaces)) + except InferenceError: + node.implements = () + + def visit_function(self, node): + """visit an astroid.Function node + + * set the locals_type mapping + * optionally tag the node with a unique id + """ + if hasattr(node, 'locals_type'): + return + node.locals_type = {} + if self.tag: + node.uid = self.generate_id() + + link_project = visit_project + link_module = visit_module + link_class = visit_class + link_function = visit_function + + def visit_assname(self, node): + """visit an astroid.AssName node + + handle locals_type + """ + # avoid double parsing done by different Linkers.visit + # running over the same project: + if hasattr(node, '_handled'): + return + node._handled = True + if node.name in node.frame(): + frame = node.frame() + else: + # the name has been defined as 'global' in the frame and belongs + # there. Btw the frame is not yet visited as the name is in the + # root locals; the frame hence has no locals_type attribute + frame = node.root() + try: + values = node.infered() + try: + already_infered = frame.locals_type[node.name] + for valnode in values: + if not valnode in already_infered: + already_infered.append(valnode) + except KeyError: + frame.locals_type[node.name] = values + except astroid.InferenceError: + pass + + def handle_assattr_type(self, node, parent): + """handle an astroid.AssAttr node + + handle instance_attrs_type + """ + try: + values = list(node.infer()) + try: + already_infered = parent.instance_attrs_type[node.attrname] + for valnode in values: + if not valnode in already_infered: + already_infered.append(valnode) + except KeyError: + parent.instance_attrs_type[node.attrname] = values + except astroid.InferenceError: + pass + + def visit_import(self, node): + """visit an astroid.Import node + + resolve module dependencies + """ + context_file = node.root().file + for name in node.names: + relative = is_relative(name[0], context_file) + self._imported_module(node, name[0], relative) + + + def visit_from(self, node): + """visit an astroid.From node + + resolve module dependencies + """ + basename = node.modname + context_file = node.root().file + if context_file is not None: + relative = is_relative(basename, context_file) + else: + relative = False + for name in node.names: + if name[0] == '*': + continue + # analyze dependencies + fullname = '%s.%s' % (basename, name[0]) + if fullname.find('.') > -1: + try: + # XXX: don't use get_module_part, missing package precedence + fullname = get_module_part(fullname, context_file) + except ImportError: + continue + if fullname != basename: + self._imported_module(node, fullname, relative) + + + def compute_module(self, context_name, mod_path): + """return true if the module should be added to dependencies""" + package_dir = dirname(self.project.path) + if context_name == mod_path: + return 0 + elif is_standard_module(mod_path, (package_dir,)): + return 1 + return 0 + + # protected methods ######################################################## + + def _imported_module(self, node, mod_path, relative): + """notify an imported module, used to analyze dependencies + """ + module = node.root() + context_name = module.name + if relative: + mod_path = '%s.%s' % ('.'.join(context_name.split('.')[:-1]), + mod_path) + if self.compute_module(context_name, mod_path): + # handle dependencies + if not hasattr(module, 'depends'): + module.depends = [] + mod_paths = module.depends + if not mod_path in mod_paths: + mod_paths.append(mod_path) diff --git a/astroid/manager.py b/astroid/manager.py new file mode 100644 index 0000000..e1660e4 --- /dev/null +++ b/astroid/manager.py @@ -0,0 +1,348 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""astroid manager: avoid multiple astroid build of a same module when +possible by providing a class responsible to get astroid representation +from various source and using a cache of built modules) +""" +from __future__ import print_function + +__docformat__ = "restructuredtext en" + +import os +from os.path import dirname, join, isdir, exists +from warnings import warn + +from logilab.common.configuration import OptionsProviderMixIn + +from astroid.exceptions import AstroidBuildingException +from astroid.modutils import NoSourceFile, is_python_source, \ + file_from_modpath, load_module_from_name, modpath_from_file, \ + get_module_files, get_source_file, zipimport + + +def astroid_wrapper(func, modname): + """wrapper to give to AstroidManager.project_from_files""" + print('parsing %s...' % modname) + try: + return func(modname) + except AstroidBuildingException as exc: + print(exc) + except Exception as 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) + except: + return '???' + + + +class AstroidManager(OptionsProviderMixIn): + """the astroid manager, responsible to build astroid from files + or modules. + + Use the Borg pattern. + """ + + name = 'astroid loader' + options = (("ignore", + {'type' : "csv", 'metavar' : "", + 'dest' : "black_list", "default" : ('CVS',), + 'help' : "add (may be a directory) to the black list\ +. It should be a base name, not a path. You may set this option multiple times\ +."}), + ("project", + {'default': "No Name", 'type' : 'string', 'short': 'p', + 'metavar' : '', + 'help' : 'set the project name.'}), + ) + brain = {} + def __init__(self): + self.__dict__ = AstroidManager.brain + if not self.__dict__: + OptionsProviderMixIn.__init__(self) + self.load_defaults() + # NOTE: cache entries are added by the [re]builder + self.astroid_cache = {} + self._mod_file_cache = {} + self.transforms = {} + + def ast_from_file(self, filepath, modname=None, fallback=True, source=False): + """given a module name, return the astroid object""" + try: + filepath = get_source_file(filepath, include_no_ext=True) + source = True + except NoSourceFile: + pass + if modname is None: + try: + modname = '.'.join(modpath_from_file(filepath)) + except ImportError: + modname = filepath + if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath: + return self.astroid_cache[modname] + if source: + from astroid.builder import AstroidBuilder + return AstroidBuilder(self).file_build(filepath, modname) + elif fallback and modname: + return self.ast_from_module_name(modname) + raise AstroidBuildingException('unable to get astroid for file %s' % + filepath) + + def ast_from_module_name(self, modname, context_file=None): + """given a module name, return the astroid object""" + if modname in self.astroid_cache: + return self.astroid_cache[modname] + if modname == '__main__': + from astroid.builder import AstroidBuilder + return AstroidBuilder(self).string_build('', modname) + old_cwd = os.getcwd() + if context_file: + os.chdir(dirname(context_file)) + try: + filepath = self.file_from_module_name(modname, context_file) + if filepath is not None and not is_python_source(filepath): + module = self.zip_import_data(filepath) + if module is not None: + return module + if filepath is None or not is_python_source(filepath): + try: + module = load_module_from_name(modname) + except Exception as ex: + msg = 'Unable to load module %s (%s)' % (modname, ex) + raise AstroidBuildingException(msg) + return self.ast_from_module(module, modname) + return self.ast_from_file(filepath, modname, fallback=False) + finally: + os.chdir(old_cwd) + + def zip_import_data(self, filepath): + if zipimport is None: + return None + from astroid.builder import AstroidBuilder + builder = AstroidBuilder(self) + for ext in ('.zip', '.egg'): + try: + eggpath, resource = filepath.rsplit(ext + '/', 1) + except ValueError: + continue + try: + importer = zipimport.zipimporter(eggpath + ext) + zmodname = resource.replace('/', '.') + if importer.is_package(resource): + zmodname = zmodname + '.__init__' + module = builder.string_build(importer.get_source(resource), + zmodname, filepath) + return module + except: + continue + return None + + def file_from_module_name(self, modname, contextfile): + try: + value = self._mod_file_cache[(modname, contextfile)] + except KeyError: + try: + value = file_from_modpath(modname.split('.'), + context_file=contextfile) + except ImportError as ex: + msg = 'Unable to load module %s (%s)' % (modname, ex) + value = AstroidBuildingException(msg) + self._mod_file_cache[(modname, contextfile)] = value + if isinstance(value, AstroidBuildingException): + raise value + return value + + def ast_from_module(self, module, modname=None): + """given an imported module, return the astroid object""" + modname = modname or module.__name__ + if modname in self.astroid_cache: + return self.astroid_cache[modname] + try: + # some builtin modules don't have __file__ attribute + filepath = module.__file__ + if is_python_source(filepath): + return self.ast_from_file(filepath, modname) + except AttributeError: + pass + from astroid.builder import AstroidBuilder + return AstroidBuilder(self).module_build(module, modname) + + def ast_from_class(self, klass, modname=None): + """get astroid for the given class""" + if modname is None: + try: + modname = klass.__module__ + except AttributeError: + raise AstroidBuildingException( + 'Unable to get module for class %s' % safe_repr(klass)) + modastroid = self.ast_from_module_name(modname) + return modastroid.getattr(klass.__name__)[0] # XXX + + + def infer_ast_from_something(self, obj, context=None): + """infer astroid for the given class""" + if hasattr(obj, '__class__') and not isinstance(obj, type): + klass = obj.__class__ + else: + klass = obj + try: + modname = klass.__module__ + except AttributeError: + raise AstroidBuildingException( + 'Unable to get module for %s' % safe_repr(klass)) + except Exception as ex: + raise AstroidBuildingException( + 'Unexpected error while retrieving module for %s: %s' + % (safe_repr(klass), ex)) + try: + name = klass.__name__ + except AttributeError: + raise AstroidBuildingException( + 'Unable to get name for %s' % safe_repr(klass)) + except Exception as ex: + raise AstroidBuildingException( + 'Unexpected error while retrieving name for %s: %s' + % (safe_repr(klass), ex)) + # take care, on living object __module__ is regularly wrong :( + modastroid = self.ast_from_module_name(modname) + if klass is obj: + for infered in modastroid.igetattr(name, context): + yield infered + else: + for infered in modastroid.igetattr(name, context): + yield infered.instanciate_class() + + def project_from_files(self, files, func_wrapper=astroid_wrapper, + project_name=None, black_list=None): + """return a Project from a list of files or modules""" + # build the project representation + project_name = project_name or self.config.project + black_list = black_list or self.config.black_list + project = Project(project_name) + for something in files: + if not exists(something): + fpath = file_from_modpath(something.split('.')) + elif isdir(something): + fpath = join(something, '__init__.py') + else: + fpath = something + astroid = func_wrapper(self.ast_from_file, fpath) + if astroid is None: + continue + # XXX why is first file defining the project.path ? + project.path = project.path or astroid.file + project.add_module(astroid) + base_name = astroid.name + # recurse in package except if __init__ was explicitly given + if astroid.package and something.find('__init__') == -1: + # recurse on others packages / modules if this is a package + for fpath in get_module_files(dirname(astroid.file), + black_list): + astroid = func_wrapper(self.ast_from_file, fpath) + if astroid is None or astroid.name == base_name: + continue + project.add_module(astroid) + return project + + def register_transform(self, node_class, transform, predicate=None): + """Register `transform(node)` function to be applied on the given + Astroid's `node_class` if `predicate` is None or return a true value + when called with the node as argument. + + The transform function may return a value which is then used to + substitute the original node in the tree. + """ + self.transforms.setdefault(node_class, []).append((transform, predicate)) + + def unregister_transform(self, node_class, transform, predicate=None): + """Unregister the given transform.""" + self.transforms[node_class].remove((transform, predicate)) + + def transform(self, node): + """Call matching transforms for the given node if any and return the + transformed node. + """ + cls = node.__class__ + if cls not in self.transforms: + # no transform registered for this class of node + return node + + transforms = self.transforms[cls] + orig_node = node # copy the reference + for transform_func, predicate in transforms: + if predicate is None or predicate(node): + ret = transform_func(node) + # if the transformation function returns something, it's + # expected to be a replacement for the node + if ret is not None: + if node is not orig_node: + # node has already be modified by some previous + # transformation, warn about it + warn('node %s substituted multiple times' % node) + node = ret + return node + + def cache_module(self, module): + """Cache a module if no module with the same name is known yet.""" + self.astroid_cache.setdefault(module.name, module) + + def clear_cache(self): + self.astroid_cache.clear() + # force bootstrap again, else we may ends up with cache inconsistency + # between the manager and CONST_PROXY, making + # unittest_lookup.LookupTC.test_builtin_lookup fail depending on the + # test order + from astroid.raw_building import astroid_bootstrapping + astroid_bootstrapping() + + +class Project(object): + """a project handle a set of modules / packages""" + def __init__(self, name=''): + self.name = name + self.path = None + self.modules = [] + self.locals = {} + self.__getitem__ = self.locals.__getitem__ + self.__iter__ = self.locals.__iter__ + self.values = self.locals.values + self.keys = self.locals.keys + self.items = self.locals.items + + def add_module(self, node): + self.locals[node.name] = node + self.modules.append(node) + + def get_module(self, name): + return self.locals[name] + + def get_children(self): + return self.modules + + def __repr__(self): + return '' % (self.name, id(self), + len(self.modules)) + + diff --git a/astroid/mixins.py b/astroid/mixins.py new file mode 100644 index 0000000..dbf1673 --- /dev/null +++ b/astroid/mixins.py @@ -0,0 +1,124 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""This module contains some mixins for the different nodes. +""" + +from logilab.common.decorators import cachedproperty + +from astroid.exceptions import (AstroidBuildingException, InferenceError, + NotFoundError) + + +class BlockRangeMixIn(object): + """override block range """ + + @cachedproperty + def blockstart_tolineno(self): + return self.lineno + + def _elsed_block_range(self, lineno, orelse, last=None): + """handle block line numbers range for try/finally, for, if and while + statements + """ + if lineno == self.fromlineno: + return lineno, lineno + if orelse: + if lineno >= orelse[0].fromlineno: + return lineno, orelse[-1].tolineno + return lineno, orelse[0].fromlineno - 1 + return lineno, last or self.tolineno + + +class FilterStmtsMixin(object): + """Mixin for statement filtering and assignment type""" + + def _get_filtered_stmts(self, _, node, _stmts, mystmt): + """method used in _filter_stmts to get statemtents and trigger break""" + if self.statement() is mystmt: + # original node's statement is the assignment, only keep + # current node (gen exp, list comp) + return [node], True + return _stmts, False + + def ass_type(self): + return self + + +class AssignTypeMixin(object): + + def ass_type(self): + return self + + def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt): + """method used in filter_stmts""" + if self is mystmt: + return _stmts, True + if self.statement() is mystmt: + # original node's statement is the assignment, only keep + # current node (gen exp, list comp) + return [node], True + return _stmts, False + + +class ParentAssignTypeMixin(AssignTypeMixin): + + def ass_type(self): + return self.parent.ass_type() + + +class FromImportMixIn(FilterStmtsMixin): + """MixIn for From and Import Nodes""" + + def _infer_name(self, frame, name): + return name + + def do_import_module(self, modname=None): + """return the ast for a module whose name is imported by + """ + # handle special case where we are on a package node importing a module + # using the same name as the package, which may end in an infinite loop + # on relative imports + # XXX: no more needed ? + mymodule = self.root() + level = getattr(self, 'level', None) # Import as no level + if modname is None: + modname = self.modname + # XXX we should investigate deeper if we really want to check + # importing itself: modname and mymodule.name be relative or absolute + if mymodule.relative_to_absolute_name(modname, level) == mymodule.name: + # FIXME: we used to raise InferenceError here, but why ? + return mymodule + try: + return mymodule.import_module(modname, level=level) + except AstroidBuildingException: + raise InferenceError(modname) + except SyntaxError as ex: + raise InferenceError(str(ex)) + + def real_name(self, asname): + """get name from 'as' name""" + for name, _asname in self.names: + if name == '*': + return asname + if not _asname: + name = name.split('.', 1)[0] + _asname = name + if asname == _asname: + return name + raise NotFoundError(asname) + diff --git a/astroid/modutils.py b/astroid/modutils.py new file mode 100644 index 0000000..efc999b --- /dev/null +++ b/astroid/modutils.py @@ -0,0 +1,638 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""Python modules manipulation utility functions. + +:type PY_SOURCE_EXTS: tuple(str) +:var PY_SOURCE_EXTS: list of possible python source file extension + +:type STD_LIB_DIR: str +:var STD_LIB_DIR: directory where standard modules are located + +:type BUILTIN_MODULES: dict +:var BUILTIN_MODULES: dictionary with builtin module names has key +""" +from __future__ import with_statement + +__docformat__ = "restructuredtext en" + +import sys +import os +from os.path import splitext, join, abspath, isdir, dirname, exists +from imp import find_module, load_module, C_BUILTIN, PY_COMPILED, PKG_DIRECTORY +from distutils.sysconfig import get_python_lib +from distutils.errors import DistutilsPlatformError + +try: + import zipimport +except ImportError: + zipimport = None + +ZIPFILE = object() + +from logilab.common import _handle_blacklist + +# Notes about STD_LIB_DIR +# Consider arch-specific installation for STD_LIB_DIR definition +# :mod:`distutils.sysconfig` contains to much hardcoded values to rely on +# +# :see: `Problems with /usr/lib64 builds `_ +# :see: `FHS `_ +if sys.platform.startswith('win'): + PY_SOURCE_EXTS = ('py', 'pyw') + PY_COMPILED_EXTS = ('dll', 'pyd') +else: + PY_SOURCE_EXTS = ('py',) + PY_COMPILED_EXTS = ('so',) + +try: + STD_LIB_DIR = get_python_lib(standard_lib=1) +# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to +# non-valid path, see https://bugs.pypy.org/issue1164 +except DistutilsPlatformError: + STD_LIB_DIR = '//' + +EXT_LIB_DIR = get_python_lib() + +BUILTIN_MODULES = dict(zip(sys.builtin_module_names, + [1]*len(sys.builtin_module_names))) + + +class NoSourceFile(Exception): + """exception raised when we are not able to get a python + source file for a precompiled file + """ + + +def load_module_from_name(dotted_name, path=None, use_sys=1): + """Load a Python module from its name. + + :type dotted_name: str + :param dotted_name: python name of a module or package + + :type path: list or None + :param path: + optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :type use_sys: bool + :param use_sys: + boolean indicating whether the sys.modules dictionary should be + used or not + + + :raise ImportError: if the module or package is not found + + :rtype: module + :return: the loaded module + """ + return load_module_from_modpath(dotted_name.split('.'), path, use_sys) + + +def load_module_from_modpath(parts, path=None, use_sys=1): + """Load a python module from its splitted name. + + :type parts: list(str) or tuple(str) + :param parts: + python name of a module or package splitted on '.' + + :type path: list or None + :param path: + optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :type use_sys: bool + :param use_sys: + boolean indicating whether the sys.modules dictionary should be used or not + + :raise ImportError: if the module or package is not found + + :rtype: module + :return: the loaded module + """ + if use_sys: + try: + return sys.modules['.'.join(parts)] + except KeyError: + pass + modpath = [] + prevmodule = None + for part in parts: + modpath.append(part) + curname = '.'.join(modpath) + module = None + if len(modpath) != len(parts): + # even with use_sys=False, should try to get outer packages from sys.modules + module = sys.modules.get(curname) + elif use_sys: + # because it may have been indirectly loaded through a parent + module = sys.modules.get(curname) + if module is None: + mp_file, mp_filename, mp_desc = find_module(part, path) + module = load_module(curname, mp_file, mp_filename, mp_desc) + if prevmodule: + setattr(prevmodule, part, module) + _file = getattr(module, '__file__', '') + if not _file and len(modpath) != len(parts): + raise ImportError('no module in %s' % '.'.join(parts[len(modpath):])) + path = [dirname(_file)] + prevmodule = module + return module + + +def load_module_from_file(filepath, path=None, use_sys=1, extrapath=None): + """Load a Python module from it's path. + + :type filepath: str + :param filepath: path to the python module or package + + :type path: list or None + :param path: + optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :type use_sys: bool + :param use_sys: + boolean indicating whether the sys.modules dictionary should be + used or not + + + :raise ImportError: if the module or package is not found + + :rtype: module + :return: the loaded module + """ + modpath = modpath_from_file(filepath, extrapath) + return load_module_from_modpath(modpath, path, use_sys) + + +def _check_init(path, mod_path): + """check there are some __init__.py all along the way""" + for part in mod_path: + path = join(path, part) + if not _has_init(path): + return False + return True + + +def modpath_from_file(filename, extrapath=None): + """given a file path return the corresponding splitted module's name + (i.e name of a module or package splitted on '.') + + :type filename: str + :param filename: file's path for which we want the module's name + + :type extrapath: dict + :param extrapath: + optional extra search path, with path as key and package name for the path + as value. This is usually useful to handle package splitted in multiple + directories using __path__ trick. + + + :raise ImportError: + if the corresponding module's name has not been found + + :rtype: list(str) + :return: the corresponding splitted module's name + """ + base = splitext(abspath(filename))[0] + if extrapath is not None: + for path_ in extrapath: + path = _abspath(path_) + if path and base[:len(path)] == path: + submodpath = [pkg for pkg in base[len(path):].split(os.sep) + if pkg] + if _check_init(path, submodpath[:-1]): + return extrapath[path_].split('.') + submodpath + for path in sys.path: + path = _abspath(path) + if path and base.startswith(path): + modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg] + if _check_init(path, modpath[:-1]): + return modpath + raise ImportError('Unable to find module for %s in %s' % ( + filename, ', \n'.join(sys.path))) + + + +def file_from_modpath(modpath, path=None, context_file=None): + """given a mod path (i.e. splitted module / package name), return the + corresponding file, giving priority to source file over precompiled + file if it exists + + :type modpath: list or tuple + :param modpath: + splitted module's name (i.e name of a module or package splitted + on '.') + (this means explicit relative imports that start with dots have + empty strings in this list!) + + :type path: list or None + :param path: + optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :type context_file: str or None + :param context_file: + context file to consider, necessary if the identifier has been + introduced using a relative import unresolvable in the actual + context (i.e. modutils) + + :raise ImportError: if there is no such module in the directory + + :rtype: str or None + :return: + the path to the module's file or None if it's an integrated + builtin module such as 'sys' + """ + if context_file is not None: + context = dirname(context_file) + else: + context = context_file + if modpath[0] == 'xml': + # handle _xmlplus + try: + return _file_from_modpath(['_xmlplus'] + modpath[1:], path, context) + except ImportError: + return _file_from_modpath(modpath, path, context) + elif modpath == ['os', 'path']: + # FIXME: currently ignoring search_path... + return os.path.__file__ + return _file_from_modpath(modpath, path, context) + + + +def get_module_part(dotted_name, context_file=None): + """given a dotted name return the module part of the name : + + >>> get_module_part('logilab.common.modutils.get_module_part') + 'logilab.common.modutils' + + :type dotted_name: str + :param dotted_name: full name of the identifier we are interested in + + :type context_file: str or None + :param context_file: + context file to consider, necessary if the identifier has been + introduced using a relative import unresolvable in the actual + context (i.e. modutils) + + + :raise ImportError: if there is no such module in the directory + + :rtype: str or None + :return: + the module part of the name or None if we have not been able at + all to import the given name + + XXX: deprecated, since it doesn't handle package precedence over module + (see #10066) + """ + # os.path trick + if dotted_name.startswith('os.path'): + return 'os.path' + parts = dotted_name.split('.') + if context_file is not None: + # first check for builtin module which won't be considered latter + # in that case (path != None) + if parts[0] in BUILTIN_MODULES: + if len(parts) > 2: + raise ImportError(dotted_name) + return parts[0] + # don't use += or insert, we want a new list to be created ! + path = None + starti = 0 + if parts[0] == '': + assert context_file is not None, \ + 'explicit relative import, but no context_file?' + path = [] # prevent resolving the import non-relatively + starti = 1 + while parts[starti] == '': # for all further dots: change context + starti += 1 + context_file = dirname(context_file) + for i in range(starti, len(parts)): + try: + file_from_modpath(parts[starti:i+1], path=path, + context_file=context_file) + except ImportError: + if not i >= max(1, len(parts) - 2): + raise + return '.'.join(parts[:i]) + return dotted_name + + +def get_module_files(src_directory, blacklist): + """given a package directory return a list of all available python + module's files in the package and its subpackages + + :type src_directory: str + :param src_directory: + path of the directory corresponding to the package + + :type blacklist: list or tuple + :param blacklist: + optional list of files or directory to ignore, default to the value of + `logilab.common.STD_BLACKLIST` + + :rtype: list + :return: + the list of all available python module's files in the package and + its subpackages + """ + files = [] + for directory, dirnames, filenames in os.walk(src_directory): + _handle_blacklist(blacklist, dirnames, filenames) + # check for __init__.py + if not '__init__.py' in filenames: + dirnames[:] = () + continue + for filename in filenames: + if _is_python_file(filename): + src = join(directory, filename) + files.append(src) + return files + + +def get_source_file(filename, include_no_ext=False): + """given a python module's file name return the matching source file + name (the filename will be returned identically if it's a already an + absolute path to a python source file...) + + :type filename: str + :param filename: python module's file name + + + :raise NoSourceFile: if no source file exists on the file system + + :rtype: str + :return: the absolute path of the source file if it exists + """ + base, orig_ext = splitext(abspath(filename)) + for ext in PY_SOURCE_EXTS: + source_path = '%s.%s' % (base, ext) + if exists(source_path): + return source_path + if include_no_ext and not orig_ext and exists(base): + return base + raise NoSourceFile(filename) + + +def is_python_source(filename): + """ + rtype: bool + return: True if the filename is a python source file + """ + return splitext(filename)[1][1:] in PY_SOURCE_EXTS + + +def is_standard_module(modname, std_path=(STD_LIB_DIR,)): + """try to guess if a module is a standard python module (by default, + see `std_path` parameter's description) + + :type modname: str + :param modname: name of the module we are interested in + + :type std_path: list(str) or tuple(str) + :param std_path: list of path considered has standard + + + :rtype: bool + :return: + true if the module: + - is located on the path listed in one of the directory in `std_path` + - is a built-in module + """ + modname = modname.split('.')[0] + try: + filename = file_from_modpath([modname]) + except ImportError: + # import failed, i'm probably not so wrong by supposing it's + # not standard... + return False + # modules which are not living in a file are considered standard + # (sys and __builtin__ for instance) + if filename is None: + return True + filename = abspath(filename) + if filename.startswith(EXT_LIB_DIR): + return False + for path in std_path: + if filename.startswith(_abspath(path)): + return True + return False + + + +def is_relative(modname, from_file): + """return true if the given module name is relative to the given + file name + + :type modname: str + :param modname: name of the module we are interested in + + :type from_file: str + :param from_file: + path of the module from which modname has been imported + + :rtype: bool + :return: + true if the module has been imported relatively to `from_file` + """ + if not isdir(from_file): + from_file = dirname(from_file) + if from_file in sys.path: + return False + try: + find_module(modname.split('.')[0], [from_file]) + return True + except ImportError: + return False + + +# internal only functions ##################################################### + +def _file_from_modpath(modpath, path=None, context=None): + """given a mod path (i.e. splitted module / package name), return the + corresponding file + + this function is used internally, see `file_from_modpath`'s + documentation for more information + """ + assert len(modpath) > 0 + if context is not None: + try: + mtype, mp_filename = _module_file(modpath, [context]) + except ImportError: + mtype, mp_filename = _module_file(modpath, path) + else: + mtype, mp_filename = _module_file(modpath, path) + if mtype == PY_COMPILED: + try: + return get_source_file(mp_filename) + except NoSourceFile: + return mp_filename + elif mtype == C_BUILTIN: + # integrated builtin module + return None + elif mtype == PKG_DIRECTORY: + mp_filename = _has_init(mp_filename) + return mp_filename + +def _search_zip(modpath, pic): + for filepath, importer in pic.items(): + if importer is not None: + if importer.find_module(modpath[0]): + if not importer.find_module(os.path.sep.join(modpath)): + raise ImportError('No module named %s in %s/%s' % ( + '.'.join(modpath[1:]), filepath, modpath)) + return ZIPFILE, abspath(filepath) + os.path.sep + os.path.sep.join(modpath), filepath + raise ImportError('No module named %s' % '.'.join(modpath)) + + +def _abspath(path, _abspathcache={}): #pylint: disable=dangerous-default-value + """abspath with caching""" + # _module_file calls abspath on every path in sys.path every time it's + # called; on a larger codebase this easily adds up to half a second just + # assembling path components. This cache alleviates that. + try: + return _abspathcache[path] + except KeyError: + if not path: # don't cache result for '' + return abspath(path) + _abspathcache[path] = abspath(path) + return _abspathcache[path] + +try: + import pkg_resources +except ImportError: + pkg_resources = None + +def _module_file(modpath, path=None): + """get a module type / file path + + :type modpath: list or tuple + :param modpath: + splitted module's name (i.e name of a module or package splitted + on '.'), with leading empty strings for explicit relative import + + :type path: list or None + :param path: + optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + + :rtype: tuple(int, str) + :return: the module type flag and the file path for a module + """ + # egg support compat + try: + pic = sys.path_importer_cache + _path = (path is None and sys.path or path) + for __path in _path: + if not __path in pic: + try: + pic[__path] = zipimport.zipimporter(__path) + except zipimport.ZipImportError: + pic[__path] = None + checkeggs = True + except AttributeError: + checkeggs = False + # pkg_resources support (aka setuptools namespace packages) + if (pkg_resources is not None + and modpath[0] in pkg_resources._namespace_packages + and modpath[0] in sys.modules + and len(modpath) > 1): + # setuptools has added into sys.modules a module object with proper + # __path__, get back information from there + module = sys.modules[modpath.pop(0)] + path = module.__path__ + imported = [] + while modpath: + modname = modpath[0] + # take care to changes in find_module implementation wrt builtin modules + # + # Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23) + # >>> imp.find_module('posix') + # (None, 'posix', ('', '', 6)) + # + # Python 3.3.1 (default, Apr 26 2013, 12:08:46) + # >>> imp.find_module('posix') + # (None, None, ('', '', 6)) + try: + _, mp_filename, mp_desc = find_module(modname, path) + except ImportError: + if checkeggs: + return _search_zip(modpath, pic)[:2] + raise + else: + if checkeggs and mp_filename: + fullabspath = [_abspath(x) for x in _path] + try: + pathindex = fullabspath.index(dirname(abspath(mp_filename))) + emtype, emp_filename, zippath = _search_zip(modpath, pic) + if pathindex > _path.index(zippath): + # an egg takes priority + return emtype, emp_filename + except ValueError: + # XXX not in _path + pass + except ImportError: + pass + checkeggs = False + imported.append(modpath.pop(0)) + mtype = mp_desc[2] + if modpath: + if mtype != PKG_DIRECTORY: + raise ImportError('No module %s in %s' % ('.'.join(modpath), + '.'.join(imported))) + # XXX guess if package is using pkgutil.extend_path by looking for + # those keywords in the first four Kbytes + try: + with open(join(mp_filename, '__init__.py'), 'rb') as stream: + data = stream.read(4096) + except IOError: + path = [mp_filename] + else: + if b'pkgutil' in data and b'extend_path' in data: + # extend_path is called, search sys.path for module/packages + # of this name see pkgutil.extend_path documentation + path = [join(p, *imported) for p in sys.path + if isdir(join(p, *imported))] + else: + path = [mp_filename] + return mtype, mp_filename + +def _is_python_file(filename): + """return true if the given filename should be considered as a python file + + .pyc and .pyo are ignored + """ + for ext in ('.py', '.so', '.pyd', '.pyw'): + if filename.endswith(ext): + return True + return False + + +def _has_init(directory): + """if the given directory has a valid __init__ file, return its path, + else return None + """ + mod_or_pack = join(directory, '__init__') + for ext in PY_SOURCE_EXTS + ('pyc', 'pyo'): + if exists(mod_or_pack + '.' + ext): + return mod_or_pack + '.' + ext + return None diff --git a/astroid/node_classes.py b/astroid/node_classes.py new file mode 100644 index 0000000..71e512f --- /dev/null +++ b/astroid/node_classes.py @@ -0,0 +1,965 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""Module for some node classes. More nodes in scoped_nodes.py +""" + +import sys + +import six +from logilab.common.decorators import cachedproperty + +from astroid.exceptions import NoDefault +from astroid.bases import (NodeNG, Statement, Instance, InferenceContext, + _infer_stmts, YES, BUILTINS) +from astroid.mixins import (BlockRangeMixIn, AssignTypeMixin, + ParentAssignTypeMixin, FromImportMixIn) + +PY3K = sys.version_info >= (3, 0) + + +def unpack_infer(stmt, context=None): + """recursively generate nodes inferred by the given statement. + If the inferred value is a list or a tuple, recurse on the elements + """ + if isinstance(stmt, (List, Tuple)): + for elt in stmt.elts: + for infered_elt in unpack_infer(elt, context): + yield infered_elt + return + # if infered is a final node, return it and stop + infered = next(stmt.infer(context)) + if infered is stmt: + yield infered + return + # else, infer recursivly, except YES object that should be returned as is + for infered in stmt.infer(context): + if infered is YES: + yield infered + else: + for inf_inf in unpack_infer(infered, context): + yield inf_inf + + +def are_exclusive(stmt1, stmt2, exceptions=None): + """return true if the two given statements are mutually exclusive + + `exceptions` may be a list of exception names. If specified, discard If + branches and check one of the statement is in an exception handler catching + one of the given exceptions. + + algorithm : + 1) index stmt1's parents + 2) climb among stmt2's parents until we find a common parent + 3) if the common parent is a If or TryExcept statement, look if nodes are + in exclusive branches + """ + # index stmt1's parents + stmt1_parents = {} + children = {} + node = stmt1.parent + previous = stmt1 + while node: + stmt1_parents[node] = 1 + children[node] = previous + previous = node + node = node.parent + # climb among stmt2's parents until we find a common parent + node = stmt2.parent + previous = stmt2 + while node: + if node in stmt1_parents: + # if the common parent is a If or TryExcept statement, look if + # nodes are in exclusive branches + if isinstance(node, If) and exceptions is None: + if (node.locate_child(previous)[1] + is not node.locate_child(children[node])[1]): + return True + elif isinstance(node, TryExcept): + c2attr, c2node = node.locate_child(previous) + c1attr, c1node = node.locate_child(children[node]) + if c1node is not c2node: + if ((c2attr == 'body' and c1attr == 'handlers' and children[node].catch(exceptions)) or + (c2attr == 'handlers' and c1attr == 'body' and previous.catch(exceptions)) or + (c2attr == 'handlers' and c1attr == 'orelse') or + (c2attr == 'orelse' and c1attr == 'handlers')): + return True + elif c2attr == 'handlers' and c1attr == 'handlers': + return previous is not children[node] + return False + previous = node + node = node.parent + return False + + +class LookupMixIn(object): + """Mixin looking up a name in the right scope + """ + + def lookup(self, name): + """lookup a variable name + + return the scope node and the list of assignments associated to the + given name according to the scope where it has been found (locals, + globals or builtin) + + The lookup is starting from self's scope. If self is not a frame itself + and the name is found in the inner frame locals, statements will be + filtered to remove ignorable statements according to self's location + """ + return self.scope().scope_lookup(self, name) + + 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) + return _infer_stmts(stmts, None, frame) + + def _filter_stmts(self, stmts, frame, offset): + """filter statements to remove ignorable statements. + + If self is not a frame itself and the name is found in the inner + frame locals, statements will be filtered to remove ignorable + statements according to self's location + """ + # if offset == -1, my actual frame is not the inner frame but its parent + # + # class A(B): pass + # + # we need this to resolve B correctly + if offset == -1: + myframe = self.frame().parent.frame() + else: + myframe = self.frame() + # If the frame of this node is the same as the statement + # of this node, then the node is part of a class or + # a function definition and the frame of this node should be the + # the upper frame, not the frame of the definition. + # For more information why this is important, + # see Pylint issue #295. + # For example, for 'b', the statement is the same + # as the frame / scope: + # + # def test(b=1): + # ... + + if self.statement() is myframe and myframe.parent: + myframe = myframe.parent.frame() + if not myframe is frame or self is frame: + return stmts + mystmt = self.statement() + # line filtering if we are in the same frame + # + # take care node may be missing lineno information (this is the case for + # nodes inserted for living objects) + if myframe is frame and mystmt.fromlineno is not None: + assert mystmt.fromlineno is not None, mystmt + mylineno = mystmt.fromlineno + offset + else: + # disabling lineno filtering + mylineno = 0 + _stmts = [] + _stmt_parents = [] + for node in stmts: + stmt = node.statement() + # line filtering is on and we have reached our location, break + if mylineno > 0 and stmt.fromlineno > mylineno: + break + assert hasattr(node, 'ass_type'), (node, node.scope(), + node.scope().locals) + ass_type = node.ass_type() + + if node.has_base(self): + break + + _stmts, done = ass_type._get_filtered_stmts(self, node, _stmts, mystmt) + if done: + break + + optional_assign = ass_type.optional_assign + if optional_assign and ass_type.parent_of(self): + # we are inside a loop, loop var assigment is hidding previous + # assigment + _stmts = [node] + _stmt_parents = [stmt.parent] + continue + + # XXX comment various branches below!!! + try: + pindex = _stmt_parents.index(stmt.parent) + except ValueError: + pass + else: + # we got a parent index, this means the currently visited node + # is at the same block level as a previously visited node + if _stmts[pindex].ass_type().parent_of(ass_type): + # both statements are not at the same block level + continue + # if currently visited node is following previously considered + # assignement and both are not exclusive, we can drop the + # previous one. For instance in the following code :: + # + # if a: + # x = 1 + # else: + # x = 2 + # print x + # + # we can't remove neither x = 1 nor x = 2 when looking for 'x' + # of 'print x'; while in the following :: + # + # x = 1 + # x = 2 + # print x + # + # we can remove x = 1 when we see x = 2 + # + # moreover, on loop assignment types, assignment won't + # necessarily be done if the loop has no iteration, so we don't + # want to clear previous assigments if any (hence the test on + # optional_assign) + if not (optional_assign or are_exclusive(_stmts[pindex], node)): + del _stmt_parents[pindex] + del _stmts[pindex] + if isinstance(node, AssName): + if not optional_assign and stmt.parent is mystmt.parent: + _stmts = [] + _stmt_parents = [] + elif isinstance(node, DelName): + _stmts = [] + _stmt_parents = [] + continue + if not are_exclusive(self, node): + _stmts.append(node) + _stmt_parents.append(stmt.parent) + return _stmts + +# Name classes + +class AssName(LookupMixIn, ParentAssignTypeMixin, NodeNG): + """class representing an AssName node""" + + +class DelName(LookupMixIn, ParentAssignTypeMixin, NodeNG): + """class representing a DelName node""" + + +class Name(LookupMixIn, NodeNG): + """class representing a Name node""" + + + + +##################### node classes ######################################## + +class Arguments(NodeNG, AssignTypeMixin): + """class representing an Arguments node""" + if PY3K: + # Python 3.4+ uses a different approach regarding annotations, + # each argument is a new class, _ast.arg, which exposes an + # 'annotation' attribute. In astroid though, arguments are exposed + # as is in the Arguments node and the only way to expose annotations + # is by using something similar with Python 3.3: + # - we expose 'varargannotation' and 'kwargannotation' of annotations + # of varargs and kwargs. + # - we expose 'annotation', a list with annotations for + # for each normal argument. If an argument doesn't have an + # annotation, its value will be None. + + _astroid_fields = ('args', 'defaults', 'kwonlyargs', + 'kw_defaults', 'annotations', + 'varargannotation', 'kwargannotation') + annotations = None + varargannotation = None + kwargannotation = None + else: + _astroid_fields = ('args', 'defaults', 'kwonlyargs', 'kw_defaults') + args = None + defaults = None + kwonlyargs = None + kw_defaults = None + + def __init__(self, vararg=None, kwarg=None): + self.vararg = vararg + self.kwarg = kwarg + + def _infer_name(self, frame, name): + if self.parent is frame: + return name + return None + + @cachedproperty + def fromlineno(self): + lineno = super(Arguments, self).fromlineno + return max(lineno, self.parent.fromlineno) + + def format_args(self): + """return arguments formatted as string""" + result = [] + if self.args: + result.append(_format_args(self.args, self.defaults)) + if self.vararg: + result.append('*%s' % self.vararg) + if self.kwarg: + result.append('**%s' % self.kwarg) + if self.kwonlyargs: + if not self.vararg: + result.append('*') + result.append(_format_args(self.kwonlyargs, self.kw_defaults)) + return ', '.join(result) + + def default_value(self, argname): + """return the default value for an argument + + :raise `NoDefault`: if there is no default value defined + """ + i = _find_arg(argname, self.args)[0] + if i is not None: + idx = i - (len(self.args) - len(self.defaults)) + if idx >= 0: + return self.defaults[idx] + i = _find_arg(argname, self.kwonlyargs)[0] + if i is not None and self.kw_defaults[i] is not None: + return self.kw_defaults[i] + raise NoDefault() + + def is_argument(self, name): + """return True if the name is defined in arguments""" + if name == self.vararg: + return True + if name == self.kwarg: + return True + return self.find_argname(name, True)[1] is not None + + def find_argname(self, argname, rec=False): + """return index and Name node with given name""" + if self.args: # self.args may be None in some cases (builtin function) + return _find_arg(argname, self.args, rec) + return None, None + + def get_children(self): + """override get_children to skip over None elements in kw_defaults""" + for child in super(Arguments, self).get_children(): + if child is not None: + yield child + + +def _find_arg(argname, args, rec=False): + for i, arg in enumerate(args): + if isinstance(arg, Tuple): + if rec: + found = _find_arg(argname, arg.elts) + if found[0] is not None: + return found + elif arg.name == argname: + return i, arg + return None, None + + +def _format_args(args, defaults=None): + values = [] + if args is None: + return '' + if defaults is not None: + default_offset = len(args) - len(defaults) + for i, arg in enumerate(args): + if isinstance(arg, Tuple): + values.append('(%s)' % _format_args(arg.elts)) + else: + values.append(arg.name) + if defaults is not None and i >= default_offset: + if defaults[i-default_offset] is not None: + values[-1] += '=' + defaults[i-default_offset].as_string() + return ', '.join(values) + + +class AssAttr(NodeNG, ParentAssignTypeMixin): + """class representing an AssAttr node""" + _astroid_fields = ('expr',) + expr = None + +class Assert(Statement): + """class representing an Assert node""" + _astroid_fields = ('test', 'fail',) + test = None + fail = None + +class Assign(Statement, AssignTypeMixin): + """class representing an Assign node""" + _astroid_fields = ('targets', 'value',) + targets = None + value = None + +class AugAssign(Statement, AssignTypeMixin): + """class representing an AugAssign node""" + _astroid_fields = ('target', 'value',) + target = None + value = None + +class Backquote(NodeNG): + """class representing a Backquote node""" + _astroid_fields = ('value',) + value = None + +class BinOp(NodeNG): + """class representing a BinOp node""" + _astroid_fields = ('left', 'right',) + left = None + right = None + +class BoolOp(NodeNG): + """class representing a BoolOp node""" + _astroid_fields = ('values',) + values = None + +class Break(Statement): + """class representing a Break node""" + + +class CallFunc(NodeNG): + """class representing a CallFunc node""" + _astroid_fields = ('func', 'args', 'starargs', 'kwargs') + func = None + args = None + starargs = None + kwargs = None + + def __init__(self): + self.starargs = None + self.kwargs = None + +class Compare(NodeNG): + """class representing a Compare node""" + _astroid_fields = ('left', 'ops',) + left = None + ops = None + + def get_children(self): + """override get_children for tuple fields""" + yield self.left + for _, comparator in self.ops: + yield comparator # we don't want the 'op' + + def last_child(self): + """override last_child""" + # XXX maybe if self.ops: + return self.ops[-1][1] + #return self.left + +class Comprehension(NodeNG): + """class representing a Comprehension node""" + _astroid_fields = ('target', 'iter', 'ifs') + target = None + iter = None + ifs = None + + optional_assign = True + def ass_type(self): + return self + + def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt): + """method used in filter_stmts""" + if self is mystmt: + if isinstance(lookup_node, (Const, Name)): + return [lookup_node], True + + elif self.statement() is mystmt: + # original node's statement is the assignment, only keeps + # current node (gen exp, list comp) + + return [node], True + + return stmts, False + + +class Const(NodeNG, Instance): + """represent a constant node like num, str, bool, None, bytes""" + + def __init__(self, value=None): + self.value = value + + def getitem(self, index, context=None): + if isinstance(self.value, six.string_types): + return Const(self.value[index]) + raise TypeError('%r (value=%s)' % (self, self.value)) + + def has_dynamic_getattr(self): + return False + + def itered(self): + if isinstance(self.value, six.string_types): + return self.value + raise TypeError() + + def pytype(self): + return self._proxied.qname() + + +class Continue(Statement): + """class representing a Continue node""" + + +class Decorators(NodeNG): + """class representing a Decorators node""" + _astroid_fields = ('nodes',) + nodes = None + + def __init__(self, nodes=None): + self.nodes = nodes + + def scope(self): + # skip the function node to go directly to the upper level scope + return self.parent.parent.scope() + +class DelAttr(NodeNG, ParentAssignTypeMixin): + """class representing a DelAttr node""" + _astroid_fields = ('expr',) + expr = None + + +class Delete(Statement, AssignTypeMixin): + """class representing a Delete node""" + _astroid_fields = ('targets',) + targets = None + + +class Dict(NodeNG, Instance): + """class representing a Dict node""" + _astroid_fields = ('items',) + + 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.items()] + + def pytype(self): + return '%s.dict' % BUILTINS + + def get_children(self): + """get children of a Dict node""" + # overrides get_children + for key, value in self.items: + yield key + yield value + + def last_child(self): + """override last_child""" + if self.items: + return self.items[-1][1] + return None + + def itered(self): + return self.items[::2] + + def getitem(self, lookup_key, context=None): + for key, value in self.items: + for inferedkey in key.infer(context): + if inferedkey is YES: + continue + if isinstance(inferedkey, Const) \ + and inferedkey.value == lookup_key: + return value + # This should raise KeyError, but all call sites only catch + # IndexError. Let's leave it like that for now. + raise IndexError(lookup_key) + + +class Discard(Statement): + """class representing a Discard node""" + _astroid_fields = ('value',) + value = None + + +class Ellipsis(NodeNG): + """class representing an Ellipsis node""" + + +class EmptyNode(NodeNG): + """class representing an EmptyNode node""" + + +class ExceptHandler(Statement, AssignTypeMixin): + """class representing an ExceptHandler node""" + _astroid_fields = ('type', 'name', 'body',) + type = None + name = None + body = None + + @cachedproperty + def blockstart_tolineno(self): + if self.name: + return self.name.tolineno + elif self.type: + return self.type.tolineno + else: + return self.lineno + + def catch(self, exceptions): + if self.type is None or exceptions is None: + return True + for node in self.type.nodes_of_class(Name): + if node.name in exceptions: + return True + + +class Exec(Statement): + """class representing an Exec node""" + _astroid_fields = ('expr', 'globals', 'locals',) + expr = None + globals = None + locals = None + + +class ExtSlice(NodeNG): + """class representing an ExtSlice node""" + _astroid_fields = ('dims',) + dims = None + +class For(BlockRangeMixIn, AssignTypeMixin, Statement): + """class representing a For node""" + _astroid_fields = ('target', 'iter', 'body', 'orelse',) + target = None + iter = None + body = None + orelse = None + + optional_assign = True + @cachedproperty + def blockstart_tolineno(self): + return self.iter.tolineno + + +class From(FromImportMixIn, Statement): + """class representing a From node""" + + def __init__(self, fromname, names, level=0): + self.modname = fromname + self.names = names + self.level = level + +class Getattr(NodeNG): + """class representing a Getattr node""" + _astroid_fields = ('expr',) + expr = None + + +class Global(Statement): + """class representing a Global node""" + + def __init__(self, names): + self.names = names + + def _infer_name(self, frame, name): + return name + + +class If(BlockRangeMixIn, Statement): + """class representing an If node""" + _astroid_fields = ('test', 'body', 'orelse') + test = None + body = None + orelse = None + + @cachedproperty + def blockstart_tolineno(self): + return self.test.tolineno + + def block_range(self, lineno): + """handle block line numbers range for if statements""" + if lineno == self.body[0].fromlineno: + return lineno, lineno + if lineno <= self.body[-1].tolineno: + return lineno, self.body[-1].tolineno + return self._elsed_block_range(lineno, self.orelse, + self.body[0].fromlineno - 1) + + +class IfExp(NodeNG): + """class representing an IfExp node""" + _astroid_fields = ('test', 'body', 'orelse') + test = None + body = None + orelse = None + + +class Import(FromImportMixIn, Statement): + """class representing an Import node""" + + +class Index(NodeNG): + """class representing an Index node""" + _astroid_fields = ('value',) + value = None + + +class Keyword(NodeNG): + """class representing a Keyword node""" + _astroid_fields = ('value',) + value = None + + +class List(NodeNG, Instance, ParentAssignTypeMixin): + """class representing a List node""" + _astroid_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 '%s.list' % BUILTINS + + def getitem(self, index, context=None): + return self.elts[index] + + def itered(self): + return self.elts + + +class Nonlocal(Statement): + """class representing a Nonlocal node""" + + def __init__(self, names): + self.names = names + + def _infer_name(self, frame, name): + return name + + +class Pass(Statement): + """class representing a Pass node""" + + +class Print(Statement): + """class representing a Print node""" + _astroid_fields = ('dest', 'values',) + dest = None + values = None + + +class Raise(Statement): + """class representing a Raise node""" + exc = None + if sys.version_info < (3, 0): + _astroid_fields = ('exc', 'inst', 'tback') + inst = None + tback = None + else: + _astroid_fields = ('exc', 'cause') + exc = None + cause = None + + def raises_not_implemented(self): + if not self.exc: + return + for name in self.exc.nodes_of_class(Name): + if name.name == 'NotImplementedError': + return True + + +class Return(Statement): + """class representing a Return node""" + _astroid_fields = ('value',) + value = None + + +class Set(NodeNG, Instance, ParentAssignTypeMixin): + """class representing a Set node""" + _astroid_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 '%s.set' % BUILTINS + + def itered(self): + return self.elts + + +class Slice(NodeNG): + """class representing a Slice node""" + _astroid_fields = ('lower', 'upper', 'step') + lower = None + upper = None + step = None + +class Starred(NodeNG, ParentAssignTypeMixin): + """class representing a Starred node""" + _astroid_fields = ('value',) + value = None + + +class Subscript(NodeNG): + """class representing a Subscript node""" + _astroid_fields = ('value', 'slice') + value = None + slice = None + + +class TryExcept(BlockRangeMixIn, Statement): + """class representing a TryExcept node""" + _astroid_fields = ('body', 'handlers', 'orelse',) + body = None + handlers = None + orelse = None + + def _infer_name(self, frame, name): + return name + + def block_range(self, lineno): + """handle block line numbers range for try/except statements""" + last = None + for exhandler in self.handlers: + if exhandler.type and lineno == exhandler.type.fromlineno: + return lineno, lineno + if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno: + return lineno, exhandler.body[-1].tolineno + if last is None: + last = exhandler.body[0].fromlineno - 1 + return self._elsed_block_range(lineno, self.orelse, last) + + +class TryFinally(BlockRangeMixIn, Statement): + """class representing a TryFinally node""" + _astroid_fields = ('body', 'finalbody',) + body = None + finalbody = None + + def block_range(self, lineno): + """handle block line numbers range for try/finally statements""" + child = self.body[0] + # py2.5 try: except: finally: + if (isinstance(child, TryExcept) and child.fromlineno == self.fromlineno + and lineno > self.fromlineno and lineno <= child.tolineno): + return child.block_range(lineno) + return self._elsed_block_range(lineno, self.finalbody) + + +class Tuple(NodeNG, Instance, ParentAssignTypeMixin): + """class representing a Tuple node""" + _astroid_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 '%s.tuple' % BUILTINS + + def getitem(self, index, context=None): + return self.elts[index] + + def itered(self): + return self.elts + + +class UnaryOp(NodeNG): + """class representing an UnaryOp node""" + _astroid_fields = ('operand',) + operand = None + + +class While(BlockRangeMixIn, Statement): + """class representing a While node""" + _astroid_fields = ('test', 'body', 'orelse',) + test = None + body = None + orelse = None + + @cachedproperty + def blockstart_tolineno(self): + return self.test.tolineno + + def block_range(self, lineno): + """handle block line numbers range for for and while statements""" + return self. _elsed_block_range(lineno, self.orelse) + + +class With(BlockRangeMixIn, AssignTypeMixin, Statement): + """class representing a With node""" + _astroid_fields = ('items', 'body') + items = None + body = None + + @cachedproperty + def blockstart_tolineno(self): + return self.items[-1][0].tolineno + + def get_children(self): + for expr, var in self.items: + yield expr + if var: + yield var + for elt in self.body: + yield elt + +class Yield(NodeNG): + """class representing a Yield node""" + _astroid_fields = ('value',) + value = None + +class YieldFrom(Yield): + """ Class representing a YieldFrom node. """ + +# constants ############################################################## + +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 astroid node for a python value""" + # XXX we should probably be stricter here and only consider stuff in + # CONST_CLS or do better treatment: in case where value is not in CONST_CLS, + # we should rather recall the builder on this value than returning an empty + # node (another option being that const_factory shouldn't be called with something + # not in CONST_CLS) + assert not isinstance(value, NodeNG) + try: + return CONST_CLS[value.__class__](value) + except (KeyError, AttributeError): + node = EmptyNode() + node.object = value + return node diff --git a/astroid/nodes.py b/astroid/nodes.py new file mode 100644 index 0000000..263ab47 --- /dev/null +++ b/astroid/nodes.py @@ -0,0 +1,73 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +""" +on all nodes : + .is_statement, returning true if the node should be considered as a + statement node + .root(), returning the root node of the tree (i.e. a Module) + .previous_sibling(), returning previous sibling statement node + .next_sibling(), returning next sibling statement node + .statement(), returning the first parent node marked as statement node + .frame(), returning the first node defining a new local scope (i.e. + Module, Function or Class) + .set_local(name, node), define an identifier on the first parent frame, + with the node defining it. This is used by the astroid builder and should not + be used from out there. + +on From and Import : + .real_name(name), + + +""" + +__docformat__ = "restructuredtext en" + +from astroid.node_classes import Arguments, AssAttr, Assert, Assign, \ + AssName, AugAssign, Backquote, BinOp, BoolOp, Break, CallFunc, Compare, \ + Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, \ + Dict, Discard, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, \ + From, Getattr, Global, If, IfExp, Import, Index, Keyword, \ + List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, \ + TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, \ + const_factory +from astroid.scoped_nodes import Module, GenExpr, Lambda, DictComp, \ + ListComp, SetComp, Function, Class + +ALL_NODE_CLASSES = ( + Arguments, AssAttr, Assert, Assign, AssName, AugAssign, + Backquote, BinOp, BoolOp, Break, + CallFunc, Class, Compare, Comprehension, Const, Continue, + Decorators, DelAttr, DelName, Delete, + Dict, DictComp, Discard, + Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, + For, From, Function, + Getattr, GenExpr, Global, + If, IfExp, Import, Index, + Keyword, + Lambda, List, ListComp, + Name, Nonlocal, + Module, + Pass, Print, + Raise, Return, + Set, SetComp, Slice, Starred, Subscript, + TryExcept, TryFinally, Tuple, + UnaryOp, + While, With, + Yield, YieldFrom + ) + diff --git a/astroid/protocols.py b/astroid/protocols.py new file mode 100644 index 0000000..4dd515f --- /dev/null +++ b/astroid/protocols.py @@ -0,0 +1,361 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""this module contains a set of functions to handle python protocols for nodes +where it makes sense. +""" + +__doctype__ = "restructuredtext en" + +from astroid.exceptions import InferenceError, NoDefault, NotFoundError +from astroid.node_classes import unpack_infer +from astroid.bases import InferenceContext, \ + raise_if_nothing_infered, yes_if_nothing_infered, Instance, YES +from astroid.nodes import const_factory +from astroid import nodes + +BIN_OP_METHOD = {'+': '__add__', + '-': '__sub__', + '/': '__div__', + '//': '__floordiv__', + '*': '__mul__', + '**': '__power__', + '%': '__mod__', + '&': '__and__', + '|': '__or__', + '^': '__xor__', + '<<': '__lshift__', + '>>': '__rshift__', + } + +UNARY_OP_METHOD = {'+': '__pos__', + '-': '__neg__', + '~': '__invert__', + 'not': None, # XXX not '__nonzero__' + } + +# unary operations ############################################################ + +def tl_infer_unary_op(self, operator): + if operator == 'not': + return const_factory(not bool(self.elts)) + raise TypeError() # XXX log unsupported operation +nodes.Tuple.infer_unary_op = tl_infer_unary_op +nodes.List.infer_unary_op = tl_infer_unary_op + + +def dict_infer_unary_op(self, operator): + if operator == 'not': + return const_factory(not bool(self.items)) + raise TypeError() # XXX log unsupported operation +nodes.Dict.infer_unary_op = dict_infer_unary_op + + +def const_infer_unary_op(self, operator): + if operator == 'not': + return const_factory(not self.value) + # XXX log potentially raised TypeError + elif operator == '+': + return const_factory(+self.value) + else: # operator == '-': + return const_factory(-self.value) +nodes.Const.infer_unary_op = const_infer_unary_op + + +# binary operations ########################################################### + +BIN_OP_IMPL = {'+': lambda a, b: a + b, + '-': lambda a, b: a - b, + '/': lambda a, b: a / b, + '//': lambda a, b: a // b, + '*': lambda a, b: a * b, + '**': lambda a, b: a ** b, + '%': lambda a, b: a % b, + '&': lambda a, b: a & b, + '|': lambda a, b: a | b, + '^': lambda a, b: a ^ b, + '<<': lambda a, b: a << b, + '>>': lambda a, b: a >> b, + } +for key, impl in list(BIN_OP_IMPL.items()): + BIN_OP_IMPL[key+'='] = impl + +def const_infer_binary_op(self, operator, other, context): + for other in other.infer(context): + if isinstance(other, nodes.Const): + try: + impl = BIN_OP_IMPL[operator] + + try: + yield const_factory(impl(self.value, other.value)) + except Exception: + # ArithmeticError is not enough: float >> float is a TypeError + # TODO : let pylint know about the problem + pass + except TypeError: + # XXX log TypeError + continue + elif other is YES: + yield other + else: + try: + for val in other.infer_binary_op(operator, self, context): + yield val + except AttributeError: + yield YES +nodes.Const.infer_binary_op = yes_if_nothing_infered(const_infer_binary_op) + + +def tl_infer_binary_op(self, operator, other, context): + for other in other.infer(context): + if isinstance(other, self.__class__) and operator == '+': + node = self.__class__() + elts = [n for elt in self.elts for n in elt.infer(context) + if not n is YES] + elts += [n for elt in other.elts for n in elt.infer(context) + if not n is YES] + node.elts = elts + yield node + elif isinstance(other, nodes.Const) and operator == '*': + if not isinstance(other.value, int): + yield YES + continue + node = self.__class__() + elts = [n for elt in self.elts for n in elt.infer(context) + if not n is YES] * other.value + node.elts = elts + yield node + elif isinstance(other, Instance) and not isinstance(other, nodes.Const): + yield YES + # XXX else log TypeError +nodes.Tuple.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op) +nodes.List.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op) + + +def dict_infer_binary_op(self, operator, other, context): + for other in other.infer(context): + if isinstance(other, Instance) and isinstance(other._proxied, nodes.Class): + yield YES + # XXX else log TypeError +nodes.Dict.infer_binary_op = yes_if_nothing_infered(dict_infer_binary_op) + +def instance_infer_binary_op(self, operator, other, context): + try: + methods = self.getattr(BIN_OP_METHOD[operator]) + except (NotFoundError, KeyError): + # Unknown operator + yield YES + else: + for method in methods: + if not isinstance(method, nodes.Function): + continue + for result in method.infer_call_result(self, context): + if result is not YES: + yield result + # We are interested only in the first infered method, + # don't go looking in the rest of the methods of the ancestors. + break + +Instance.infer_binary_op = yes_if_nothing_infered(instance_infer_binary_op) + + +# assignment ################################################################## + +"""the assigned_stmts method is responsible to return the assigned statement +(e.g. not inferred) according to the assignment type. + +The `asspath` argument is used to record the lhs path of the original node. +For instance if we want assigned statements for 'c' in 'a, (b,c)', asspath +will be [1, 1] once arrived to the Assign node. + +The `context` argument is the current inference context which should be given +to any intermediary inference necessary. +""" + +def _resolve_looppart(parts, asspath, context): + """recursive function to resolve multiple assignments on loops""" + asspath = asspath[:] + index = asspath.pop(0) + for part in parts: + if part is YES: + continue + # XXX handle __iter__ and log potentially detected errors + if not hasattr(part, 'itered'): + continue + try: + itered = part.itered() + except TypeError: + continue # XXX log error + for stmt in itered: + try: + assigned = stmt.getitem(index, context) + except (AttributeError, IndexError): + continue + except TypeError: # stmt is unsubscriptable Const + continue + if not asspath: + # we achieved to resolved the assignment path, + # don't infer the last part + yield assigned + elif assigned is YES: + break + else: + # we are not yet on the last part of the path + # search on each possibly inferred value + try: + for infered in _resolve_looppart(assigned.infer(context), + asspath, context): + yield infered + except InferenceError: + break + + +def for_assigned_stmts(self, node, context=None, asspath=None): + if asspath is None: + for lst in self.iter.infer(context): + if isinstance(lst, (nodes.Tuple, nodes.List)): + for item in lst.elts: + yield item + else: + for infered in _resolve_looppart(self.iter.infer(context), + asspath, context): + yield infered + +nodes.For.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts) +nodes.Comprehension.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts) + + +def mulass_assigned_stmts(self, node, context=None, asspath=None): + if asspath is None: + asspath = [] + asspath.insert(0, self.elts.index(node)) + return self.parent.assigned_stmts(self, context, asspath) +nodes.Tuple.assigned_stmts = mulass_assigned_stmts +nodes.List.assigned_stmts = mulass_assigned_stmts + + +def assend_assigned_stmts(self, context=None): + return self.parent.assigned_stmts(self, context=context) +nodes.AssName.assigned_stmts = assend_assigned_stmts +nodes.AssAttr.assigned_stmts = assend_assigned_stmts + + +def _arguments_infer_argname(self, name, context): + # arguments information may be missing, in which case we can't do anything + # more + if not (self.args or self.vararg or self.kwarg): + yield YES + return + # first argument of instance/class method + if self.args and getattr(self.args[0], 'name', None) == name: + functype = self.parent.type + if functype == 'method': + yield Instance(self.parent.parent.frame()) + return + if functype == 'classmethod': + yield self.parent.parent.frame() + return + if name == self.vararg: + vararg = const_factory(()) + vararg.parent = self + yield vararg + return + if name == self.kwarg: + kwarg = const_factory({}) + kwarg.parent = self + yield kwarg + return + # if there is a default value, yield it. And then yield YES to reflect + # we can't guess given argument value + try: + if context is None: + context = InferenceContext() + for infered in self.default_value(name).infer(context): + yield infered + yield YES + except NoDefault: + yield YES + + +def arguments_assigned_stmts(self, node, context, asspath=None): + if context.callcontext: + # reset call context/name + callcontext = context.callcontext + return callcontext.infer_argument(self.parent, node.name, context) + return _arguments_infer_argname(self, node.name, context) +nodes.Arguments.assigned_stmts = arguments_assigned_stmts + + +def assign_assigned_stmts(self, node, context=None, asspath=None): + if not asspath: + yield self.value + return + for infered in _resolve_asspart(self.value.infer(context), asspath, context): + yield infered +nodes.Assign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) +nodes.AugAssign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) + + +def _resolve_asspart(parts, asspath, context): + """recursive function to resolve multiple assignments""" + asspath = asspath[:] + index = asspath.pop(0) + for part in parts: + if hasattr(part, 'getitem'): + try: + assigned = part.getitem(index, context) + # XXX raise a specific exception to avoid potential hiding of + # unexpected exception ? + except (TypeError, IndexError): + return + if not asspath: + # we achieved to resolved the assignment path, don't infer the + # last part + yield assigned + elif assigned is YES: + return + else: + # we are not yet on the last part of the path search on each + # possibly inferred value + try: + for infered in _resolve_asspart(assigned.infer(context), + asspath, context): + yield infered + except InferenceError: + return + + +def excepthandler_assigned_stmts(self, node, context=None, asspath=None): + for assigned in unpack_infer(self.type): + if isinstance(assigned, nodes.Class): + assigned = Instance(assigned) + yield assigned +nodes.ExceptHandler.assigned_stmts = raise_if_nothing_infered(excepthandler_assigned_stmts) + + +def with_assigned_stmts(self, node, context=None, asspath=None): + if asspath is None: + for _, vars in self.items: + if vars is None: + continue + for lst in vars.infer(context): + if isinstance(lst, (nodes.Tuple, nodes.List)): + for item in lst.nodes: + yield item +nodes.With.assigned_stmts = raise_if_nothing_infered(with_assigned_stmts) + + diff --git a/astroid/raw_building.py b/astroid/raw_building.py new file mode 100644 index 0000000..08f6af6 --- /dev/null +++ b/astroid/raw_building.py @@ -0,0 +1,364 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""this module contains a set of functions to create astroid trees from scratch +(build_* functions) or from living object (object_build_* functions) +""" + +__docformat__ = "restructuredtext en" + +import sys +from os.path import abspath +from inspect import (getargspec, isdatadescriptor, isfunction, ismethod, + ismethoddescriptor, isclass, isbuiltin, ismodule) +import six + +from astroid.node_classes import CONST_CLS +from astroid.nodes import (Module, Class, Const, const_factory, From, + Function, EmptyNode, Name, Arguments) +from astroid.bases import BUILTINS, Generator +from astroid.manager import AstroidManager +MANAGER = AstroidManager() + +_CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types + +def _io_discrepancy(member): + # _io module names itself `io`: http://bugs.python.org/issue18602 + member_self = getattr(member, '__self__', None) + return (member_self and + ismodule(member_self) and + member_self.__name__ == '_io' and + member.__module__ == 'io') + +def _attach_local_node(parent, node, name): + node.name = name # needed by add_local_node + parent.add_local_node(node) + +_marker = object() + +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 = EmptyNode() + enode.object = object + _attach_local_node(node, enode, name) + +def _has_underlying_object(self): + return hasattr(self, 'object') and self.object is not _marker + +EmptyNode.has_underlying_object = _has_underlying_object + +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, 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 = From(modname, [(membername, None)]) + _attach_local_node(node, from_node, membername) + + +def build_module(name, doc=None): + """create and initialize a astroid Module node""" + 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 astroid Class node""" + node = Class(name, doc) + for base in basenames: + basenode = Name() + basenode.name = base + node.bases.append(basenode) + basenode.parent = node + return node + +def build_function(name, args=None, defaults=None, flag=0, doc=None): + """create and initialize a astroid Function node""" + args, defaults = args or [], defaults or [] + # first argument is now a list of decorators + func = Function(name, doc) + func.args = argsnode = Arguments() + argsnode.args = [] + for arg in args: + argsnode.args.append(Name()) + argsnode.args[-1].name = arg + argsnode.args[-1].parent = argsnode + argsnode.defaults = [] + for default in defaults: + argsnode.defaults.append(const_factory(default)) + argsnode.defaults[-1].parent = argsnode + argsnode.kwarg = None + argsnode.vararg = None + argsnode.parent = func + if args: + register_arguments(func) + return func + + +def build_from_import(fromname, names): + """create and initialize an astroid From import statement""" + return From(fromname, [(name, None) for name in names]) + +def register_arguments(func, args=None): + """add given arguments to local + + args is a list that may contains nested lists + (i.e. def func(a, (b, c, d)): ...) + """ + if args is None: + args = func.args.args + if func.args.vararg: + func.set_local(func.args.vararg, func.args) + if func.args.kwarg: + func.set_local(func.args.kwarg, func.args) + for arg in args: + if isinstance(arg, Name): + func.set_local(arg.name, arg) + else: + register_arguments(func, arg.elts) + +def object_build_class(node, member, localname): + """create astroid for a living class object""" + basenames = [base.__name__ for base in member.__bases__] + return _base_class_object_build(node, member, basenames, + localname=localname) + +def object_build_function(node, member, localname): + """create astroid for a living function object""" + args, varargs, varkw, defaults = getargspec(member) + if varargs is not None: + args.append(varargs) + if varkw is not None: + args.append(varkw) + func = build_function(getattr(member, '__name__', None) or localname, args, + defaults, member.func_code.co_flags, member.__doc__) + node.add_local_node(func, localname) + +def object_build_datadescriptor(node, member, name): + """create astroid for a living data descriptor object""" + return _base_class_object_build(node, member, [], name) + +def object_build_methoddescriptor(node, member, localname): + """create astroid for a living method descriptor object""" + # FIXME get arguments ? + func = build_function(getattr(member, '__name__', None) or localname, + doc=member.__doc__) + # set node's arguments to None to notice that we have no information, not + # and empty argument list + func.args.args = None + node.add_local_node(func, localname) + +def _base_class_object_build(node, member, basenames, name=None, localname=None): + """create astroid for a living class object, with a given set of base names + (e.g. ancestors) + """ + klass = build_class(name or getattr(member, '__name__', None) or localname, + basenames, member.__doc__) + klass._newstyle = isinstance(member, type) + node.add_local_node(klass, localname) + try: + # limit the instantiation trick since it's too dangerous + # (such as infinite test execution...) + # this at least resolves common case such as Exception.args, + # OSError.errno + if issubclass(member, Exception): + instdict = member().__dict__ + else: + raise TypeError + except: + pass + else: + for name, obj in instdict.items(): + valnode = EmptyNode() + valnode.object = obj + valnode.parent = klass + valnode.lineno = 1 + klass.instance_attrs[name] = [valnode] + return klass + + + + +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. + """ + + # astroid from living objects ############################################### + + def __init__(self): + self._done = {} + self._module = None + + def inspect_build(self, module, modname=None, path=None): + """build astroid 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__ + try: + node = build_module(modname, module.__doc__) + except AttributeError: + # in jython, java modules have no __doc__ (see #109562) + node = build_module(modname) + node.file = node.path = path and abspath(path) or path + node.name = modname + MANAGER.cache_module(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 = six.get_method_function(member) + if isfunction(member): + # verify this is not an imported function + filename = getattr(six.get_function_code(member), + 'co_filename', None) + if filename is None: + assert isinstance(member, object) + object_build_methoddescriptor(node, member, name) + elif filename != getattr(self._module, '__file__', None): + attach_dummy_node(node, name, member) + else: + object_build_function(node, member, name) + elif isbuiltin(member): + if (not _io_discrepancy(member) and + 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 type(member) in _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 astroid 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 = BUILTINS + 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 + + +### astroid bootstrapping ###################################################### +Astroid_BUILDER = InspectBuilder() + +_CONST_PROXY = {} +def astroid_bootstrapping(): + """astroid 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 + from logilab.common.compat import builtins + astroid_builtin = Astroid_BUILDER.inspect_build(builtins) + for cls, node_cls in CONST_CLS.items(): + if cls is type(None): + proxy = build_class('NoneType') + proxy.parent = astroid_builtin + else: + proxy = astroid_builtin.getattr(cls.__name__)[0] + if cls in (dict, list, set, tuple): + node_cls._proxied = proxy + else: + _CONST_PROXY[cls] = proxy + +astroid_bootstrapping() + +# 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) + +from types import GeneratorType +Generator._proxied = Class(GeneratorType.__name__, GeneratorType.__doc__) +Astroid_BUILDER.object_build(Generator._proxied, GeneratorType) + diff --git a/astroid/rebuilder.py b/astroid/rebuilder.py new file mode 100644 index 0000000..e3899fd --- /dev/null +++ b/astroid/rebuilder.py @@ -0,0 +1,905 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""this module contains utilities for rebuilding a _ast tree in +order to get a single Astroid representation +""" + +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 astroid 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', + 'comprehension': 'Comprehension', + 'Call': 'CallFunc', + 'ClassDef': 'Class', + "ListCompFor": 'Comprehension', + "GenExprFor": 'Comprehension', + 'excepthandler': 'ExceptHandler', + 'Expr': 'Discard', + 'FunctionDef': 'Function', + 'GeneratorExp': 'GenExpr', + 'ImportFrom': 'From', + 'keyword': 'Keyword', + 'Repr': 'Backquote', + } +PY3K = sys.version_info >= (3, 0) +PY34 = sys.version_info >= (3, 4) + +def _init_set_doc(node, newnode): + newnode.doc = None + try: + if isinstance(node.body[0], Discard) and isinstance(node.body[0].value, Str): + newnode.doc = node.body[0].value.s + node.body = node.body[1:] + + except IndexError: + pass # ast built from scratch + +def _lineno_parent(oldnode, newnode, parent): + newnode.parent = parent + newnode.lineno = oldnode.lineno + newnode.col_offset = oldnode.col_offset + +def _set_infos(oldnode, newnode, parent): + newnode.parent = parent + if hasattr(oldnode, 'lineno'): + newnode.lineno = oldnode.lineno + if hasattr(oldnode, 'col_offset'): + newnode.col_offset = oldnode.col_offset + +def _create_yield_node(node, parent, rebuilder, factory): + newnode = factory() + _lineno_parent(node, newnode, parent) + if node.value is not None: + newnode.value = rebuilder.visit(node.value, newnode) + return newnode + + +class TreeRebuilder(object): + """Rebuilds the _ast tree to become an Astroid tree""" + + def __init__(self, manager): + self._manager = manager + self.asscontext = None + self._global_names = [] + self._from_nodes = [] + self._delayed_assattr = [] + self._visit_meths = {} + self._transform = manager.transform + + 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 + newnode.parent = None + _init_set_doc(node, newnode) + newnode.body = [self.visit(child, newnode) for child in node.body] + return self._transform(newnode) + + def visit(self, node, parent): + cls = node.__class__ + if cls in self._visit_meths: + visit_method = self._visit_meths[cls] + 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 self._transform(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() + newnode.parent = 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.kwonlyargs = [] + newnode.kw_defaults = [] + vararg, kwarg = node.vararg, node.kwarg + # change added in 82732 (7c5c678e4164), vararg and kwarg + # are instances of `_ast.arg`, not strings + if vararg: + if PY34: + if vararg.annotation: + newnode.varargannotation = self.visit(vararg.annotation, + newnode) + vararg = vararg.arg + elif PY3K and node.varargannotation: + newnode.varargannotation = self.visit(node.varargannotation, + newnode) + if kwarg: + if PY34: + if kwarg.annotation: + newnode.kwargannotation = self.visit(kwarg.annotation, + newnode) + kwarg = kwarg.arg + elif PY3K: + if node.kwargannotation: + newnode.kwargannotation = self.visit(node.kwargannotation, + newnode) + newnode.vararg = vararg + newnode.kwarg = kwarg + # save argument names in locals: + if vararg: + newnode.parent.set_local(vararg, newnode) + if kwarg: + newnode.parent.set_local(kwarg, newnode) + 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) + 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) + if node.msg is not None: + newnode.fail = self.visit(node.msg, newnode) + 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) + # set some function or metaclass infos XXX explain ? + klass = newnode.parent.frame() + 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, new.Function): + if func_name in ('classmethod', 'staticmethod'): + meth.type = func_name + elif func_name == 'classproperty': # see lgc.decorators + meth.type = 'classmethod' + meth.extra_decorators.append(newnode.value) + except (AttributeError, KeyError): + continue + 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) + 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) + 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__] + 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__] + 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) + for child in node.keywords: + newnode.args.append(self.visit(child, newnode)) + return newnode + + def visit_class(self, node, parent): + """visit a Class node to become astroid""" + 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.parent.frame().set_local(newnode.name, newnode) + return newnode + + def visit_const(self, node, parent): + """visit a Const node by returning a fresh instance of it""" + 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 = 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)] + return newnode + + def visit_comprehension(self, node, parent): + """visit a Comprehension node by returning a fresh instance of it""" + newnode = new.Comprehension() + newnode.parent = 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] + 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 astroid.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] + 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 + 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)] + 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] + 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) + return newnode + + def visit_ellipsis(self, node, parent): + """visit an Ellipsis node by returning a fresh instance of it""" + 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 = new.EmptyNode() + _set_infos(node, newnode, parent) + 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) + 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] + 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) + 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) + return newnode + + def visit_extslice(self, node, parent): + """visit an ExtSlice node by returning a fresh instance of it""" + newnode = new.ExtSlice() + newnode.parent = parent + newnode.dims = [self.visit(dim, newnode) for dim in node.dims] + 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] + 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 or None) + _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 astroid""" + self._global_names.append({}) + 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) + if PY3K and node.returns: + newnode.returns = self.visit(node.returns, newnode) + self._global_names.pop() + frame = newnode.parent.frame() + 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, new.Name): + if decorator_expr.name in ('classmethod', 'staticmethod'): + newnode._type = decorator_expr.name + elif decorator_expr.name == 'classproperty': + newnode._type = 'classmethod' + 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] + 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 + return newnode + + def visit_global(self, node, parent): + """visit an Global node to become astroid""" + 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 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] + 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) + 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() + newnode.parent = parent + newnode.value = self.visit(node.value, newnode) + return newnode + + def visit_keyword(self, node, parent): + """visit a Keyword node by returning a fresh instance of it""" + newnode = new.Keyword() + newnode.parent = parent + newnode.arg = node.arg + newnode.value = self.visit(node.value, newnode) + 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) + 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] + 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] + 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) + 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 = new.Pass() + _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) + _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 + if node.dest is not None: + newnode.dest = self.visit(node.dest, newnode) + newnode.values = [self.visit(child, newnode) for child in node.values] + 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) + 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) + return newnode + + def visit_set(self, node, parent): + """visit a Set 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] + 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] + return newnode + + def visit_slice(self, node, parent): + """visit a Slice node by returning a fresh instance of it""" + newnode = new.Slice() + newnode.parent = 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) + 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 + 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] + 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] + 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] + 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__] + 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] + return newnode + + def visit_with(self, node, parent): + newnode = new.With() + _lineno_parent(node, newnode, parent) + expr = self.visit(node.context_expr, newnode) + self.asscontext = "Ass" + if node.optional_vars is not None: + vars = self.visit(node.optional_vars, newnode) + else: + vars = None + self.asscontext = None + newnode.items = [(expr, vars)] + newnode.body = [self.visit(child, newnode) for child in node.body] + return newnode + + def visit_yield(self, node, parent): + """visit a Yield node by returning a fresh instance of it""" + return _create_yield_node(node, parent, self, new.Yield) + +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 node is coming from py>=3.0, but we use AssName in py2.x + # XXX or we should instead introduce a Arg node in astroid ? + return self.visit_assname(node, parent, node.arg) + + def visit_nameconstant(self, node, parent): + # in Python 3.4 we have NameConstant for True / False / None + newnode = new.Const(node.value) + _set_infos(node, newnode, parent) + return newnode + + def visit_arguments(self, node, parent): + newnode = super(TreeRebuilder3k, self).visit_arguments(node, parent) + self.asscontext = "Ass" + newnode.kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs] + self.asscontext = None + newnode.kw_defaults = [self.visit(child, newnode) if child else None for child in node.kw_defaults] + newnode.annotations = [ + self.visit(arg.annotation, newnode) if arg.annotation else None + for arg in node.args] + 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) + 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] + 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) + 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) + return newnode + + def visit_try(self, node, parent): + # python 3.3 introduce a new Try node replacing TryFinally/TryExcept nodes + if node.finalbody: + newnode = new.TryFinally() + _lineno_parent(node, newnode, parent) + newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody] + if node.handlers: + excnode = new.TryExcept() + _lineno_parent(node, excnode, newnode) + excnode.body = [self.visit(child, excnode) for child in node.body] + excnode.handlers = [self.visit(child, excnode) for child in node.handlers] + excnode.orelse = [self.visit(child, excnode) for child in node.orelse] + newnode.body = [excnode] + else: + newnode.body = [self.visit(child, newnode) for child in node.body] + elif node.handlers: + 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] + return newnode + + def visit_with(self, node, parent): + if 'items' not in node._fields: + # python < 3.3 + return super(TreeRebuilder3k, self).visit_with(node, parent) + + newnode = new.With() + _lineno_parent(node, newnode, parent) + def visit_child(child): + expr = self.visit(child.context_expr, newnode) + self.asscontext = 'Ass' + if child.optional_vars: + var = self.visit(child.optional_vars, newnode) + else: + var = None + self.asscontext = None + return expr, var + newnode.items = [visit_child(child) + for child in node.items] + newnode.body = [self.visit(child, newnode) for child in node.body] + return newnode + + def visit_yieldfrom(self, node, parent): + return _create_yield_node(node, parent, self, new.YieldFrom) + + def visit_class(self, node, parent): + newnode = super(TreeRebuilder3k, self).visit_class(node, parent) + newnode._newstyle = True + for keyword in node.keywords: + if keyword.arg == 'metaclass': + newnode._metaclass = self.visit(keyword, newnode).value + break + return newnode + +if sys.version_info >= (3, 0): + TreeRebuilder = TreeRebuilder3k + + diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py new file mode 100644 index 0000000..b792bd8 --- /dev/null +++ b/astroid/scoped_nodes.py @@ -0,0 +1,1304 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""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 with_statement + +__doctype__ = "restructuredtext en" + +import sys +from itertools import chain +try: + from io import BytesIO +except ImportError: + from cStringIO import StringIO as BytesIO + +import six +from logilab.common.compat import builtins +from logilab.common.decorators import cached, cachedproperty + +from astroid.exceptions import NotFoundError, \ + AstroidBuildingException, InferenceError +from astroid.node_classes import Const, DelName, DelAttr, \ + Dict, From, List, Pass, Raise, Return, Tuple, Yield, YieldFrom, \ + LookupMixIn, const_factory as cf, unpack_infer, Name, CallFunc +from astroid.bases import NodeNG, InferenceContext, Instance,\ + YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, \ + BUILTINS +from astroid.mixins import FilterStmtsMixin +from astroid.bases import Statement +from astroid.manager import AstroidManager + +ITER_METHODS = ('__iter__', '__getitem__') +PY3K = sys.version_info >= (3, 0) + + +def remove_nodes(func, cls): + def wrapper(*args, **kwargs): + nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)] + if not nodes: + raise NotFoundError() + return nodes + return wrapper + + +def function_to_method(n, klass): + if isinstance(n, Function): + if n.type == 'classmethod': + return BoundMethod(n, klass) + if n.type != 'staticmethod': + return UnboundMethod(n) + return n + +def std_special_attributes(self, name, add_locals=True): + if add_locals: + locals = self.locals + else: + locals = {} + if name == '__name__': + return [cf(self.name)] + locals.get(name, []) + if name == '__doc__': + return [cf(self.doc)] + locals.get(name, []) + if name == '__dict__': + return [Dict()] + locals.get(name, []) + raise NotFoundError(name) + +MANAGER = AstroidManager() +def builtin_lookup(name): + """lookup a name into the builtin module + return the list of matching statements and the astroid for the builtin + module + """ + builtin_astroid = MANAGER.ast_from_module(builtins) + if name == '__dict__': + return builtin_astroid, () + try: + stmts = builtin_astroid.locals[name] + except KeyError: + stmts = () + return builtin_astroid, stmts + + +# TODO move this Mixin to mixins.py; problem: 'Function' in _scope_lookup +class LocalsDictNodeNG(LookupMixIn, NodeNG): + """ this class provides locals handling common to Module, Function + and Class nodes, including a dict like interface for direct access + to locals information + """ + + # attributes below are set by the builder module or by raw factories + + # dictionary of locals with name as key and node defining the local as + # value + + def qname(self): + """return the 'qualified' name of the node, eg module.name, + module.class.name ... + """ + if self.parent is None: + return self.name + return '%s.%s' % (self.parent.frame().qname(), self.name) + + def frame(self): + """return the first parent frame node (i.e. Module, Function or Class) + """ + return self + + def scope(self): + """return the first node defining a new scope (i.e. Module, + Function, Class, Lambda but also GenExpr, DictComp and SetComp) + """ + return self + + + def _scope_lookup(self, node, name, offset=0): + """XXX method for interfacing the scope lookup""" + try: + stmts = node._filter_stmts(self.locals[name], self, offset) + except KeyError: + stmts = () + if stmts: + return self, stmts + if self.parent: # i.e. not Module + # nested scope: if parent scope is a function, that's fine + # else jump to the module + pscope = self.parent.scope() + if not pscope.is_function: + pscope = pscope.root() + return pscope.scope_lookup(node, name) + return builtin_lookup(name) # Module + + + + def set_local(self, name, stmt): + """define in locals ( is the node defining the name) + if the node is a Module node (i.e. has globals), add the name to + globals + + if the name is already defined, ignore it + """ + #assert not stmt in self.locals.get(name, ()), (self, stmt) + self.locals.setdefault(name, []).append(stmt) + + __setitem__ = set_local + + def _append_node(self, child): + """append a child, linking it in the tree""" + self.body.append(child) + child.parent = self + + def add_local_node(self, child_node, name=None): + """append a child which should alter locals to the given node""" + if name != '__class__': + # add __class__ node as a child will cause infinite recursion later! + self._append_node(child_node) + self.set_local(name or child_node.name, child_node) + + + def __getitem__(self, item): + """method from the `dict` interface returning the first node + associated with the given name in the locals dictionary + + :type item: str + :param item: the name of the locally defined object + :raises KeyError: if the name is not defined + """ + return self.locals[item][0] + + def __iter__(self): + """method from the `dict` interface returning an iterator on + `self.keys()` + """ + return iter(self.keys()) + + def keys(self): + """method from the `dict` interface returning a tuple containing + locally defined names + """ + return list(self.locals.keys()) + + def values(self): + """method from the `dict` interface returning a tuple containing + locally defined nodes which are instance of `Function` or `Class` + """ + return [self[key] for key in self.keys()] + + def items(self): + """method from the `dict` interface returning a list of tuple + containing each locally defined name with its associated node, + which is an instance of `Function` or `Class` + """ + return list(zip(self.keys(), self.values())) + + + def __contains__(self, name): + return name in self.locals + has_key = __contains__ + +# Module ##################################################################### + +class Module(LocalsDictNodeNG): + _astroid_fields = ('body',) + + fromlineno = 0 + lineno = 0 + + # attributes below are set by the builder module or by raw factories + + # the file from which as been extracted the astroid representation. It may + # be None if the representation has been built from a built-in module + file = None + # Alternatively, if built from a string/bytes, this can be set + file_bytes = None + # encoding of python source file, so we can get unicode out of it (python2 + # only) + file_encoding = None + # the module name + name = None + # boolean for astroid built from source (i.e. ast) + pure_python = None + # boolean for package module + package = None + # dictionary of globals with name as key and node defining the global + # as value + globals = None + + # Future imports + future_imports = None + + # names of python special attributes (handled by getattr impl.) + special_attributes = set(('__name__', '__doc__', '__file__', '__path__', + '__dict__')) + # names of module attributes available through the global scope + scope_attrs = set(('__name__', '__doc__', '__file__', '__path__')) + + def __init__(self, name, doc, pure_python=True): + self.name = name + self.doc = doc + self.pure_python = pure_python + self.locals = self.globals = {} + self.body = [] + self.future_imports = set() + + @property + def file_stream(self): + if self.file_bytes is not None: + return BytesIO(self.file_bytes) + if self.file is not None: + return open(self.file, 'rb') + return None + + def block_range(self, lineno): + """return block line numbers. + + start from the beginning whatever the given lineno + """ + return self.fromlineno, self.tolineno + + def scope_lookup(self, node, name, offset=0): + if name in self.scope_attrs and not name in self.locals: + try: + return self, self.getattr(name) + except NotFoundError: + return self, () + return self._scope_lookup(node, name, offset) + + def pytype(self): + return '%s.module' % BUILTINS + + def display_type(self): + return 'Module' + + def getattr(self, name, context=None, ignore_locals=False): + if name in self.special_attributes: + if name == '__file__': + return [cf(self.file)] + self.locals.get(name, []) + if name == '__path__' and self.package: + return [List()] + self.locals.get(name, []) + return std_special_attributes(self, name) + if not ignore_locals and name in self.locals: + return self.locals[name] + if self.package: + try: + return [self.import_module(name, relative_only=True)] + except AstroidBuildingException: + raise NotFoundError(name) + except SyntaxError: + 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) + + def igetattr(self, name, context=None): + """inferred getattr""" + # set lookup name since this is necessary to infer on import nodes for + # instance + if not context: + context = InferenceContext() + try: + return _infer_stmts(self.getattr(name, context), context, frame=self, lookupname=name) + except NotFoundError: + raise InferenceError(name) + + def fully_defined(self): + """return True if this module has been built from a .py file + and so contains a complete representation including the code + """ + return self.file is not None and self.file.endswith('.py') + + def statement(self): + """return the first parent node marked as statement node + consider a module as a statement... + """ + return self + + def previous_sibling(self): + """module has no sibling""" + return + + def next_sibling(self): + """module has no sibling""" + return + + if sys.version_info < (2, 8): + @cachedproperty + 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 = True + + def absolute_import_activated(self): + return self._absolute_import_activated + + def import_module(self, modname, relative_only=False, level=None): + """import the given module considering self as context""" + if relative_only and level is None: + level = 0 + absmodname = self.relative_to_absolute_name(modname, level) + try: + return MANAGER.ast_from_module_name(absmodname) + except AstroidBuildingException: + # we only want to import a sub module or package of this module, + # skip here + if relative_only: + raise + return MANAGER.ast_from_module_name(modname) + + def relative_to_absolute_name(self, modname, level): + """return the absolute module name for a relative import. + + The relative import can be implicit or explicit. + """ + # XXX this returns non sens when called on an absolute import + # like 'pylint.checkers.astroid.utils' + # XXX doesn't return absolute name if self.name isn't absolute name + if self.absolute_import_activated() and level is None: + return modname + if level: + if self.package: + level = level - 1 + package_name = self.name.rsplit('.', level)[0] + elif self.package: + package_name = self.name + else: + package_name = self.name.rsplit('.', 1)[0] + if package_name: + if not modname: + return package_name + return '%s.%s' % (package_name, modname) + return modname + + + def wildcard_import_names(self): + """return the list of imported names when this module is 'wildcard + imported' + + It doesn't include the '__builtins__' name which is added by the + current CPython implementation of wildcard imports. + """ + # take advantage of a living module if it exists + try: + living = sys.modules[self.name] + except KeyError: + pass + else: + try: + return living.__all__ + except AttributeError: + return [name for name in living.__dict__.keys() + if not name.startswith('_')] + # else lookup the astroid + # + # We separate the different steps of lookup in try/excepts + # to avoid catching too many Exceptions + default = [name for name in self.keys() if not name.startswith('_')] + try: + all = self['__all__'] + except KeyError: + return default + try: + explicit = next(all.assigned_stmts()) + except InferenceError: + return default + except AttributeError: + # not an assignment node + # XXX infer? + return default + + # Try our best to detect the exported name. + infered = [] + try: + explicit = next(explicit.infer()) + except InferenceError: + return default + if not isinstance(explicit, (Tuple, List)): + return default + + str_const = lambda node: (isinstance(node, Const) and + isinstance(node.value, six.string_types)) + for node in explicit.elts: + if str_const(node): + infered.append(node.value) + else: + try: + infered_node = next(node.infer()) + except InferenceError: + continue + if str_const(infered_node): + infered.append(infered_node.value) + return infered + + + +class ComprehensionScope(LocalsDictNodeNG): + def frame(self): + return self.parent.frame() + + scope_lookup = LocalsDictNodeNG._scope_lookup + + +class GenExpr(ComprehensionScope): + _astroid_fields = ('elt', 'generators') + + def __init__(self): + self.locals = {} + self.elt = None + self.generators = [] + + +class DictComp(ComprehensionScope): + _astroid_fields = ('key', 'value', 'generators') + + def __init__(self): + self.locals = {} + self.key = None + self.value = None + self.generators = [] + + +class SetComp(ComprehensionScope): + _astroid_fields = ('elt', 'generators') + + def __init__(self): + self.locals = {} + self.elt = None + self.generators = [] + + +class _ListComp(NodeNG): + """class representing a ListComp node""" + _astroid_fields = ('elt', 'generators') + elt = None + generators = None + +if sys.version_info >= (3, 0): + class ListComp(_ListComp, ComprehensionScope): + """class representing a ListComp node""" + def __init__(self): + self.locals = {} +else: + class ListComp(_ListComp): + """class representing a ListComp node""" + +# Function ################################################################### + +def _infer_decorator_callchain(node): + """ Detect decorator call chaining and see if the + end result is a static or a classmethod. + """ + current = node + while True: + if isinstance(current, CallFunc): + try: + current = next(current.func.infer()) + except InferenceError: + return + elif isinstance(current, Function): + if not current.parent: + return + try: + # TODO: We don't handle multiple inference results right now, + # because there's no flow to reason when the return + # is what we are looking for, a static or a class method. + result = next(current.infer_call_result(current.parent)) + if current is result: + # This will lead to an infinite loop, where a decorator + # returns itself. + return + except (StopIteration, InferenceError): + return + if isinstance(result, (Function, CallFunc)): + current = result + else: + if isinstance(result, Instance): + result = result._proxied + if isinstance(result, Class): + if (result.name == 'classmethod' and + result.root().name == BUILTINS): + return 'classmethod' + elif (result.name == 'staticmethod' and + result.root().name == BUILTINS): + return 'staticmethod' + else: + return + else: + # We aren't interested in anything else returned, + # so go back to the function type inference. + return + else: + return + +def _function_type(self): + """ + Function type, possible values are: + method, function, staticmethod, classmethod. + """ + # Can't infer that this node is decorated + # with a subclass of `classmethod` where `type` is first set, + # so do it here. + if self.decorators: + for node in self.decorators.nodes: + if isinstance(node, CallFunc): + _type = _infer_decorator_callchain(node) + if _type is None: + continue + else: + return _type + if not isinstance(node, Name): + continue + try: + for infered in node.infer(): + if not isinstance(infered, Class): + continue + for ancestor in infered.ancestors(): + if isinstance(ancestor, Class): + if (ancestor.name == 'classmethod' and + ancestor.root().name == BUILTINS): + return 'classmethod' + elif (ancestor.name == 'staticmethod' and + ancestor.root().name == BUILTINS): + return 'staticmethod' + except InferenceError: + pass + return self._type + + +class Lambda(LocalsDictNodeNG, FilterStmtsMixin): + _astroid_fields = ('args', 'body',) + name = '' + + # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' + type = 'function' + + def __init__(self): + self.locals = {} + self.args = [] + self.body = [] + + def pytype(self): + if 'method' in self.type: + return '%s.instancemethod' % BUILTINS + return '%s.function' % BUILTINS + + def display_type(self): + if 'method' in self.type: + return 'Method' + return 'Function' + + def callable(self): + return True + + def argnames(self): + """return a list of argument names""" + if self.args.args: # maybe None with builtin functions + names = _rec_get_names(self.args.args) + else: + names = [] + if self.args.vararg: + names.append(self.args.vararg) + if self.args.kwarg: + names.append(self.args.kwarg) + return names + + def infer_call_result(self, caller, context=None): + """infer what a function is returning when called""" + return self.body.infer(context) + + def scope_lookup(self, node, name, offset=0): + if node in self.args.defaults or node in self.args.kw_defaults: + frame = self.parent.frame() + # line offset to avoid that def func(f=func) resolve the default + # value to the defined function + offset = -1 + else: + # check this is not used in function decorators + frame = self + return frame._scope_lookup(node, name, offset) + + +class Function(Statement, Lambda): + if PY3K: + _astroid_fields = ('decorators', 'args', 'body', 'returns') + returns = None + else: + _astroid_fields = ('decorators', 'args', 'body') + + special_attributes = set(('__name__', '__doc__', '__dict__')) + is_function = True + # attributes below are set by the builder module or by raw factories + blockstart_tolineno = None + decorators = None + _type = "function" + type = cachedproperty(_function_type) + + def __init__(self, name, doc): + self.locals = {} + self.args = [] + self.body = [] + self.name = name + self.doc = doc + self.extra_decorators = [] + self.instance_attrs = {} + + @cachedproperty + def fromlineno(self): + # lineno is the line number of the first decorator, we want the def + # statement lineno + lineno = self.lineno + if self.decorators is not None: + lineno += sum(node.tolineno - node.lineno + 1 + for node in self.decorators.nodes) + + return lineno + + @cachedproperty + def blockstart_tolineno(self): + return self.args.tolineno + + def block_range(self, lineno): + """return block line numbers. + + start from the "def" position whatever the given lineno + """ + return self.fromlineno, self.tolineno + + def getattr(self, name, context=None): + """this method doesn't look in the instance_attrs dictionary since it's + done by an Instance proxy at inference time. + """ + 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): + """return true if the function node should be considered as a method""" + # check we are defined in a Class, because this is usually expected + # (e.g. pylint...) when is_method() return True + return self.type != 'function' and isinstance(self.parent.frame(), Class) + + def decoratornames(self): + """return a list of decorator qualified names""" + result = set() + decoratornodes = [] + if self.decorators is not None: + decoratornodes += self.decorators.nodes + decoratornodes += self.extra_decorators + for decnode in decoratornodes: + for infnode in decnode.infer(): + result.add(infnode.qname()) + return result + decoratornames = cached(decoratornames) + + def is_bound(self): + """return true if the function is bound to an Instance or a class""" + return self.type == 'classmethod' + + def is_abstract(self, pass_is_abstract=True): + """Returns True if the method is abstract. + + A method is considered abstract if + - the only statement is 'raise NotImplementedError', or + - the only statement is 'pass' and pass_is_abstract is True, or + - the method is annotated with abc.astractproperty/abc.abstractmethod + """ + if self.decorators: + for node in self.decorators.nodes: + try: + infered = next(node.infer()) + except InferenceError: + continue + if infered and infered.qname() in ('abc.abstractproperty', + 'abc.abstractmethod'): + return True + + for child_node in self.body: + if isinstance(child_node, Raise): + if child_node.raises_not_implemented(): + return True + if pass_is_abstract and isinstance(child_node, Pass): + return True + return False + # empty function is the same as function with a single "pass" statement + if pass_is_abstract: + return True + + def is_generator(self): + """return true if this is a generator function""" + # XXX should be flagged, not computed + return next(self.nodes_of_class((Yield, YieldFrom), + skip_klass=(Function, Lambda)), False) + + def infer_call_result(self, caller, context=None): + """infer what a function is returning when called""" + if self.is_generator(): + yield Generator() + return + returns = self.nodes_of_class(Return, skip_klass=Function) + for returnnode in returns: + if returnnode.value is None: + yield Const(None) + else: + try: + for infered in returnnode.value.infer(context): + yield infered + except InferenceError: + yield YES + + +def _rec_get_names(args, names=None): + """return a list of all argument names""" + if names is None: + names = [] + for arg in args: + if isinstance(arg, Tuple): + _rec_get_names(arg.elts, names) + else: + names.append(arg.name) + return names + + +# Class ###################################################################### + + +def _is_metaclass(klass, seen=None): + """ Return if the given class can be + used as a metaclass. + """ + if klass.name == 'type': + return True + if seen is None: + seen = set() + for base in klass.bases: + try: + for baseobj in base.infer(): + if baseobj in seen: + continue + else: + seen.add(baseobj) + if isinstance(baseobj, Instance): + # not abstract + return False + if baseobj is YES: + continue + if baseobj is klass: + continue + if not isinstance(baseobj, Class): + continue + if baseobj._type == 'metaclass': + return True + if _is_metaclass(baseobj, seen): + return True + except InferenceError: + continue + return False + + +def _class_type(klass, ancestors=None): + """return a Class node type to differ metaclass, interface and exception + from 'regular' classes + """ + # XXX we have to store ancestors in case we have a ancestor loop + if klass._type is not None: + return klass._type + if _is_metaclass(klass): + klass._type = 'metaclass' + elif klass.name.endswith('Interface'): + klass._type = 'interface' + elif klass.name.endswith('Exception'): + klass._type = 'exception' + else: + if ancestors is None: + ancestors = set() + if klass in ancestors: + # XXX we are in loop ancestors, and have found no type + klass._type = 'class' + return 'class' + ancestors.add(klass) + for base in klass.ancestors(recurs=False): + name = _class_type(base, ancestors) + if name != 'class': + if name == 'metaclass' and not _is_metaclass(klass): + # don't propagate it if the current class + # can't be a metaclass + continue + klass._type = base.type + break + if klass._type is None: + klass._type = 'class' + return klass._type + +def _iface_hdlr(iface_node): + """a handler function used by interfaces to handle suspicious + interface nodes + """ + return True + + +class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): + + # some of the attributes below are set by the builder module or + # by a raw factories + + # a dictionary of class instances attributes + _astroid_fields = ('decorators', 'bases', 'body') # name + + decorators = None + special_attributes = set(('__name__', '__doc__', '__dict__', '__module__', + '__bases__', '__mro__', '__subclasses__')) + blockstart_tolineno = None + + _type = None + type = property(_class_type, + doc="class'type, possible values are 'class' | " + "'metaclass' | 'interface' | 'exception'") + + def __init__(self, name, doc): + self.instance_attrs = {} + self.locals = {} + self.bases = [] + self.body = [] + self.name = name + self.doc = doc + + def _newstyle_impl(self, context=None): + if context is None: + context = InferenceContext() + if self._newstyle is not None: + return self._newstyle + for base in self.ancestors(recurs=False, context=context): + if base._newstyle_impl(context): + self._newstyle = True + break + klass = self._explicit_metaclass() + # could be any callable, we'd need to infer the result of klass(name, + # bases, dict). punt if it's not a class node. + if klass is not None and isinstance(klass, Class): + self._newstyle = klass._newstyle_impl(context) + if self._newstyle is None: + self._newstyle = False + return self._newstyle + + _newstyle = None + newstyle = property(_newstyle_impl, + doc="boolean indicating if it's a new style class" + "or not") + + @cachedproperty + def blockstart_tolineno(self): + if self.bases: + return self.bases[-1].tolineno + else: + return self.fromlineno + + def block_range(self, lineno): + """return block line numbers. + + start from the "class" position whatever the given lineno + """ + return self.fromlineno, self.tolineno + + def pytype(self): + if self.newstyle: + return '%s.type' % BUILTINS + return '%s.classobj' % BUILTINS + + def display_type(self): + return 'Class' + + def callable(self): + return True + + def _is_subtype_of(self, type_name): + if self.qname() == type_name: + return True + for anc in self.ancestors(): + if anc.qname() == type_name: + return True + + def infer_call_result(self, caller, context=None): + """infer what a class is returning when called""" + if self._is_subtype_of('%s.type' % (BUILTINS,)) and len(caller.args) == 3: + name_node = next(caller.args[0].infer()) + if (isinstance(name_node, Const) and + isinstance(name_node.value, six.string_types)): + name = name_node.value + else: + yield YES + return + result = Class(name, None) + bases = next(caller.args[1].infer()) + if isinstance(bases, (Tuple, List)): + result.bases = bases.itered() + else: + # There is currently no AST node that can represent an 'unknown' + # node (YES is not an AST node), therefore we simply return YES here + # although we know at least the name of the class. + yield YES + return + result.parent = caller.parent + yield result + else: + yield Instance(self) + + def scope_lookup(self, node, name, offset=0): + if node in self.bases: + frame = self.parent.frame() + # line offset to avoid that class A(A) resolve the ancestor to + # the defined class + offset = -1 + else: + frame = self + return frame._scope_lookup(node, name, offset) + + # list of parent class as a list of string (i.e. names as they appear + # in the class definition) XXX bw compat + def basenames(self): + return [bnode.as_string() for bnode in self.bases] + basenames = property(basenames) + + def ancestors(self, recurs=True, context=None): + """return an iterator on the node base classes in a prefixed + depth first order + + :param recurs: + boolean indicating if it should recurse or return direct + ancestors only + """ + # FIXME: should be possible to choose the resolution order + # FIXME: inference make infinite loops possible here + yielded = set([self]) + if context is None: + context = InferenceContext() + for stmt in self.bases: + try: + for baseobj in stmt.infer(context): + if not isinstance(baseobj, Class): + if isinstance(baseobj, Instance): + baseobj = baseobj._proxied + else: + # duh ? + continue + if baseobj in yielded: + continue # cf xxx above + yielded.add(baseobj) + yield baseobj + if recurs: + for grandpa in baseobj.ancestors(recurs=True, + context=context): + if grandpa in yielded: + continue # cf xxx above + yielded.add(grandpa) + yield grandpa + except InferenceError: + # XXX log error ? + continue + + def local_attr_ancestors(self, name, context=None): + """return an iterator on astroid representation of parent classes + which have defined in their locals + """ + for astroid in self.ancestors(context=context): + if name in astroid: + yield astroid + + def instance_attr_ancestors(self, name, context=None): + """return an iterator on astroid representation of parent classes + which have defined in their instance attribute dictionary + """ + for astroid in self.ancestors(context=context): + if name in astroid.instance_attrs: + yield astroid + + def has_base(self, node): + return node in self.bases + + def local_attr(self, name, context=None): + """return the list of assign node associated to name in this class + locals or in its parents + + :raises `NotFoundError`: + if no attribute with this name has been find in this class or + its parent classes + """ + try: + return self.locals[name] + except KeyError: + # get if from the first parent implementing it if any + for class_node in self.local_attr_ancestors(name, context): + return class_node.locals[name] + raise NotFoundError(name) + local_attr = remove_nodes(local_attr, DelAttr) + + def instance_attr(self, name, context=None): + """return the astroid nodes associated to name in this class instance + attributes dictionary and in its parents + + :raises `NotFoundError`: + if no attribute with this name has been find in this class or + its parent classes + """ + # Return a copy, so we don't modify self.instance_attrs, + # which could lead to infinite loop. + values = list(self.instance_attrs.get(name, [])) + # get all values from parents + for class_node in self.instance_attr_ancestors(name, context): + values += class_node.instance_attrs[name] + if not values: + raise NotFoundError(name) + return values + instance_attr = remove_nodes(instance_attr, DelAttr) + + def instanciate_class(self): + """return Instance of Class node, else return self""" + return Instance(self) + + def getattr(self, name, context=None): + """this method doesn't look in the instance_attrs dictionary since it's + done by an Instance proxy at inference time. + + It may return a YES object if the attribute has not been actually + found but a __getattr__ or __getattribute__ method is defined + """ + values = self.locals.get(name, []) + if name in self.special_attributes: + if name == '__module__': + return [cf(self.root().qname())] + values + # FIXME: do we really need the actual list of ancestors? + # returning [Tuple()] + values don't break any test + # this is ticket http://www.logilab.org/ticket/52785 + # XXX need proper meta class handling + MRO implementation + if name == '__bases__' or (name == '__mro__' and self.newstyle): + node = Tuple() + node.items = self.ancestors(recurs=True, context=context) + return [node] + values + return std_special_attributes(self, name) + # don't modify the list in self.locals! + values = list(values) + for classnode in self.ancestors(recurs=True, context=context): + values += classnode.locals.get(name, []) + if not values: + raise NotFoundError(name) + return values + + def igetattr(self, name, context=None): + """inferred getattr, need special treatment in class to handle + descriptors + """ + # set lookup name since this is necessary to infer on import nodes for + # instance + if not context: + context = InferenceContext() + try: + for infered in _infer_stmts(self.getattr(name, context), context, + frame=self, lookupname=name): + # yield YES object instead of descriptors when necessary + if not isinstance(infered, Const) and isinstance(infered, Instance): + try: + infered._proxied.getattr('__get__', context) + except NotFoundError: + yield infered + else: + yield YES + else: + yield function_to_method(infered, self) + except NotFoundError: + if not name.startswith('__') and self.has_dynamic_getattr(context): + # class handle some dynamic attributes, return a YES object + yield YES + else: + raise InferenceError(name) + + def has_dynamic_getattr(self, context=None): + """return True if the class has a custom __getattr__ or + __getattribute__ method + """ + # need to explicitly handle optparse.Values (setattr is not detected) + if self.name == 'Values' and self.root().name == 'optparse': + return True + try: + self.getattr('__getattr__', context) + return True + except NotFoundError: + #if self.newstyle: XXX cause an infinite recursion error + try: + getattribute = self.getattr('__getattribute__', context)[0] + if getattribute.root().name != BUILTINS: + # class has a custom __getattribute__ defined + return True + except NotFoundError: + pass + return False + + def methods(self): + """return an iterator on all methods defined in the class and + its ancestors + """ + done = {} + for astroid in chain(iter((self,)), self.ancestors()): + for meth in astroid.mymethods(): + if meth.name in done: + continue + done[meth.name] = None + yield meth + + def mymethods(self): + """return an iterator on all methods defined in the class""" + for member in self.values(): + if isinstance(member, Function): + yield member + + def interfaces(self, herited=True, handler_func=_iface_hdlr): + """return an iterator on interfaces implemented by the given + class node + """ + # FIXME: what if __implements__ = (MyIFace, MyParent.__implements__)... + try: + implements = Instance(self).getattr('__implements__')[0] + except NotFoundError: + return + if not herited and not implements.frame() is self: + return + found = set() + missing = False + for iface in unpack_infer(implements): + if iface is YES: + missing = True + continue + if not iface in found and handler_func(iface): + found.add(iface) + yield iface + if missing: + raise InferenceError() + + _metaclass = None + def _explicit_metaclass(self): + """ Return the explicit defined metaclass + for the current class. + + An explicit defined metaclass is defined + either by passing the ``metaclass`` keyword argument + in the class definition line (Python 3) or (Python 2) by + having a ``__metaclass__`` class attribute, or if there are + no explicit bases but there is a global ``__metaclass__`` variable. + """ + if self._metaclass: + # Expects this from Py3k TreeRebuilder + try: + return next(node for node in self._metaclass.infer() + if node is not YES) + except (InferenceError, StopIteration): + return None + if sys.version_info >= (3, ): + return None + + if '__metaclass__' in self.locals: + assignment = self.locals['__metaclass__'][-1] + elif self.bases: + return None + elif '__metaclass__' in self.root().locals: + assignments = [ass for ass in self.root().locals['__metaclass__'] + if ass.lineno < self.lineno] + if not assignments: + return None + assignment = assignments[-1] + else: + return None + + try: + infered = next(assignment.infer()) + except InferenceError: + return + if infered is YES: # don't expose this + return None + return infered + + def metaclass(self): + """ Return the metaclass of this class. + + If this class does not define explicitly a metaclass, + then the first defined metaclass in ancestors will be used + instead. + """ + klass = self._explicit_metaclass() + if klass is None: + for parent in self.ancestors(): + klass = parent.metaclass() + if klass is not None: + break + return klass + + def _islots(self): + """ Return an iterator with the inferred slots. """ + if '__slots__' not in self.locals: + return + for slots in self.igetattr('__slots__'): + # check if __slots__ is a valid type + for meth in ITER_METHODS: + try: + slots.getattr(meth) + break + except NotFoundError: + continue + else: + continue + + if isinstance(slots, Const): + # a string. Ignore the following checks, + # but yield the node, only if it has a value + if slots.value: + yield slots + continue + if not hasattr(slots, 'itered'): + # we can't obtain the values, maybe a .deque? + continue + + if isinstance(slots, Dict): + values = [item[0] for item in slots.items] + else: + values = slots.itered() + if values is YES: + continue + + for elt in values: + try: + for infered in elt.infer(): + if infered is YES: + continue + if (not isinstance(infered, Const) or + not isinstance(infered.value, str)): + continue + if not infered.value: + continue + yield infered + except InferenceError: + continue + + # Cached, because inferring them all the time is expensive + @cached + def slots(self): + """ Return all the slots for this node. """ + return list(self._islots()) diff --git a/astroid/test_utils.py b/astroid/test_utils.py new file mode 100644 index 0000000..7ae9361 --- /dev/null +++ b/astroid/test_utils.py @@ -0,0 +1,213 @@ +"""Utility functions for test code that uses astroid ASTs as input.""" +import functools +import sys +import textwrap + +from astroid import nodes +from astroid import builder +# The name of the transient function that is used to +# wrap expressions to be extracted when calling +# extract_node. +_TRANSIENT_FUNCTION = '__' + +# The comment used to select a statement to be extracted +# when calling extract_node. +_STATEMENT_SELECTOR = '#@' + + +def _extract_expressions(node): + """Find expressions in a call to _TRANSIENT_FUNCTION and extract them. + + The function walks the AST recursively to search for expressions that + are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an + expression, it completely removes the function call node from the tree, + replacing it by the wrapped expression inside the parent. + + :param node: An astroid node. + :type node: astroid.bases.NodeNG + :yields: The sequence of wrapped expressions on the modified tree + expression can be found. + """ + if (isinstance(node, nodes.CallFunc) + and isinstance(node.func, nodes.Name) + and node.func.name == _TRANSIENT_FUNCTION): + real_expr = node.args[0] + real_expr.parent = node.parent + # Search for node in all _astng_fields (the fields checked when + # get_children is called) of its parent. Some of those fields may + # be lists or tuples, in which case the elements need to be checked. + # When we find it, replace it by real_expr, so that the AST looks + # like no call to _TRANSIENT_FUNCTION ever took place. + for name in node.parent._astroid_fields: + child = getattr(node.parent, name) + if isinstance(child, (list, tuple)): + for idx, compound_child in enumerate(child): + if compound_child is node: + child[idx] = real_expr + elif child is node: + setattr(node.parent, name, real_expr) + yield real_expr + else: + for child in node.get_children(): + for result in _extract_expressions(child): + yield result + + +def _find_statement_by_line(node, line): + """Extracts the statement on a specific line from an AST. + + If the line number of node matches line, it will be returned; + otherwise its children are iterated and the function is called + recursively. + + :param node: An astroid node. + :type node: astroid.bases.NodeNG + :param line: The line number of the statement to extract. + :type line: int + :returns: The statement on the line, or None if no statement for the line + can be found. + :rtype: astroid.bases.NodeNG or None + """ + if isinstance(node, (nodes.Class, nodes.Function)): + # This is an inaccuracy in the AST: the nodes that can be + # decorated do not carry explicit information on which line + # the actual definition (class/def), but .fromline seems to + # be close enough. + node_line = node.fromlineno + else: + node_line = node.lineno + + if node_line == line: + return node + + for child in node.get_children(): + result = _find_statement_by_line(child, line) + if result: + return result + + return None + +def extract_node(code, module_name=''): + """Parses some Python code as a module and extracts a designated AST node. + + Statements: + To extract one or more statement nodes, append #@ to the end of the line + + Examples: + >>> def x(): + >>> def y(): + >>> return 1 #@ + + The return statement will be extracted. + + >>> class X(object): + >>> def meth(self): #@ + >>> pass + + The funcion object 'meth' will be extracted. + + Expressions: + To extract arbitrary expressions, surround them with the fake + function call __(...). After parsing, the surrounded expression + will be returned and the whole AST (accessible via the returned + node's parent attribute) will look like the function call was + never there in the first place. + + Examples: + >>> a = __(1) + + The const node will be extracted. + + >>> def x(d=__(foo.bar)): pass + + The node containing the default argument will be extracted. + + >>> def foo(a, b): + >>> return 0 < __(len(a)) < b + + The node containing the function call 'len' will be extracted. + + If no statements or expressions are selected, the last toplevel + statement will be returned. + + If the selected statement is a discard statement, (i.e. an expression + turned into a statement), the wrapped expression is returned instead. + + For convenience, singleton lists are unpacked. + + :param str code: A piece of Python code that is parsed as + a module. Will be passed through textwrap.dedent first. + :param str module_name: The name of the module. + :returns: The designated node from the parse tree, or a list of nodes. + :rtype: astroid.bases.NodeNG, or a list of nodes. + """ + def _extract(node): + if isinstance(node, nodes.Discard): + return node.value + else: + return node + + requested_lines = [] + for idx, line in enumerate(code.splitlines()): + if line.strip().endswith(_STATEMENT_SELECTOR): + requested_lines.append(idx + 1) + + tree = build_module(code, module_name=module_name) + extracted = [] + if requested_lines: + for line in requested_lines: + extracted.append(_find_statement_by_line(tree, line)) + else: + # Modifies the tree. + extracted = list(_extract_expressions(tree)) + + if not extracted: + extracted.append(tree.body[-1]) + + extracted = [_extract(node) for node in extracted] + if len(extracted) == 1: + return extracted[0] + else: + return extracted + + +def build_module(code, module_name=''): + """Parses a string module with a builder. + :param code: The code for the module. + :type code: str + :param module_name: The name for the module + :type module_name: str + :returns: The module AST. + :rtype: astroid.bases.NodeNG + """ + code = textwrap.dedent(code) + return builder.AstroidBuilder(None).string_build(code, modname=module_name) + + +def require_version(minver=None, maxver=None): + """ Compare version of python interpreter to the given one. Skip the test + if older. + """ + def parse(string, default=None): + string = string or default + try: + return tuple(int(v) for v in string.split('.')) + except ValueError: + raise ValueError('%s is not a correct version : should be X.Y[.Z].' % version) + + def check_require_version(f): + current = sys.version_info[:3] + if parse(minver, "0") < current <= parse(maxver, "4"): + return f + else: + str_version = '.'.join(str(v) for v in sys.version_info) + @functools.wraps(f) + def new_f(self, *args, **kwargs): + if minver is not None: + self.skipTest('Needs Python > %s. Current version is %s.' % (minver, str_version)) + elif maxver is not None: + self.skipTest('Needs Python <= %s. Current version is %s.' % (maxver, str_version)) + return new_f + + + return check_require_version diff --git a/astroid/tests/data/MyPyPa-0.1.0-py2.5.egg b/astroid/tests/data/MyPyPa-0.1.0-py2.5.egg new file mode 100644 index 0000000..f62599c Binary files /dev/null and b/astroid/tests/data/MyPyPa-0.1.0-py2.5.egg differ diff --git a/astroid/tests/data/MyPyPa-0.1.0-py2.5.zip b/astroid/tests/data/MyPyPa-0.1.0-py2.5.zip new file mode 100644 index 0000000..f62599c Binary files /dev/null and b/astroid/tests/data/MyPyPa-0.1.0-py2.5.zip differ diff --git a/astroid/tests/data/SSL1/Connection1.py b/astroid/tests/data/SSL1/Connection1.py new file mode 100644 index 0000000..c0f0152 --- /dev/null +++ b/astroid/tests/data/SSL1/Connection1.py @@ -0,0 +1,14 @@ +"""M2Crypto.SSL.Connection + +Copyright (c) 1999-2004 Ng Pheng Siong. All rights reserved.""" + +RCS_id='$Id: Connection1.py,v 1.1 2005-06-13 20:55:22 syt Exp $' + +#Some code deleted here + +class Connection: + + """An SSL connection.""" + + def __init__(self, ctx, sock=None): + print 'init Connection' diff --git a/astroid/tests/data/SSL1/__init__.py b/astroid/tests/data/SSL1/__init__.py new file mode 100644 index 0000000..a007b04 --- /dev/null +++ b/astroid/tests/data/SSL1/__init__.py @@ -0,0 +1 @@ +from Connection1 import Connection diff --git a/astroid/tests/data/__init__.py b/astroid/tests/data/__init__.py new file mode 100644 index 0000000..332e2e7 --- /dev/null +++ b/astroid/tests/data/__init__.py @@ -0,0 +1 @@ +__revision__="$Id: __init__.py,v 1.1 2005-06-13 20:55:20 syt Exp $" diff --git a/astroid/tests/data/absimport.py b/astroid/tests/data/absimport.py new file mode 100644 index 0000000..f98effa --- /dev/null +++ b/astroid/tests/data/absimport.py @@ -0,0 +1,3 @@ +from __future__ import absolute_import +import email +from email import message diff --git a/astroid/tests/data/all.py b/astroid/tests/data/all.py new file mode 100644 index 0000000..23f7d2b --- /dev/null +++ b/astroid/tests/data/all.py @@ -0,0 +1,9 @@ + +name = 'a' +_bla = 2 +other = 'o' +class Aaa: pass + +def func(): print 'yo' + +__all__ = 'Aaa', '_bla', 'name' diff --git a/astroid/tests/data/appl/__init__.py b/astroid/tests/data/appl/__init__.py new file mode 100644 index 0000000..d652ffd --- /dev/null +++ b/astroid/tests/data/appl/__init__.py @@ -0,0 +1,3 @@ +""" +Init +""" diff --git a/astroid/tests/data/appl/myConnection.py b/astroid/tests/data/appl/myConnection.py new file mode 100644 index 0000000..93026a6 --- /dev/null +++ b/astroid/tests/data/appl/myConnection.py @@ -0,0 +1,11 @@ +import SSL1 +class MyConnection(SSL1.Connection): + + """An SSL connection.""" + + def __init__(self, dummy): + print 'MyConnection init' + +if __name__ == '__main__': + myConnection = MyConnection(' ') + raw_input('Press Enter to continue...') diff --git a/astroid/tests/data/email.py b/astroid/tests/data/email.py new file mode 100644 index 0000000..dc59356 --- /dev/null +++ b/astroid/tests/data/email.py @@ -0,0 +1 @@ +"""fake email module to test absolute import doesn't grab this one""" diff --git a/astroid/tests/data/find_test/__init__.py b/astroid/tests/data/find_test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data/find_test/module.py b/astroid/tests/data/find_test/module.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data/find_test/module2.py b/astroid/tests/data/find_test/module2.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data/find_test/noendingnewline.py b/astroid/tests/data/find_test/noendingnewline.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data/find_test/nonregr.py b/astroid/tests/data/find_test/nonregr.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data/format.py b/astroid/tests/data/format.py new file mode 100644 index 0000000..7379706 --- /dev/null +++ b/astroid/tests/data/format.py @@ -0,0 +1,34 @@ +"""A multiline string +""" + +function('aeozrijz\ +earzer', hop) +# XXX write test +x = [i for i in range(5) + if i % 4] + +fonction(1, + 2, + 3, + 4) + +def definition(a, + b, + c): + return a + b + c + +class debile(dict, + object): + pass + +if aaaa: pass +else: + aaaa,bbbb = 1,2 + aaaa,bbbb = bbbb,aaaa +# XXX write test +hop = \ + aaaa + + +__revision__.lower(); + diff --git a/astroid/tests/data/lmfp/__init__.py b/astroid/tests/data/lmfp/__init__.py new file mode 100644 index 0000000..74b26b8 --- /dev/null +++ b/astroid/tests/data/lmfp/__init__.py @@ -0,0 +1,2 @@ +# force a "direct" python import +from . import foo diff --git a/astroid/tests/data/lmfp/foo.py b/astroid/tests/data/lmfp/foo.py new file mode 100644 index 0000000..8f7de1e --- /dev/null +++ b/astroid/tests/data/lmfp/foo.py @@ -0,0 +1,6 @@ +import sys +if not getattr(sys, 'bar', None): + sys.just_once = [] +# there used to be two numbers here because +# of a load_module_from_path bug +sys.just_once.append(42) diff --git a/astroid/tests/data/module.py b/astroid/tests/data/module.py new file mode 100644 index 0000000..fb48dc1 --- /dev/null +++ b/astroid/tests/data/module.py @@ -0,0 +1,89 @@ +"""test module for astroid +""" + +__revision__ = '$Id: module.py,v 1.2 2005-11-02 11:56:54 syt Exp $' +from logilab.common.shellutils import ProgressBar as pb +from astroid import modutils +from astroid.utils import * +import os.path +MY_DICT = {} + +def global_access(key, val): + """function test""" + local = 1 + MY_DICT[key] = val + for i in val: + if i: + del MY_DICT[i] + continue + else: + break + else: + print '!!!' + + +class YO: + """hehe""" + a = 1 + + def __init__(self): + try: + self.yo = 1 + except ValueError, ex: + pass + except (NameError, TypeError): + raise XXXError() + except: + raise + + + +class YOUPI(YO): + class_attr = None + + def __init__(self): + self.member = None + + def method(self): + """method test""" + global MY_DICT + try: + MY_DICT = {} + local = None + autre = [a for (a, b) in MY_DICT if b] + if b in autre: + print 'yo', + else: + if a in autre: + print 'hehe' + global_access(local, val=autre) + finally: + return local + + def static_method(): + """static method test""" + assert MY_DICT, '???' + static_method = staticmethod(static_method) + + def class_method(cls): + """class method test""" + exec a in b + class_method = classmethod(class_method) + + +def four_args(a, b, c, d): + """four arguments (was nested_args)""" + print a, b, c, d + while 1: + if a: + break + a += +1 + else: + b += -2 + if c: + d = ((a) and (b)) or (c) + else: + c = ((a) and (b)) or (d) + map(lambda x, y: (y, x), a) +redirect = four_args + diff --git a/astroid/tests/data/module1abs/__init__.py b/astroid/tests/data/module1abs/__init__.py new file mode 100644 index 0000000..9eb5902 --- /dev/null +++ b/astroid/tests/data/module1abs/__init__.py @@ -0,0 +1,4 @@ +from __future__ import absolute_import +from . import core +from .core import * +print sys.version diff --git a/astroid/tests/data/module1abs/core.py b/astroid/tests/data/module1abs/core.py new file mode 100644 index 0000000..de10111 --- /dev/null +++ b/astroid/tests/data/module1abs/core.py @@ -0,0 +1 @@ +import sys diff --git a/astroid/tests/data/module2.py b/astroid/tests/data/module2.py new file mode 100644 index 0000000..d6bf49e --- /dev/null +++ b/astroid/tests/data/module2.py @@ -0,0 +1,143 @@ +from data.module import YO, YOUPI +import data + + +class Specialization(YOUPI, YO): + pass + + + +class Metaclass(type): + pass + + + +class Interface: + pass + + + +class MyIFace(Interface): + pass + + + +class AnotherIFace(Interface): + pass + + + +class MyException(Exception): + pass + + + +class MyError(MyException): + pass + + + +class AbstractClass(object): + + def to_override(self, whatever): + raise NotImplementedError() + + def return_something(self, param): + if param: + return 'toto' + return + + + +class Concrete0: + __implements__ = MyIFace + + + +class Concrete1: + __implements__ = (MyIFace, AnotherIFace) + + + +class Concrete2: + __implements__ = (MyIFace, AnotherIFace) + + + +class Concrete23(Concrete1): + pass + +del YO.member +del YO +[SYN1, SYN2] = (Concrete0, Concrete1) +assert `1` +b = (1) | (((2) & (3)) ^ (8)) +bb = ((1) | (two)) | (6) +ccc = ((one) & (two)) & (three) +dddd = ((x) ^ (o)) ^ (r) +exec 'c = 3' +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) +c = (b) // (2) +c = (b) - (2) +c = (b) % (2) +c = (b) ** (2) +c = (b) << (2) +c = (b) >> (2) +c = ~b +c = not b +d = [c] +e = d[:] +e = d[a:b:c] +raise_string(*args, **kwargs) +print >> stream, 'bonjour' +print >> stream, 'salut', + +def make_class(any, base=data.module.YO, *args, **kwargs): + """check base is correctly resolved to Concrete0""" + + + class Aaaa(base): + """dynamic class""" + + + return Aaaa +from os.path import abspath +import os as myos + + +class A: + pass + + + +class A(A): + pass + + +def generator(): + """A generator.""" + yield + +def not_a_generator(): + """A function that contains generator, but is not one.""" + + def generator(): + yield + genl = lambda : (yield) + +def with_metaclass(meta, *bases): + return meta('NewBase', bases, {}) + + +class NotMetaclass(with_metaclass(Metaclass)): + pass + + diff --git a/astroid/tests/data/noendingnewline.py b/astroid/tests/data/noendingnewline.py new file mode 100644 index 0000000..e1d6e4a --- /dev/null +++ b/astroid/tests/data/noendingnewline.py @@ -0,0 +1,36 @@ +import unittest + + +class TestCase(unittest.TestCase): + + def setUp(self): + unittest.TestCase.setUp(self) + + + def tearDown(self): + unittest.TestCase.tearDown(self) + + def testIt(self): + self.a = 10 + self.xxx() + + + def xxx(self): + if False: + pass + print 'a' + + if False: + pass + pass + + if False: + pass + print 'rara' + + +if __name__ == '__main__': + print 'test2' + unittest.main() + + diff --git a/astroid/tests/data/nonregr.py b/astroid/tests/data/nonregr.py new file mode 100644 index 0000000..a670e39 --- /dev/null +++ b/astroid/tests/data/nonregr.py @@ -0,0 +1,57 @@ +from __future__ import generators + +try: + enumerate = enumerate +except NameError: + + def enumerate(iterable): + """emulates the python2.3 enumerate() function""" + i = 0 + for val in iterable: + yield i, val + i += 1 + +def toto(value): + for k, v in value: + print v.get('yo') + + +import imp +fp, mpath, desc = imp.find_module('optparse',a) +s_opt = imp.load_module('std_optparse', fp, mpath, desc) + +class OptionParser(s_opt.OptionParser): + + def parse_args(self, args=None, values=None, real_optparse=False): + if real_optparse: + pass +## return super(OptionParser, self).parse_args() + else: + import optcomp + optcomp.completion(self) + + +class Aaa(object): + """docstring""" + def __init__(self): + self.__setattr__('a','b') + pass + + def one_public(self): + """docstring""" + pass + + def another_public(self): + """docstring""" + pass + +class Ccc(Aaa): + """docstring""" + + class Ddd(Aaa): + """docstring""" + pass + + class Eee(Ddd): + """docstring""" + pass diff --git a/astroid/tests/data/notall.py b/astroid/tests/data/notall.py new file mode 100644 index 0000000..b998d88 --- /dev/null +++ b/astroid/tests/data/notall.py @@ -0,0 +1,8 @@ + +name = 'a' +_bla = 2 +other = 'o' +class Aaa: pass + +def func(): print 'yo' + diff --git a/astroid/tests/data2/__init__.py b/astroid/tests/data2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data2/clientmodule_test.py b/astroid/tests/data2/clientmodule_test.py new file mode 100644 index 0000000..196fff8 --- /dev/null +++ b/astroid/tests/data2/clientmodule_test.py @@ -0,0 +1,32 @@ +""" docstring for file clientmodule.py """ +from data2.suppliermodule_test import Interface as IFace, DoNothing + +class Toto: pass + +class Ancestor: + """ Ancestor method """ + __implements__ = (IFace,) + + def __init__(self, value): + local_variable = 0 + self.attr = 'this method shouldn\'t have a docstring' + self.__value = value + + def get_value(self): + """ nice docstring ;-) """ + return self.__value + + def set_value(self, value): + self.__value = value + return 'this method shouldn\'t have a docstring' + +class Specialization(Ancestor): + TYPE = 'final class' + top = 'class' + + def __init__(self, value, _id): + Ancestor.__init__(self, value) + self._id = _id + self.relation = DoNothing() + self.toto = Toto() + diff --git a/astroid/tests/data2/suppliermodule_test.py b/astroid/tests/data2/suppliermodule_test.py new file mode 100644 index 0000000..ddacb47 --- /dev/null +++ b/astroid/tests/data2/suppliermodule_test.py @@ -0,0 +1,13 @@ +""" file suppliermodule.py """ + +class NotImplemented(Exception): + pass + +class Interface: + def get_value(self): + raise NotImplemented() + + def set_value(self, value): + raise NotImplemented() + +class DoNothing : pass diff --git a/astroid/tests/data_py3/MyPyPa-0.1.0-py2.5.egg b/astroid/tests/data_py3/MyPyPa-0.1.0-py2.5.egg new file mode 100644 index 0000000..f62599c Binary files /dev/null and b/astroid/tests/data_py3/MyPyPa-0.1.0-py2.5.egg differ diff --git a/astroid/tests/data_py3/MyPyPa-0.1.0-py2.5.zip b/astroid/tests/data_py3/MyPyPa-0.1.0-py2.5.zip new file mode 100644 index 0000000..f62599c Binary files /dev/null and b/astroid/tests/data_py3/MyPyPa-0.1.0-py2.5.zip differ diff --git a/astroid/tests/data_py3/SSL1/Connection1.py b/astroid/tests/data_py3/SSL1/Connection1.py new file mode 100644 index 0000000..7373271 --- /dev/null +++ b/astroid/tests/data_py3/SSL1/Connection1.py @@ -0,0 +1,14 @@ +"""M2Crypto.SSL.Connection + +Copyright (c) 1999-2004 Ng Pheng Siong. All rights reserved.""" + +RCS_id='$Id: Connection1.py,v 1.1 2005-06-13 20:55:22 syt Exp $' + +#Some code deleted here + +class Connection: + + """An SSL connection.""" + + def __init__(self, ctx, sock=None): + print('init Connection') diff --git a/astroid/tests/data_py3/SSL1/__init__.py b/astroid/tests/data_py3/SSL1/__init__.py new file mode 100644 index 0000000..c83eded --- /dev/null +++ b/astroid/tests/data_py3/SSL1/__init__.py @@ -0,0 +1 @@ +from .Connection1 import Connection diff --git a/astroid/tests/data_py3/__init__.py b/astroid/tests/data_py3/__init__.py new file mode 100644 index 0000000..332e2e7 --- /dev/null +++ b/astroid/tests/data_py3/__init__.py @@ -0,0 +1 @@ +__revision__="$Id: __init__.py,v 1.1 2005-06-13 20:55:20 syt Exp $" diff --git a/astroid/tests/data_py3/absimport.py b/astroid/tests/data_py3/absimport.py new file mode 100644 index 0000000..88f9d95 --- /dev/null +++ b/astroid/tests/data_py3/absimport.py @@ -0,0 +1,3 @@ + +import email +from email import message diff --git a/astroid/tests/data_py3/all.py b/astroid/tests/data_py3/all.py new file mode 100644 index 0000000..587765b --- /dev/null +++ b/astroid/tests/data_py3/all.py @@ -0,0 +1,9 @@ + +name = 'a' +_bla = 2 +other = 'o' +class Aaa: pass + +def func(): print('yo') + +__all__ = 'Aaa', '_bla', 'name' diff --git a/astroid/tests/data_py3/appl/__init__.py b/astroid/tests/data_py3/appl/__init__.py new file mode 100644 index 0000000..d652ffd --- /dev/null +++ b/astroid/tests/data_py3/appl/__init__.py @@ -0,0 +1,3 @@ +""" +Init +""" diff --git a/astroid/tests/data_py3/appl/myConnection.py b/astroid/tests/data_py3/appl/myConnection.py new file mode 100644 index 0000000..b5b206a --- /dev/null +++ b/astroid/tests/data_py3/appl/myConnection.py @@ -0,0 +1,11 @@ +import SSL1 +class MyConnection(SSL1.Connection): + + """An SSL connection.""" + + def __init__(self, dummy): + print('MyConnection init') + +if __name__ == '__main__': + myConnection = MyConnection(' ') + input('Press Enter to continue...') diff --git a/astroid/tests/data_py3/email.py b/astroid/tests/data_py3/email.py new file mode 100644 index 0000000..dc59356 --- /dev/null +++ b/astroid/tests/data_py3/email.py @@ -0,0 +1 @@ +"""fake email module to test absolute import doesn't grab this one""" diff --git a/astroid/tests/data_py3/find_test/__init__.py b/astroid/tests/data_py3/find_test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data_py3/find_test/module.py b/astroid/tests/data_py3/find_test/module.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data_py3/find_test/module2.py b/astroid/tests/data_py3/find_test/module2.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data_py3/find_test/noendingnewline.py b/astroid/tests/data_py3/find_test/noendingnewline.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data_py3/find_test/nonregr.py b/astroid/tests/data_py3/find_test/nonregr.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/data_py3/format.py b/astroid/tests/data_py3/format.py new file mode 100644 index 0000000..7379706 --- /dev/null +++ b/astroid/tests/data_py3/format.py @@ -0,0 +1,34 @@ +"""A multiline string +""" + +function('aeozrijz\ +earzer', hop) +# XXX write test +x = [i for i in range(5) + if i % 4] + +fonction(1, + 2, + 3, + 4) + +def definition(a, + b, + c): + return a + b + c + +class debile(dict, + object): + pass + +if aaaa: pass +else: + aaaa,bbbb = 1,2 + aaaa,bbbb = bbbb,aaaa +# XXX write test +hop = \ + aaaa + + +__revision__.lower(); + diff --git a/astroid/tests/data_py3/lmfp/__init__.py b/astroid/tests/data_py3/lmfp/__init__.py new file mode 100644 index 0000000..74b26b8 --- /dev/null +++ b/astroid/tests/data_py3/lmfp/__init__.py @@ -0,0 +1,2 @@ +# force a "direct" python import +from . import foo diff --git a/astroid/tests/data_py3/lmfp/foo.py b/astroid/tests/data_py3/lmfp/foo.py new file mode 100644 index 0000000..8f7de1e --- /dev/null +++ b/astroid/tests/data_py3/lmfp/foo.py @@ -0,0 +1,6 @@ +import sys +if not getattr(sys, 'bar', None): + sys.just_once = [] +# there used to be two numbers here because +# of a load_module_from_path bug +sys.just_once.append(42) diff --git a/astroid/tests/data_py3/module.py b/astroid/tests/data_py3/module.py new file mode 100644 index 0000000..ec5b64a --- /dev/null +++ b/astroid/tests/data_py3/module.py @@ -0,0 +1,89 @@ +"""test module for astroid +""" + +__revision__ = '$Id: module.py,v 1.2 2005-11-02 11:56:54 syt Exp $' +from logilab.common.shellutils import ProgressBar as pb +from astroid import modutils +from astroid.utils import * +import os.path +MY_DICT = {} + +def global_access(key, val): + """function test""" + local = 1 + MY_DICT[key] = val + for i in val: + if i: + del MY_DICT[i] + continue + else: + break + else: + print('!!!') + + +class YO: + """hehe""" + a = 1 + + def __init__(self): + try: + self.yo = 1 + except ValueError as ex: + pass + except (NameError, TypeError): + raise XXXError() + except: + raise + + + +class YOUPI(YO): + class_attr = None + + def __init__(self): + self.member = None + + def method(self): + """method test""" + global MY_DICT + try: + MY_DICT = {} + local = None + autre = [a for (a, b) in MY_DICT if b] + if b in autre: + print('yo', end=' ') + else: + if a in autre: + print('hehe') + global_access(local, val=autre) + finally: + return local + + def static_method(): + """static method test""" + assert MY_DICT, '???' + static_method = staticmethod(static_method) + + def class_method(cls): + """class method test""" + exec(a, b) + class_method = classmethod(class_method) + + +def four_args(a, b, c, d): + """four arguments (was nested_args)""" + print(a, b, c, d) + while 1: + if a: + break + a += +1 + else: + b += -2 + if c: + d = ((a) and (b)) or (c) + else: + c = ((a) and (b)) or (d) + list(map(lambda x, y: (y, x), a)) +redirect = four_args + diff --git a/astroid/tests/data_py3/module1abs/__init__.py b/astroid/tests/data_py3/module1abs/__init__.py new file mode 100644 index 0000000..f9d5b68 --- /dev/null +++ b/astroid/tests/data_py3/module1abs/__init__.py @@ -0,0 +1,4 @@ + +from . import core +from .core import * +print(sys.version) diff --git a/astroid/tests/data_py3/module1abs/core.py b/astroid/tests/data_py3/module1abs/core.py new file mode 100644 index 0000000..de10111 --- /dev/null +++ b/astroid/tests/data_py3/module1abs/core.py @@ -0,0 +1 @@ +import sys diff --git a/astroid/tests/data_py3/module2.py b/astroid/tests/data_py3/module2.py new file mode 100644 index 0000000..94a8bc7 --- /dev/null +++ b/astroid/tests/data_py3/module2.py @@ -0,0 +1,143 @@ +from data_py3.module import YO, YOUPI +import data_py3 + + +class Specialization(YOUPI, YO): + pass + + + +class Metaclass(type): + pass + + + +class Interface: + pass + + + +class MyIFace(Interface): + pass + + + +class AnotherIFace(Interface): + pass + + + +class MyException(Exception): + pass + + + +class MyError(MyException): + pass + + + +class AbstractClass(object): + + def to_override(self, whatever): + raise NotImplementedError() + + def return_something(self, param): + if param: + return 'toto' + return + + + +class Concrete0: + __implements__ = MyIFace + + + +class Concrete1: + __implements__ = (MyIFace, AnotherIFace) + + + +class Concrete2: + __implements__ = (MyIFace, AnotherIFace) + + + +class Concrete23(Concrete1): + pass + +del YO.member +del YO +[SYN1, SYN2] = (Concrete0, Concrete1) +assert repr(1) +b = (1) | (((2) & (3)) ^ (8)) +bb = ((1) | (two)) | (6) +ccc = ((one) & (two)) & (three) +dddd = ((x) ^ (o)) ^ (r) +exec('c = 3') +exec('c = 3', {}, {}) + +def raise_string(a=2, *args, **kwargs): + raise Exception('yo') + yield 'coucou' + yield +a = (b) + (2) +c = (b) * (2) +c = (b) / (2) +c = (b) // (2) +c = (b) - (2) +c = (b) % (2) +c = (b) ** (2) +c = (b) << (2) +c = (b) >> (2) +c = ~b +c = not b +d = [c] +e = d[:] +e = d[a:b:c] +raise_string(*args, **kwargs) +print('bonjour', file=stream) +print('salut', end=' ', file=stream) + +def make_class(any, base=data_py3.module.YO, *args, **kwargs): + """check base is correctly resolved to Concrete0""" + + + class Aaaa(base): + """dynamic class""" + + + return Aaaa +from os.path import abspath +import os as myos + + +class A: + pass + + + +class A(A): + pass + + +def generator(): + """A generator.""" + yield + +def not_a_generator(): + """A function that contains generator, but is not one.""" + + def generator(): + yield + genl = lambda : (yield) + +def with_metaclass(meta, *bases): + return meta('NewBase', bases, {}) + + +class NotMetaclass(with_metaclass(Metaclass)): + pass + + diff --git a/astroid/tests/data_py3/noendingnewline.py b/astroid/tests/data_py3/noendingnewline.py new file mode 100644 index 0000000..e17b92c --- /dev/null +++ b/astroid/tests/data_py3/noendingnewline.py @@ -0,0 +1,36 @@ +import unittest + + +class TestCase(unittest.TestCase): + + def setUp(self): + unittest.TestCase.setUp(self) + + + def tearDown(self): + unittest.TestCase.tearDown(self) + + def testIt(self): + self.a = 10 + self.xxx() + + + def xxx(self): + if False: + pass + print('a') + + if False: + pass + pass + + if False: + pass + print('rara') + + +if __name__ == '__main__': + print('test2') + unittest.main() + + diff --git a/astroid/tests/data_py3/nonregr.py b/astroid/tests/data_py3/nonregr.py new file mode 100644 index 0000000..78765c8 --- /dev/null +++ b/astroid/tests/data_py3/nonregr.py @@ -0,0 +1,57 @@ + + +try: + enumerate = enumerate +except NameError: + + def enumerate(iterable): + """emulates the python2.3 enumerate() function""" + i = 0 + for val in iterable: + yield i, val + i += 1 + +def toto(value): + for k, v in value: + print(v.get('yo')) + + +import imp +fp, mpath, desc = imp.find_module('optparse',a) +s_opt = imp.load_module('std_optparse', fp, mpath, desc) + +class OptionParser(s_opt.OptionParser): + + def parse_args(self, args=None, values=None, real_optparse=False): + if real_optparse: + pass +## return super(OptionParser, self).parse_args() + else: + import optcomp + optcomp.completion(self) + + +class Aaa(object): + """docstring""" + def __init__(self): + self.__setattr__('a','b') + pass + + def one_public(self): + """docstring""" + pass + + def another_public(self): + """docstring""" + pass + +class Ccc(Aaa): + """docstring""" + + class Ddd(Aaa): + """docstring""" + pass + + class Eee(Ddd): + """docstring""" + pass diff --git a/astroid/tests/data_py3/notall.py b/astroid/tests/data_py3/notall.py new file mode 100644 index 0000000..9d35aa3 --- /dev/null +++ b/astroid/tests/data_py3/notall.py @@ -0,0 +1,8 @@ + +name = 'a' +_bla = 2 +other = 'o' +class Aaa: pass + +def func(): print('yo') + diff --git a/astroid/tests/regrtest_data/absimp/__init__.py b/astroid/tests/regrtest_data/absimp/__init__.py new file mode 100644 index 0000000..b98444d --- /dev/null +++ b/astroid/tests/regrtest_data/absimp/__init__.py @@ -0,0 +1,5 @@ +"""a package with absolute import activated +""" + +from __future__ import absolute_import + diff --git a/astroid/tests/regrtest_data/absimp/sidepackage/__init__.py b/astroid/tests/regrtest_data/absimp/sidepackage/__init__.py new file mode 100644 index 0000000..239499a --- /dev/null +++ b/astroid/tests/regrtest_data/absimp/sidepackage/__init__.py @@ -0,0 +1,3 @@ +"""a side package with nothing in it +""" + diff --git a/astroid/tests/regrtest_data/absimp/string.py b/astroid/tests/regrtest_data/absimp/string.py new file mode 100644 index 0000000..e68e749 --- /dev/null +++ b/astroid/tests/regrtest_data/absimp/string.py @@ -0,0 +1,3 @@ +from __future__ import absolute_import, print_function +import string +print(string) diff --git a/astroid/tests/regrtest_data/descriptor_crash.py b/astroid/tests/regrtest_data/descriptor_crash.py new file mode 100644 index 0000000..11fbb4a --- /dev/null +++ b/astroid/tests/regrtest_data/descriptor_crash.py @@ -0,0 +1,11 @@ + +import urllib + +class Page(object): + _urlOpen = staticmethod(urllib.urlopen) + + def getPage(self, url): + handle = self._urlOpen(url) + data = handle.read() + handle.close() + return data diff --git a/astroid/tests/regrtest_data/package/__init__.py b/astroid/tests/regrtest_data/package/__init__.py new file mode 100644 index 0000000..575d18b --- /dev/null +++ b/astroid/tests/regrtest_data/package/__init__.py @@ -0,0 +1,4 @@ +"""package's __init__ file""" + + +from . import subpackage diff --git a/astroid/tests/regrtest_data/package/absimport.py b/astroid/tests/regrtest_data/package/absimport.py new file mode 100644 index 0000000..33ed117 --- /dev/null +++ b/astroid/tests/regrtest_data/package/absimport.py @@ -0,0 +1,6 @@ +from __future__ import absolute_import, print_function +import import_package_subpackage_module # fail +print(import_package_subpackage_module) + +from . import hello as hola + diff --git a/astroid/tests/regrtest_data/package/hello.py b/astroid/tests/regrtest_data/package/hello.py new file mode 100644 index 0000000..b154c84 --- /dev/null +++ b/astroid/tests/regrtest_data/package/hello.py @@ -0,0 +1,2 @@ +"""hello module""" + diff --git a/astroid/tests/regrtest_data/package/import_package_subpackage_module.py b/astroid/tests/regrtest_data/package/import_package_subpackage_module.py new file mode 100644 index 0000000..ad442c1 --- /dev/null +++ b/astroid/tests/regrtest_data/package/import_package_subpackage_module.py @@ -0,0 +1,49 @@ +# pylint: disable-msg=I0011,C0301,W0611 +"""I found some of my scripts trigger off an AttributeError in pylint +0.8.1 (with common 0.12.0 and astroid 0.13.1). + +Traceback (most recent call last): + File "/usr/bin/pylint", line 4, in ? + lint.Run(sys.argv[1:]) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 729, in __init__ + linter.check(args) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 412, in check + self.check_file(filepath, modname, checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 426, in check_file + astroid = self._check_file(filepath, modname, checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 450, in _check_file + self.check_astroid_module(astroid, checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 494, in check_astroid_module + self.astroid_events(astroid, [checker for checker in checkers + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astroid_events + self.astroid_events(child, checkers, _reversed_checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astroid_events + self.astroid_events(child, checkers, _reversed_checkers) + File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 508, in astroid_events + checker.visit(astroid) + File "/usr/lib/python2.4/site-packages/logilab/astroid/utils.py", line 84, in visit + method(node) + File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 295, in visit_import + self._check_module_attrs(node, module, name_parts[1:]) + File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 357, in _check_module_attrs + self.add_message('E0611', args=(name, module.name), +AttributeError: Import instance has no attribute 'name' + + +You can reproduce it by: +(1) create package structure like the following: + +package/ + __init__.py + subpackage/ + __init__.py + module.py + +(2) in package/__init__.py write: + +import subpackage + +(3) run pylint with a script importing package.subpackage.module. +""" +__revision__ = '$Id: import_package_subpackage_module.py,v 1.1 2005-11-10 15:59:32 syt Exp $' +import package.subpackage.module diff --git a/astroid/tests/regrtest_data/package/subpackage/__init__.py b/astroid/tests/regrtest_data/package/subpackage/__init__.py new file mode 100644 index 0000000..dc4782e --- /dev/null +++ b/astroid/tests/regrtest_data/package/subpackage/__init__.py @@ -0,0 +1 @@ +"""package.subpackage""" diff --git a/astroid/tests/regrtest_data/package/subpackage/module.py b/astroid/tests/regrtest_data/package/subpackage/module.py new file mode 100644 index 0000000..4b7244b --- /dev/null +++ b/astroid/tests/regrtest_data/package/subpackage/module.py @@ -0,0 +1 @@ +"""package.subpackage.module""" diff --git a/astroid/tests/regrtest_data/recursion.py b/astroid/tests/regrtest_data/recursion.py new file mode 100644 index 0000000..a34dad3 --- /dev/null +++ b/astroid/tests/regrtest_data/recursion.py @@ -0,0 +1,3 @@ +""" For issue #25 """ +class Base(object): + pass \ No newline at end of file diff --git a/astroid/tests/regrtest_data/unicode_package/__init__.py b/astroid/tests/regrtest_data/unicode_package/__init__.py new file mode 100644 index 0000000..713e559 --- /dev/null +++ b/astroid/tests/regrtest_data/unicode_package/__init__.py @@ -0,0 +1 @@ +x = "șțîâ" \ No newline at end of file diff --git a/astroid/tests/regrtest_data/unicode_package/core/__init__.py b/astroid/tests/regrtest_data/unicode_package/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/astroid/tests/unittest_brain.py b/astroid/tests/unittest_brain.py new file mode 100644 index 0000000..8fa853c --- /dev/null +++ b/astroid/tests/unittest_brain.py @@ -0,0 +1,99 @@ +# Copyright 2013 Google Inc. All Rights Reserved. +# +# This file is part of astroid. +# +# 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 . +"""Tests for basic functionality in astroid.brain.""" +import sys + +import unittest + +from astroid import MANAGER +from astroid import bases +from astroid import test_utils +import astroid + +class HashlibTC(unittest.TestCase): + def test_hashlib(self): + """Tests that brain extensions for hashlib work.""" + hashlib_module = MANAGER.ast_from_module_name('hashlib') + for class_name in ['md5', 'sha1']: + class_obj = hashlib_module[class_name] + self.assertIn('update', class_obj) + self.assertIn('digest', class_obj) + self.assertIn('hexdigest', class_obj) + self.assertEqual(len(class_obj['__init__'].args.args), 2) + self.assertEqual(len(class_obj['__init__'].args.defaults), 1) + self.assertEqual(len(class_obj['update'].args.args), 2) + self.assertEqual(len(class_obj['digest'].args.args), 1) + self.assertEqual(len(class_obj['hexdigest'].args.args), 1) + + +class NamedTupleTest(unittest.TestCase): + def test_namedtuple_base(self): + klass = test_utils.extract_node(""" + from collections import namedtuple + + class X(namedtuple("X", ["a", "b", "c"])): + pass + """) + self.assertEqual( + [anc.name for anc in klass.ancestors()], + ['X', 'tuple', 'object']) + for anc in klass.ancestors(): + self.assertFalse(anc.parent is None) + + def test_namedtuple_inference(self): + klass = test_utils.extract_node(""" + from collections import namedtuple + + name = "X" + fields = ["a", "b", "c"] + class X(namedtuple(name, fields)): + pass + """) + for base in klass.ancestors(): + if base.name == 'X': + break + self.assertSetEqual({"a", "b", "c"}, set(base.instance_attrs)) + + def test_namedtuple_inference_failure(self): + klass = test_utils.extract_node(""" + from collections import namedtuple + + def foo(fields): + return __(namedtuple("foo", fields)) + """) + self.assertIs(bases.YES, next(klass.infer())) + + + def test_namedtuple_advanced_inference(self): + if sys.version_info[0] > 2: + self.skipTest('Currently broken for Python 3.') + # urlparse return an object of class ParseResult, which has a + # namedtuple call and a mixin as base classes + result = test_utils.extract_node(""" + import urlparse + + result = __(urlparse.urlparse('gopher://')) + """) + instance = next(result.infer()) + self.assertEqual(len(instance.getattr('scheme')), 1) + self.assertEqual(len(instance.getattr('port')), 1) + with self.assertRaises(astroid.NotFoundError): + instance.getattr('foo') + + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_builder.py b/astroid/tests/unittest_builder.py new file mode 100644 index 0000000..47ae158 --- /dev/null +++ b/astroid/tests/unittest_builder.py @@ -0,0 +1,781 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""tests for the astroid builder and rebuilder module""" + +import unittest +import sys +from os.path import join, abspath, dirname +from functools import partial + +from pprint import pprint + +from astroid import builder, nodes, InferenceError, NotFoundError +from astroid.nodes import Module +from astroid.bases import YES, BUILTINS +from astroid.manager import AstroidManager +from astroid import test_utils + +MANAGER = AstroidManager() + +PY3K = sys.version_info >= (3, 0) + +if PY3K: + DATA = join(dirname(abspath(__file__)), 'data_py3') +else: + DATA = join(dirname(abspath(__file__)), 'data') + +from unittest_inference import get_name_node + +if PY3K: + import data_py3 as data + from data_py3 import module as test_module +else: + import data + from data import module as test_module + + +class FromToLineNoTC(unittest.TestCase): + + astroid = builder.AstroidBuilder().file_build(join(DATA, 'format.py')) + + def test_callfunc_lineno(self): + stmts = self.astroid.body + # on line 4: + # function('aeozrijz\ + # earzer', hop) + discard = stmts[0] + self.assertIsInstance(discard, nodes.Discard) + self.assertEqual(discard.fromlineno, 4) + self.assertEqual(discard.tolineno, 5) + callfunc = discard.value + self.assertIsInstance(callfunc, nodes.CallFunc) + self.assertEqual(callfunc.fromlineno, 4) + self.assertEqual(callfunc.tolineno, 5) + name = callfunc.func + self.assertIsInstance(name, nodes.Name) + self.assertEqual(name.fromlineno, 4) + self.assertEqual(name.tolineno, 4) + strarg = callfunc.args[0] + self.assertIsInstance(strarg, nodes.Const) + if hasattr(sys, 'pypy_version_info'): + lineno = 4 + else: + lineno = 5 # no way for this one in CPython (is 4 actually) + self.assertEqual(strarg.fromlineno, lineno) + self.assertEqual(strarg.tolineno, lineno) + namearg = callfunc.args[1] + self.assertIsInstance(namearg, nodes.Name) + self.assertEqual(namearg.fromlineno, 5) + self.assertEqual(namearg.tolineno, 5) + # on line 10: + # fonction(1, + # 2, + # 3, + # 4) + discard = stmts[2] + self.assertIsInstance(discard, nodes.Discard) + self.assertEqual(discard.fromlineno, 10) + self.assertEqual(discard.tolineno, 13) + callfunc = discard.value + self.assertIsInstance(callfunc, nodes.CallFunc) + self.assertEqual(callfunc.fromlineno, 10) + self.assertEqual(callfunc.tolineno, 13) + name = callfunc.func + self.assertIsInstance(name, nodes.Name) + self.assertEqual(name.fromlineno, 10) + self.assertEqual(name.tolineno, 10) + for i, arg in enumerate(callfunc.args): + self.assertIsInstance(arg, nodes.Const) + self.assertEqual(arg.fromlineno, 10+i) + self.assertEqual(arg.tolineno, 10+i) + + def test_function_lineno(self): + stmts = self.astroid.body + # on line 15: + # def definition(a, + # b, + # c): + # return a + b + c + function = stmts[3] + self.assertIsInstance(function, nodes.Function) + self.assertEqual(function.fromlineno, 15) + self.assertEqual(function.tolineno, 18) + 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): + astroid = builder.AstroidBuilder().string_build(''' +@decorator +def function( + arg): + print (arg) +''', __name__, __file__) + function = astroid['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.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): + stmts = self.astroid.body + # on line 20: + # class debile(dict, + # object): + # pass + class_ = stmts[4] + self.assertIsInstance(class_, nodes.Class) + self.assertEqual(class_.fromlineno, 20) + self.assertEqual(class_.tolineno, 22) + self.assertEqual(class_.blockstart_tolineno, 21) + pass_ = class_.body[0] + self.assertIsInstance(pass_, nodes.Pass) + self.assertEqual(pass_.fromlineno, 22) + self.assertEqual(pass_.tolineno, 22) + + def test_if_lineno(self): + stmts = self.astroid.body + # on line 20: + # if aaaa: pass + # else: + # aaaa,bbbb = 1,2 + # aaaa,bbbb = bbbb,aaaa + if_ = stmts[5] + self.assertIsInstance(if_, nodes.If) + self.assertEqual(if_.fromlineno, 24) + self.assertEqual(if_.tolineno, 27) + self.assertEqual(if_.blockstart_tolineno, 24) + self.assertEqual(if_.orelse[0].fromlineno, 26) + self.assertEqual(if_.orelse[1].tolineno, 27) + + def test_for_while_lineno(self): + for code in (''' +for a in range(4): + print (a) + break +else: + print ("bouh") +''', ''' +while a: + print (a) + break +else: + print ("bouh") +''', + ): + astroid = builder.AstroidBuilder().string_build(code, __name__, __file__) + stmt = astroid.body[0] + self.assertEqual(stmt.fromlineno, 2) + self.assertEqual(stmt.tolineno, 6) + self.assertEqual(stmt.blockstart_tolineno, 2) + self.assertEqual(stmt.orelse[0].fromlineno, 6) # XXX + self.assertEqual(stmt.orelse[0].tolineno, 6) + + + def test_try_except_lineno(self): + astroid = builder.AstroidBuilder().string_build(''' +try: + print (a) +except: + pass +else: + print ("bouh") +''', __name__, __file__) + try_ = astroid.body[0] + self.assertEqual(try_.fromlineno, 2) + self.assertEqual(try_.tolineno, 7) + self.assertEqual(try_.blockstart_tolineno, 2) + self.assertEqual(try_.orelse[0].fromlineno, 7) # XXX + self.assertEqual(try_.orelse[0].tolineno, 7) + hdlr = try_.handlers[0] + self.assertEqual(hdlr.fromlineno, 4) + self.assertEqual(hdlr.tolineno, 5) + self.assertEqual(hdlr.blockstart_tolineno, 4) + + + def test_try_finally_lineno(self): + astroid = builder.AstroidBuilder().string_build(''' +try: + print (a) +finally: + print ("bouh") +''', __name__, __file__) + try_ = astroid.body[0] + self.assertEqual(try_.fromlineno, 2) + self.assertEqual(try_.tolineno, 5) + self.assertEqual(try_.blockstart_tolineno, 2) + self.assertEqual(try_.finalbody[0].fromlineno, 5) # XXX + self.assertEqual(try_.finalbody[0].tolineno, 5) + + + def test_try_finally_25_lineno(self): + astroid = builder.AstroidBuilder().string_build(''' +try: + print (a) +except: + pass +finally: + print ("bouh") +''', __name__, __file__) + try_ = astroid.body[0] + self.assertEqual(try_.fromlineno, 2) + self.assertEqual(try_.tolineno, 7) + self.assertEqual(try_.blockstart_tolineno, 2) + self.assertEqual(try_.finalbody[0].fromlineno, 7) # XXX + self.assertEqual(try_.finalbody[0].tolineno, 7) + + + def test_with_lineno(self): + astroid = builder.AstroidBuilder().string_build(''' +from __future__ import with_statement +with file("/tmp/pouet") as f: + print (f) +''', __name__, __file__) + with_ = astroid.body[1] + self.assertEqual(with_.fromlineno, 3) + self.assertEqual(with_.tolineno, 4) + self.assertEqual(with_.blockstart_tolineno, 3) + + + +class BuilderTC(unittest.TestCase): + + def setUp(self): + self.builder = builder.AstroidBuilder() + + def test_border_cases(self): + """check that a file with no trailing new line is parseable""" + self.builder.file_build(join(DATA, 'noendingnewline.py'), 'data.noendingnewline') + self.assertRaises(builder.AstroidBuildingException, + self.builder.file_build, join(DATA, 'inexistant.py'), 'whatever') + + def test_inspect_build0(self): + """test astroid tree build from a living object""" + builtin_ast = MANAGER.ast_from_module_name(BUILTINS) + if sys.version_info < (3, 0): + fclass = builtin_ast['file'] + self.assertIn('name', fclass) + self.assertIn('mode', fclass) + self.assertIn('read', fclass) + self.assertTrue(fclass.newstyle) + self.assertTrue(fclass.pytype(), '%s.type' % BUILTINS) + self.assertIsInstance(fclass['read'], nodes.Function) + # check builtin function has args.args == None + dclass = builtin_ast['dict'] + self.assertIsNone(dclass['has_key'].args.args) + # just check type and object are there + builtin_ast.getattr('type') + objectastroid = builtin_ast.getattr('object')[0] + self.assertIsInstance(objectastroid.getattr('__new__')[0], nodes.Function) + # check open file alias + builtin_ast.getattr('open') + # check 'help' is there (defined dynamically by site.py) + builtin_ast.getattr('help') + # check property has __init__ + pclass = builtin_ast['property'] + self.assertIn('__init__', pclass) + self.assertIsInstance(builtin_ast['None'], nodes.Const) + self.assertIsInstance(builtin_ast['True'], nodes.Const) + self.assertIsInstance(builtin_ast['False'], nodes.Const) + if sys.version_info < (3, 0): + self.assertIsInstance(builtin_ast['Exception'], nodes.From) + self.assertIsInstance(builtin_ast['NotImplementedError'], nodes.From) + else: + self.assertIsInstance(builtin_ast['Exception'], nodes.Class) + self.assertIsInstance(builtin_ast['NotImplementedError'], nodes.Class) + + def test_inspect_build1(self): + time_ast = MANAGER.ast_from_module_name('time') + self.assertTrue(time_ast) + self.assertEqual(time_ast['time'].args.defaults, []) + + def test_inspect_build2(self): + """test astroid tree build from a living object""" + try: + from mx import DateTime + except ImportError: + self.skipTest('test skipped: mxDateTime is not available') + else: + dt_ast = self.builder.inspect_build(DateTime) + dt_ast.getattr('DateTime') + # this one is failing since DateTimeType.__module__ = 'builtins' ! + #dt_ast.getattr('DateTimeType') + + def test_inspect_build3(self): + self.builder.inspect_build(unittest) + + def test_inspect_build_instance(self): + """test astroid tree build from a living object""" + if sys.version_info >= (3, 0): + self.skipTest('The module "exceptions" is gone in py3.x') + import exceptions + builtin_ast = self.builder.inspect_build(exceptions) + fclass = builtin_ast['OSError'] + # things like OSError.strerror are now (2.5) data descriptors on the + # class instead of entries in the __dict__ of an instance + container = fclass + self.assertIn('errno', container) + self.assertIn('strerror', container) + self.assertIn('filename', container) + + def test_inspect_build_type_object(self): + builtin_ast = MANAGER.ast_from_module_name(BUILTINS) + + infered = list(builtin_ast.igetattr('object')) + self.assertEqual(len(infered), 1) + infered = infered[0] + self.assertEqual(infered.name, 'object') + infered.as_string() # no crash test + + infered = list(builtin_ast.igetattr('type')) + self.assertEqual(len(infered), 1) + infered = infered[0] + self.assertEqual(infered.name, 'type') + infered.as_string() # no crash test + + def test_inspect_transform_module(self): + # ensure no cached version of the time module + MANAGER._mod_file_cache.pop(('time', None), None) + MANAGER.astroid_cache.pop('time', None) + def transform_time(node): + if node.name == 'time': + node.transformed = True + MANAGER.register_transform(nodes.Module, transform_time) + try: + time_ast = MANAGER.ast_from_module_name('time') + self.assertTrue(getattr(time_ast, 'transformed', False)) + finally: + MANAGER.unregister_transform(nodes.Module, transform_time) + + def test_package_name(self): + """test base properties and method of a astroid module""" + datap = self.builder.file_build(join(DATA, '__init__.py'), 'data') + self.assertEqual(datap.name, 'data') + self.assertEqual(datap.package, 1) + datap = self.builder.file_build(join(DATA, '__init__.py'), 'data.__init__') + self.assertEqual(datap.name, 'data') + self.assertEqual(datap.package, 1) + + def test_yield_parent(self): + """check if we added discard nodes as yield parent (w/ compiler)""" + data = """ +def yiell(): + yield 0 + if noe: + yield more +""" + func = self.builder.string_build(data).body[0] + self.assertIsInstance(func, nodes.Function) + stmt = func.body[0] + self.assertIsInstance(stmt, nodes.Discard) + self.assertIsInstance(stmt.value, nodes.Yield) + self.assertIsInstance(func.body[1].body[0], nodes.Discard) + self.assertIsInstance(func.body[1].body[0].value, nodes.Yield) + + def test_object(self): + obj_ast = self.builder.inspect_build(object) + self.assertIn('__setattr__', obj_ast) + + def test_newstyle_detection(self): + data = ''' +class A: + "old style" + +class B(A): + "old style" + +class C(object): + "new style" + +class D(C): + "new style" + +__metaclass__ = type + +class E(A): + "old style" + +class F: + "new style" +''' + mod_ast = self.builder.string_build(data, __name__, __file__) + if PY3K: + self.assertTrue(mod_ast['A'].newstyle) + self.assertTrue(mod_ast['B'].newstyle) + self.assertTrue(mod_ast['E'].newstyle) + else: + self.assertFalse(mod_ast['A'].newstyle) + self.assertFalse(mod_ast['B'].newstyle) + self.assertFalse(mod_ast['E'].newstyle) + self.assertTrue(mod_ast['C'].newstyle) + self.assertTrue(mod_ast['D'].newstyle) + self.assertTrue(mod_ast['F'].newstyle) + + def test_globals(self): + data = ''' +CSTE = 1 + +def update_global(): + global CSTE + CSTE += 1 + +def global_no_effect(): + global CSTE2 + print (CSTE) +''' + astroid = self.builder.string_build(data, __name__, __file__) + self.assertEqual(len(astroid.getattr('CSTE')), 2) + self.assertIsInstance(astroid.getattr('CSTE')[0], nodes.AssName) + self.assertEqual(astroid.getattr('CSTE')[0].fromlineno, 2) + self.assertEqual(astroid.getattr('CSTE')[1].fromlineno, 6) + self.assertRaises(NotFoundError, + astroid.getattr, 'CSTE2') + self.assertRaises(InferenceError, + partial(next, astroid['global_no_effect'].ilookup('CSTE2'))) + + def test_socket_build(self): + import socket + astroid = self.builder.module_build(socket) + # XXX just check the first one. Actually 3 objects are inferred (look at + # the socket module) but the last one as those attributes dynamically + # set and astroid is missing this. + for fclass in astroid.igetattr('socket'): + #print fclass.root().name, fclass.name, fclass.lineno + self.assertIn('connect', fclass) + self.assertIn('send', fclass) + self.assertIn('close', fclass) + break + + def test_gen_expr_var_scope(self): + data = 'l = list(n for n in range(10))\n' + astroid = self.builder.string_build(data, __name__, __file__) + # n unavailable outside gen expr scope + self.assertNotIn('n', astroid) + # test n is inferable anyway + n = get_name_node(astroid, 'n') + self.assertIsNot(n.scope(), astroid) + self.assertEqual([i.__class__ for i in n.infer()], + [YES.__class__]) + + def test_no_future_imports(self): + mod = test_utils.build_module("import sys") + self.assertEqual(set(), mod.future_imports) + + def test_future_imports(self): + mod = test_utils.build_module("from __future__ import print_function") + self.assertEqual(set(['print_function']), mod.future_imports) + + def test_two_future_imports(self): + mod = test_utils.build_module(""" + from __future__ import print_function + from __future__ import absolute_import + """) + self.assertEqual(set(['print_function', 'absolute_import']), mod.future_imports) + +class FileBuildTC(unittest.TestCase): + module = builder.AstroidBuilder().file_build( + join(DATA, 'module.py'), + '%s.module' % (data.__name__,)) + + def test_module_base_props(self): + """test base properties and method of a astroid module""" + module = self.module + self.assertEqual(module.name, '%s.module' % (data.__name__)) + self.assertEqual(module.doc, "test module for astroid\n") + self.assertEqual(module.fromlineno, 0) + self.assertIsNone(module.parent) + self.assertEqual(module.frame(), module) + self.assertEqual(module.root(), module) + self.assertEqual(module.file, join(abspath(data.__path__[0]), 'module.py')) + self.assertEqual(module.pure_python, 1) + self.assertEqual(module.package, 0) + self.assertFalse(module.is_statement) + self.assertEqual(module.statement(), module) + self.assertEqual(module.statement(), module) + + def test_module_locals(self): + """test the 'locals' dictionary of a astroid module""" + module = self.module + _locals = module.locals + self.assertIs(_locals, module.globals) + keys = sorted(_locals.keys()) + should = ['MY_DICT', 'YO', 'YOUPI', + '__revision__', 'global_access','modutils', 'four_args', + 'os', 'redirect', 'pb', 'LocalsVisitor', 'ASTWalker'] + should.sort() + self.assertEqual(keys, should) + + def test_function_base_props(self): + """test base properties and method of a astroid function""" + module = self.module + function = module['global_access'] + self.assertEqual(function.name, 'global_access') + self.assertEqual(function.doc, 'function test') + self.assertEqual(function.fromlineno, 11) + self.assertTrue(function.parent) + self.assertEqual(function.frame(), function) + self.assertEqual(function.parent.frame(), module) + self.assertEqual(function.root(), module) + self.assertEqual([n.name for n in function.args.args], ['key', 'val']) + self.assertEqual(function.type, 'function') + + def test_function_locals(self): + """test the 'locals' dictionary of a astroid function""" + _locals = self.module['global_access'].locals + self.assertEqual(len(_locals), 4) + keys = sorted(_locals.keys()) + self.assertEqual(keys, ['i', 'key', 'local', 'val']) + + def test_class_base_props(self): + """test base properties and method of a astroid class""" + module = self.module + klass = module['YO'] + self.assertEqual(klass.name, 'YO') + self.assertEqual(klass.doc, 'hehe') + self.assertEqual(klass.fromlineno, 25) + self.assertTrue(klass.parent) + self.assertEqual(klass.frame(), klass) + self.assertEqual(klass.parent.frame(), module) + self.assertEqual(klass.root(), module) + self.assertEqual(klass.basenames, []) + if PY3K: + self.assertTrue(klass.newstyle) + else: + self.assertFalse(klass.newstyle) + + def test_class_locals(self): + """test the 'locals' dictionary of a astroid class""" + module = self.module + klass1 = module['YO'] + locals1 = klass1.locals + keys = sorted(locals1.keys()) + self.assertEqual(keys, ['__init__', 'a']) + klass2 = module['YOUPI'] + locals2 = klass2.locals + keys = locals2.keys() + self.assertEqual(sorted(keys), + ['__init__', 'class_attr', 'class_method', + 'method', 'static_method']) + + def test_class_instance_attrs(self): + module = self.module + klass1 = module['YO'] + klass2 = module['YOUPI'] + self.assertEqual(list(klass1.instance_attrs.keys()), ['yo']) + self.assertEqual(list(klass2.instance_attrs.keys()), ['member']) + + def test_class_basenames(self): + module = self.module + klass1 = module['YO'] + klass2 = module['YOUPI'] + self.assertEqual(klass1.basenames, []) + self.assertEqual(klass2.basenames, ['YO']) + + def test_method_base_props(self): + """test base properties and method of a astroid method""" + klass2 = self.module['YOUPI'] + # "normal" method + method = klass2['method'] + self.assertEqual(method.name, 'method') + self.assertEqual([n.name for n in method.args.args], ['self']) + self.assertEqual(method.doc, 'method test') + self.assertEqual(method.fromlineno, 47) + self.assertEqual(method.type, 'method') + # class method + method = klass2['class_method'] + self.assertEqual([n.name for n in method.args.args], ['cls']) + self.assertEqual(method.type, 'classmethod') + # static method + method = klass2['static_method'] + self.assertEqual(method.args.args, []) + self.assertEqual(method.type, 'staticmethod') + + def test_method_locals(self): + """test the 'locals' dictionary of a astroid method""" + method = self.module['YOUPI']['method'] + _locals = method.locals + keys = sorted(_locals) + if sys.version_info < (3, 0): + self.assertEqual(len(_locals), 5) + self.assertEqual(keys, ['a', 'autre', 'b', 'local', 'self']) + else:# ListComp variables are no more accessible outside + self.assertEqual(len(_locals), 3) + self.assertEqual(keys, ['autre', 'local', 'self']) + + +class ModuleBuildTC(FileBuildTC): + + def setUp(self): + abuilder = builder.AstroidBuilder() + self.module = abuilder.module_build(test_module) + + +class MoreTC(unittest.TestCase): + + def setUp(self): + self.builder = builder.AstroidBuilder() + + def test_infered_build(self): + code = '''class A: pass +A.type = "class" + +def A_ass_type(self): + print (self) +A.ass_type = A_ass_type + ''' + astroid = self.builder.string_build(code) + lclass = list(astroid.igetattr('A')) + self.assertEqual(len(lclass), 1) + lclass = lclass[0] + self.assertIn('ass_type', lclass.locals) + self.assertIn('type', lclass.locals) + + def test_augassign_attr(self): + astroid = self.builder.string_build("""class Counter: + v = 0 + def inc(self): + self.v += 1 + """, __name__, __file__) + # Check self.v += 1 generate AugAssign(AssAttr(...)), not AugAssign(GetAttr(AssName...)) + + def test_dumb_module(self): + astroid = self.builder.string_build("pouet") + + def test_infered_dont_pollute(self): + code = ''' +def func(a=None): + a.custom_attr = 0 +def func2(a={}): + a.custom_attr = 0 + ''' + astroid = self.builder.string_build(code) + nonetype = nodes.const_factory(None) + self.assertNotIn('custom_attr', nonetype.locals) + self.assertNotIn('custom_attr', nonetype.instance_attrs) + nonetype = nodes.const_factory({}) + self.assertNotIn('custom_attr', nonetype.locals) + self.assertNotIn('custom_attr', nonetype.instance_attrs) + + + def test_asstuple(self): + code = 'a, b = range(2)' + astroid = self.builder.string_build(code) + self.assertIn('b', astroid.locals) + code = ''' +def visit_if(self, node): + node.test, body = node.tests[0] +''' + astroid = self.builder.string_build(code) + self.assertIn('body', astroid['visit_if'].locals) + + def test_build_constants(self): + '''test expected values of constants after rebuilding''' + code = ''' +def func(): + return None + return + return 'None' +''' + astroid = self.builder.string_build(code) + none, nothing, chain = [ret.value for ret in astroid.body[0].body] + self.assertIsInstance(none, nodes.Const) + self.assertIsNone(none.value) + self.assertIsNone(nothing) + self.assertIsInstance(chain, nodes.Const) + self.assertEqual(chain.value, 'None') + + + def test_lgc_classproperty(self): + '''test expected values of constants after rebuilding''' + code = ''' +from logilab.common.decorators import classproperty + +class A(object): + @classproperty + def hop(cls): + return None +''' + astroid = self.builder.string_build(code) + self.assertEqual(astroid['A']['hop'].type, 'classmethod') + + +if sys.version_info < (3, 0): + guess_encoding = builder._guess_encoding + + class TestGuessEncoding(unittest.TestCase): + + def testEmacs(self): + e = guess_encoding('# -*- coding: UTF-8 -*-') + self.assertEqual(e, 'UTF-8') + e = guess_encoding('# -*- coding:UTF-8 -*-') + self.assertEqual(e, 'UTF-8') + e = guess_encoding(''' + ### -*- coding: ISO-8859-1 -*- + ''') + self.assertEqual(e, 'ISO-8859-1') + e = guess_encoding(''' + + ### -*- coding: ISO-8859-1 -*- + ''') + self.assertIsNone(e) + + def testVim(self): + e = guess_encoding('# vim:fileencoding=UTF-8') + self.assertEqual(e, 'UTF-8') + e = guess_encoding(''' + ### vim:fileencoding=ISO-8859-1 + ''') + self.assertEqual(e, 'ISO-8859-1') + e = guess_encoding(''' + + ### vim:fileencoding= ISO-8859-1 + ''') + self.assertIsNone(e) + + def test_wrong_coding(self): + # setting "coding" varaible + e = guess_encoding("coding = UTF-8") + self.assertIsNone(e) + # setting a dictionnary entry + e = guess_encoding("coding:UTF-8") + self.assertIsNone(e) + # setting an arguement + e = guess_encoding("def do_something(a_word_with_coding=None):") + self.assertIsNone(e) + + + def testUTF8(self): + e = guess_encoding('\xef\xbb\xbf any UTF-8 data') + self.assertEqual(e, 'UTF-8') + e = guess_encoding(' any UTF-8 data \xef\xbb\xbf') + self.assertIsNone(e) + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_inference.py b/astroid/tests/unittest_inference.py new file mode 100644 index 0000000..9b7b472 --- /dev/null +++ b/astroid/tests/unittest_inference.py @@ -0,0 +1,1383 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""tests for the astroid inference capabilities +""" +from os.path import join, dirname, abspath +import sys +from functools import partial +from textwrap import dedent +import unittest + +import six + +from astroid import InferenceError, builder, nodes +from astroid.inference import infer_end as inference_infer_end +from astroid.bases import YES, Instance, BoundMethod, UnboundMethod,\ + path_wrapper, BUILTINS +from astroid.test_utils import require_version + + +def get_name_node(start_from, name, index=0): + return [n for n in start_from.nodes_of_class(nodes.Name) if n.name == name][index] + +def get_node_of_class(start_from, klass): + return next(start_from.nodes_of_class(klass)) + +builder = builder.AstroidBuilder() + +class InferenceUtilsTC(unittest.TestCase): + + def test_path_wrapper(self): + def infer_default(self, *args): + raise InferenceError + infer_default = path_wrapper(infer_default) + infer_end = path_wrapper(inference_infer_end) + self.assertRaises(InferenceError, + partial(next, infer_default(1))) + self.assertEqual(next(infer_end(1)), 1) + +if sys.version_info < (3, 0): + EXC_MODULE = 'exceptions' +else: + EXC_MODULE = BUILTINS + + +class InferenceTC(unittest.TestCase): + + CODE = ''' + +class C(object): + "new style" + attr = 4 + + def meth1(self, arg1, optarg=0): + var = object() + print ("yo", arg1, optarg) + self.iattr = "hop" + return var + + def meth2(self): + self.meth1(*self.meth3) + + def meth3(self, d=attr): + b = self.attr + c = self.iattr + return b, c + +ex = Exception("msg") +v = C().meth1(1) +m_unbound = C.meth1 +m_bound = C().meth1 +a, b, c = ex, 1, "bonjour" +[d, e, f] = [ex, 1.0, ("bonjour", v)] +g, h = f +i, (j, k) = "glup", f + +a, b= b, a # Gasp ! +''' + + astroid = builder.string_build(CODE, __name__, __file__) + + def test_module_inference(self): + infered = self.astroid.infer() + obj = next(infered) + self.assertEqual(obj.name, __name__) + self.assertEqual(obj.root().name, __name__) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_class_inference(self): + infered = self.astroid['C'].infer() + obj = next(infered) + self.assertEqual(obj.name, 'C') + self.assertEqual(obj.root().name, __name__) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_function_inference(self): + infered = self.astroid['C']['meth1'].infer() + obj = next(infered) + self.assertEqual(obj.name, 'meth1') + self.assertEqual(obj.root().name, __name__) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_builtin_name_inference(self): + infered = self.astroid['C']['meth1']['var'].infer() + var = next(infered) + self.assertEqual(var.name, 'object') + self.assertEqual(var.root().name, BUILTINS) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_tupleassign_name_inference(self): + infered = self.astroid['a'].infer() + exc = next(infered) + self.assertIsInstance(exc, Instance) + self.assertEqual(exc.name, 'Exception') + self.assertEqual(exc.root().name, EXC_MODULE) + self.assertRaises(StopIteration, partial(next, infered)) + infered = self.astroid['b'].infer() + const = next(infered) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, 1) + self.assertRaises(StopIteration, partial(next, infered)) + infered = self.astroid['c'].infer() + const = next(infered) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, "bonjour") + self.assertRaises(StopIteration, partial(next, infered)) + + def test_listassign_name_inference(self): + infered = self.astroid['d'].infer() + exc = next(infered) + self.assertIsInstance(exc, Instance) + self.assertEqual(exc.name, 'Exception') + self.assertEqual(exc.root().name, EXC_MODULE) + self.assertRaises(StopIteration, partial(next, infered)) + infered = self.astroid['e'].infer() + const = next(infered) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, 1.0) + self.assertRaises(StopIteration, partial(next, infered)) + infered = self.astroid['f'].infer() + const = next(infered) + self.assertIsInstance(const, nodes.Tuple) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_advanced_tupleassign_name_inference1(self): + infered = self.astroid['g'].infer() + const = next(infered) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, "bonjour") + self.assertRaises(StopIteration, partial(next, infered)) + infered = self.astroid['h'].infer() + var = next(infered) + self.assertEqual(var.name, 'object') + self.assertEqual(var.root().name, BUILTINS) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_advanced_tupleassign_name_inference2(self): + infered = self.astroid['i'].infer() + const = next(infered) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, u"glup") + self.assertRaises(StopIteration, partial(next, infered)) + infered = self.astroid['j'].infer() + const = next(infered) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, "bonjour") + self.assertRaises(StopIteration, partial(next, infered)) + infered = self.astroid['k'].infer() + var = next(infered) + self.assertEqual(var.name, 'object') + self.assertEqual(var.root().name, BUILTINS) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_swap_assign_inference(self): + infered = self.astroid.locals['a'][1].infer() + const = next(infered) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, 1) + self.assertRaises(StopIteration, partial(next, infered)) + infered = self.astroid.locals['b'][1].infer() + exc = next(infered) + self.assertIsInstance(exc, Instance) + self.assertEqual(exc.name, 'Exception') + self.assertEqual(exc.root().name, EXC_MODULE) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_getattr_inference1(self): + infered = self.astroid['ex'].infer() + exc = next(infered) + self.assertIsInstance(exc, Instance) + self.assertEqual(exc.name, 'Exception') + self.assertEqual(exc.root().name, EXC_MODULE) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_getattr_inference2(self): + infered = get_node_of_class(self.astroid['C']['meth2'], nodes.Getattr).infer() + meth1 = next(infered) + self.assertEqual(meth1.name, 'meth1') + self.assertEqual(meth1.root().name, __name__) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_getattr_inference3(self): + infered = self.astroid['C']['meth3']['b'].infer() + const = next(infered) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, 4) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_getattr_inference4(self): + infered = self.astroid['C']['meth3']['c'].infer() + const = next(infered) + self.assertIsInstance(const, nodes.Const) + self.assertEqual(const.value, "hop") + self.assertRaises(StopIteration, partial(next, infered)) + + def test_callfunc_inference(self): + infered = self.astroid['v'].infer() + meth1 = next(infered) + self.assertIsInstance(meth1, Instance) + self.assertEqual(meth1.name, 'object') + self.assertEqual(meth1.root().name, BUILTINS) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_unbound_method_inference(self): + infered = self.astroid['m_unbound'].infer() + meth1 = next(infered) + self.assertIsInstance(meth1, UnboundMethod) + self.assertEqual(meth1.name, 'meth1') + self.assertEqual(meth1.parent.frame().name, 'C') + self.assertRaises(StopIteration, partial(next, infered)) + + def test_bound_method_inference(self): + infered = self.astroid['m_bound'].infer() + meth1 = next(infered) + self.assertIsInstance(meth1, BoundMethod) + self.assertEqual(meth1.name, 'meth1') + self.assertEqual(meth1.parent.frame().name, 'C') + self.assertRaises(StopIteration, partial(next, infered)) + + def test_args_default_inference1(self): + optarg = get_name_node(self.astroid['C']['meth1'], 'optarg') + infered = optarg.infer() + obj1 = next(infered) + self.assertIsInstance(obj1, nodes.Const) + self.assertEqual(obj1.value, 0) + obj1 = next(infered) + self.assertIs(obj1, YES, obj1) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_args_default_inference2(self): + infered = self.astroid['C']['meth3'].ilookup('d') + obj1 = next(infered) + self.assertIsInstance(obj1, nodes.Const) + self.assertEqual(obj1.value, 4) + obj1 = next(infered) + self.assertIs(obj1, YES, obj1) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_inference_restrictions(self): + infered = get_name_node(self.astroid['C']['meth1'], 'arg1').infer() + obj1 = next(infered) + self.assertIs(obj1, YES, obj1) + self.assertRaises(StopIteration, partial(next, infered)) + + def test_ancestors_inference(self): + code = ''' +class A: + pass + +class A(A): + pass + ''' + astroid = builder.string_build(code, __name__, __file__) + a1 = astroid.locals['A'][0] + a2 = astroid.locals['A'][1] + a2_ancestors = list(a2.ancestors()) + self.assertEqual(len(a2_ancestors), 1) + self.assertIs(a2_ancestors[0], a1) + + def test_ancestors_inference2(self): + code = ''' +class A: + pass + +class B(A): pass + +class A(B): + pass + ''' + astroid = builder.string_build(code, __name__, __file__) + a1 = astroid.locals['A'][0] + a2 = astroid.locals['A'][1] + a2_ancestors = list(a2.ancestors()) + self.assertEqual(len(a2_ancestors), 2) + self.assertIs(a2_ancestors[0], astroid.locals['B'][0]) + self.assertIs(a2_ancestors[1], a1) + + + def test_f_arg_f(self): + code = ''' +def f(f=1): + return f + +a = f() + ''' + astroid = builder.string_build(code, __name__, __file__) + a = astroid['a'] + a_infered = a.infered() + self.assertEqual(a_infered[0].value, 1) + self.assertEqual(len(a_infered), 1) + + def test_exc_ancestors(self): + code = ''' +def f(): + raise NotImplementedError + ''' + astroid = builder.string_build(code, __name__, __file__) + error = next(astroid.nodes_of_class(nodes.Name)) + nie = error.infered()[0] + self.assertIsInstance(nie, nodes.Class) + nie_ancestors = [c.name for c in nie.ancestors()] + if sys.version_info < (3, 0): + self.assertEqual(nie_ancestors, ['RuntimeError', 'StandardError', 'Exception', 'BaseException', 'object']) + else: + self.assertEqual(nie_ancestors, ['RuntimeError', 'Exception', 'BaseException', 'object']) + + def test_except_inference(self): + code = ''' +try: + print (hop) +except NameError, ex: + ex1 = ex +except Exception, ex: + ex2 = ex + raise + ''' + if sys.version_info >= (3, 0): + code = code.replace(', ex:', ' as ex:') + astroid = builder.string_build(code, __name__, __file__) + ex1 = astroid['ex1'] + ex1_infer = ex1.infer() + ex1 = next(ex1_infer) + self.assertIsInstance(ex1, Instance) + self.assertEqual(ex1.name, 'NameError') + self.assertRaises(StopIteration, partial(next, ex1_infer)) + ex2 = astroid['ex2'] + ex2_infer = ex2.infer() + ex2 = next(ex2_infer) + self.assertIsInstance(ex2, Instance) + self.assertEqual(ex2.name, 'Exception') + self.assertRaises(StopIteration, partial(next, ex2_infer)) + + def test_del1(self): + code = ''' +del undefined_attr + ''' + delete = builder.string_build(code, __name__, __file__).body[0] + self.assertRaises(InferenceError, delete.infer) + + def test_del2(self): + code = ''' +a = 1 +b = a +del a +c = a +a = 2 +d = a + ''' + astroid = builder.string_build(code, __name__, __file__) + n = astroid['b'] + n_infer = n.infer() + infered = next(n_infer) + self.assertIsInstance(infered, nodes.Const) + self.assertEqual(infered.value, 1) + self.assertRaises(StopIteration, partial(next, n_infer)) + n = astroid['c'] + n_infer = n.infer() + self.assertRaises(InferenceError, partial(next, n_infer)) + n = astroid['d'] + n_infer = n.infer() + infered = next(n_infer) + self.assertIsInstance(infered, nodes.Const) + self.assertEqual(infered.value, 2) + self.assertRaises(StopIteration, partial(next, n_infer)) + + def test_builtin_types(self): + code = ''' +l = [1] +t = (2,) +d = {} +s = '' +s2 = '_' + ''' + astroid = builder.string_build(code, __name__, __file__) + n = astroid['l'] + infered = next(n.infer()) + self.assertIsInstance(infered, nodes.List) + self.assertIsInstance(infered, Instance) + self.assertEqual(infered.getitem(0).value, 1) + self.assertIsInstance(infered._proxied, nodes.Class) + self.assertEqual(infered._proxied.name, 'list') + self.assertIn('append', infered._proxied.locals) + n = astroid['t'] + infered = next(n.infer()) + self.assertIsInstance(infered, nodes.Tuple) + self.assertIsInstance(infered, Instance) + self.assertEqual(infered.getitem(0).value, 2) + self.assertIsInstance(infered._proxied, nodes.Class) + self.assertEqual(infered._proxied.name, 'tuple') + n = astroid['d'] + infered = next(n.infer()) + self.assertIsInstance(infered, nodes.Dict) + self.assertIsInstance(infered, Instance) + self.assertIsInstance(infered._proxied, nodes.Class) + self.assertEqual(infered._proxied.name, 'dict') + self.assertIn('get', infered._proxied.locals) + n = astroid['s'] + infered = next(n.infer()) + self.assertIsInstance(infered, nodes.Const) + self.assertIsInstance(infered, Instance) + self.assertEqual(infered.name, 'str') + self.assertIn('lower', infered._proxied.locals) + n = astroid['s2'] + infered = next(n.infer()) + self.assertEqual(infered.getitem(0).value, '_') + + @require_version('2.7') + def test_builtin_types_py27(self): + code = 's = {1}' + astroid = builder.string_build(code, __name__, __file__) + n = astroid['s'] + infered = next(n.infer()) + self.assertIsInstance(infered, nodes.Set) + self.assertIsInstance(infered, Instance) + self.assertEqual(infered.name, 'set') + self.assertIn('remove', infered._proxied.locals) + + def test_unicode_type(self): + if sys.version_info >= (3, 0): + self.skipTest('unicode removed on py >= 3.0') + code = '''u = u""''' + astroid = builder.string_build(code, __name__, __file__) + n = astroid['u'] + infered = next(n.infer()) + self.assertIsInstance(infered, nodes.Const) + self.assertIsInstance(infered, Instance) + self.assertEqual(infered.name, 'unicode') + self.assertIn('lower', infered._proxied.locals) + + def test_descriptor_are_callable(self): + code = ''' +class A: + statm = staticmethod(open) + clsm = classmethod('whatever') + ''' + astroid = builder.string_build(code, __name__, __file__) + statm = next(astroid['A'].igetattr('statm')) + self.assertTrue(statm.callable()) + clsm = next(astroid['A'].igetattr('clsm')) + self.assertTrue(clsm.callable()) + + def test_bt_ancestor_crash(self): + code = ''' +class Warning(Warning): + pass + ''' + astroid = builder.string_build(code, __name__, __file__) + w = astroid['Warning'] + ancestors = w.ancestors() + ancestor = next(ancestors) + self.assertEqual(ancestor.name, 'Warning') + self.assertEqual(ancestor.root().name, EXC_MODULE) + ancestor = next(ancestors) + self.assertEqual(ancestor.name, 'Exception') + self.assertEqual(ancestor.root().name, EXC_MODULE) + ancestor = next(ancestors) + self.assertEqual(ancestor.name, 'BaseException') + self.assertEqual(ancestor.root().name, EXC_MODULE) + ancestor = next(ancestors) + self.assertEqual(ancestor.name, 'object') + self.assertEqual(ancestor.root().name, BUILTINS) + self.assertRaises(StopIteration, partial(next, ancestors)) + + def test_qqch(self): + code = ''' +from astroid.modutils import load_module_from_name +xxx = load_module_from_name('__pkginfo__') + ''' + astroid = builder.string_build(code, __name__, __file__) + xxx = astroid['xxx'] + self.assertSetEqual(set(n.__class__ for n in xxx.infered()), + set([nodes.Const, YES.__class__])) + + def test_method_argument(self): + code = ''' +class ErudiEntitySchema: + """a entity has a type, a set of subject and or object relations""" + def __init__(self, e_type, **kwargs): + kwargs['e_type'] = e_type.capitalize().encode() + + def meth(self, e_type, *args, **kwargs): + kwargs['e_type'] = e_type.capitalize().encode() + print(args) + ''' + astroid = builder.string_build(code, __name__, __file__) + arg = get_name_node(astroid['ErudiEntitySchema']['__init__'], 'e_type') + self.assertEqual([n.__class__ for n in arg.infer()], + [YES.__class__]) + arg = get_name_node(astroid['ErudiEntitySchema']['__init__'], 'kwargs') + self.assertEqual([n.__class__ for n in arg.infer()], + [nodes.Dict]) + arg = get_name_node(astroid['ErudiEntitySchema']['meth'], 'e_type') + self.assertEqual([n.__class__ for n in arg.infer()], + [YES.__class__]) + arg = get_name_node(astroid['ErudiEntitySchema']['meth'], 'args') + self.assertEqual([n.__class__ for n in arg.infer()], + [nodes.Tuple]) + arg = get_name_node(astroid['ErudiEntitySchema']['meth'], 'kwargs') + self.assertEqual([n.__class__ for n in arg.infer()], + [nodes.Dict]) + + + def test_tuple_then_list(self): + code = ''' +def test_view(rql, vid, tags=()): + tags = list(tags) + tags.append(vid) + ''' + astroid = builder.string_build(code, __name__, __file__) + name = get_name_node(astroid['test_view'], 'tags', -1) + it = name.infer() + tags = next(it) + self.assertEqual(tags.__class__, Instance) + self.assertEqual(tags._proxied.name, 'list') + self.assertRaises(StopIteration, partial(next, it)) + + + + def test_mulassign_inference(self): + code = ''' + +def first_word(line): + """Return the first word of a line""" + + return line.split()[0] + +def last_word(line): + """Return last word of a line""" + + return line.split()[-1] + +def process_line(word_pos): + """Silly function: returns (ok, callable) based on argument. + + For test purpose only. + """ + + if word_pos > 0: + return (True, first_word) + elif word_pos < 0: + return (True, last_word) + else: + return (False, None) + +if __name__ == '__main__': + + line_number = 0 + for a_line in file('test_callable.py'): + tupletest = process_line(line_number) + (ok, fct) = process_line(line_number) + if ok: + fct(a_line) +''' + astroid = builder.string_build(code, __name__, __file__) + self.assertEqual(len(list(astroid['process_line'].infer_call_result( + None))), 3) + self.assertEqual(len(list(astroid['tupletest'].infer())), 3) + values = ['Function(first_word)', 'Function(last_word)', 'Const(NoneType)'] + self.assertEqual([str(infered) + for infered in astroid['fct'].infer()], values) + + def test_float_complex_ambiguity(self): + code = ''' +def no_conjugate_member(magic_flag): + """should not raise E1101 on something.conjugate""" + if magic_flag: + something = 1.0 + else: + something = 1.0j + if isinstance(something, float): + return something + return something.conjugate() + ''' + astroid = builder.string_build(code, __name__, __file__) + self.assertEqual([i.value for i in + astroid['no_conjugate_member'].ilookup('something')], [1.0, 1.0j]) + self.assertEqual([i.value for i in + get_name_node(astroid, 'something', -1).infer()], [1.0, 1.0j]) + + def test_lookup_cond_branches(self): + code = ''' +def no_conjugate_member(magic_flag): + """should not raise E1101 on something.conjugate""" + something = 1.0 + if magic_flag: + something = 1.0j + return something.conjugate() + ''' + astroid = builder.string_build(code, __name__, __file__) + self.assertEqual([i.value for i in + get_name_node(astroid, 'something', -1).infer()], [1.0, 1.0j]) + + + def test_simple_subscript(self): + code = ''' +a = [1, 2, 3][0] +b = (1, 2, 3)[1] +c = (1, 2, 3)[-1] +d = a + b + c +print (d) +e = {'key': 'value'} +f = e['key'] +print (f) + ''' + astroid = builder.string_build(code, __name__, __file__) + self.assertEqual([i.value for i in + get_name_node(astroid, 'a', -1).infer()], [1]) + self.assertEqual([i.value for i in + get_name_node(astroid, 'b', -1).infer()], [2]) + self.assertEqual([i.value for i in + get_name_node(astroid, 'c', -1).infer()], [3]) + self.assertEqual([i.value for i in + get_name_node(astroid, 'd', -1).infer()], [6]) + self.assertEqual([i.value for i in + get_name_node(astroid, 'f', -1).infer()], ['value']) + + #def test_simple_tuple(self): + #"""test case for a simple tuple value""" + ## XXX tuple inference is not implemented ... + #code = """ +#a = (1,) +#b = (22,) +#some = a + b +#""" + #astroid = builder.string_build(code, __name__, __file__) + #self.assertEqual(astroid['some'].infer.next().as_string(), "(1, 22)") + + def test_simple_for(self): + code = ''' +for a in [1, 2, 3]: + print (a) +for b,c in [(1,2), (3,4)]: + print (b) + print (c) + +print ([(d,e) for e,d in ([1,2], [3,4])]) + ''' + astroid = builder.string_build(code, __name__, __file__) + self.assertEqual([i.value for i in + get_name_node(astroid, 'a', -1).infer()], [1, 2, 3]) + self.assertEqual([i.value for i in + get_name_node(astroid, 'b', -1).infer()], [1, 3]) + self.assertEqual([i.value for i in + get_name_node(astroid, 'c', -1).infer()], [2, 4]) + self.assertEqual([i.value for i in + get_name_node(astroid, 'd', -1).infer()], [2, 4]) + self.assertEqual([i.value for i in + get_name_node(astroid, 'e', -1).infer()], [1, 3]) + + + def test_simple_for_genexpr(self): + code = ''' +print ((d,e) for e,d in ([1,2], [3,4])) + ''' + astroid = builder.string_build(code, __name__, __file__) + self.assertEqual([i.value for i in + get_name_node(astroid, 'd', -1).infer()], [2, 4]) + self.assertEqual([i.value for i in + get_name_node(astroid, 'e', -1).infer()], [1, 3]) + + + def test_builtin_help(self): + code = ''' +help() + ''' + # XXX failing since __builtin__.help assignment has + # been moved into a function... + astroid = builder.string_build(code, __name__, __file__) + node = get_name_node(astroid, 'help', -1) + infered = list(node.infer()) + self.assertEqual(len(infered), 1, infered) + self.assertIsInstance(infered[0], Instance) + self.assertEqual(infered[0].name, "_Helper") + + def test_builtin_open(self): + code = ''' +open("toto.txt") + ''' + astroid = builder.string_build(code, __name__, __file__) + node = get_name_node(astroid, 'open', -1) + infered = list(node.infer()) + self.assertEqual(len(infered), 1) + if hasattr(sys, 'pypy_version_info'): + self.assertIsInstance(infered[0], nodes.Class) + self.assertEqual(infered[0].name, 'file') + else: + self.assertIsInstance(infered[0], nodes.Function) + self.assertEqual(infered[0].name, 'open') + + def test_callfunc_context_func(self): + code = ''' +def mirror(arg=None): + return arg + +un = mirror(1) + ''' + astroid = builder.string_build(code, __name__, __file__) + infered = list(astroid.igetattr('un')) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.Const) + self.assertEqual(infered[0].value, 1) + + def test_callfunc_context_lambda(self): + code = ''' +mirror = lambda x=None: x + +un = mirror(1) + ''' + astroid = builder.string_build(code, __name__, __file__) + infered = list(astroid.igetattr('mirror')) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.Lambda) + infered = list(astroid.igetattr('un')) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.Const) + self.assertEqual(infered[0].value, 1) + + def test_factory_method(self): + code = ''' +class Super(object): + @classmethod + def instance(cls): + return cls() + +class Sub(Super): + def method(self): + print ('method called') + +sub = Sub.instance() + ''' + astroid = builder.string_build(code, __name__, __file__) + infered = list(astroid.igetattr('sub')) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], Instance) + self.assertEqual(infered[0]._proxied.name, 'Sub') + + + def test_import_as(self): + code = ''' +import os.path as osp +print (osp.dirname(__file__)) + +from os.path import exists as e +assert e(__file__) + +from new import code as make_code +print (make_code) + ''' + astroid = builder.string_build(code, __name__, __file__) + infered = list(astroid.igetattr('osp')) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.Module) + self.assertEqual(infered[0].name, 'os.path') + infered = list(astroid.igetattr('e')) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.Function) + self.assertEqual(infered[0].name, 'exists') + if sys.version_info >= (3, 0): + self.skipTest(' module has been removed') + infered = list(astroid.igetattr('make_code')) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], Instance) + self.assertEqual(str(infered[0]), + 'Instance of %s.type' % BUILTINS) + + def _test_const_infered(self, node, value): + infered = list(node.infer()) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.Const) + self.assertEqual(infered[0].value, value) + + def test_unary_not(self): + for code in ('a = not (1,); b = not ()', + 'a = not {1:2}; b = not {}'): + astroid = builder.string_build(code, __name__, __file__) + self._test_const_infered(astroid['a'], False) + self._test_const_infered(astroid['b'], True) + + def test_binary_op_int_add(self): + astroid = builder.string_build('a = 1 + 2', __name__, __file__) + self._test_const_infered(astroid['a'], 3) + + def test_binary_op_int_sub(self): + astroid = builder.string_build('a = 1 - 2', __name__, __file__) + self._test_const_infered(astroid['a'], -1) + + def test_binary_op_float_div(self): + astroid = builder.string_build('a = 1 / 2.', __name__, __file__) + self._test_const_infered(astroid['a'], 1 / 2.) + + def test_binary_op_str_mul(self): + astroid = builder.string_build('a = "*" * 40', __name__, __file__) + self._test_const_infered(astroid['a'], "*" * 40) + + def test_binary_op_bitand(self): + astroid = builder.string_build('a = 23&20', __name__, __file__) + self._test_const_infered(astroid['a'], 23&20) + + def test_binary_op_bitor(self): + astroid = builder.string_build('a = 23|8', __name__, __file__) + self._test_const_infered(astroid['a'], 23|8) + + def test_binary_op_bitxor(self): + astroid = builder.string_build('a = 23^9', __name__, __file__) + self._test_const_infered(astroid['a'], 23^9) + + def test_binary_op_shiftright(self): + astroid = builder.string_build('a = 23 >>1', __name__, __file__) + self._test_const_infered(astroid['a'], 23>>1) + + def test_binary_op_shiftleft(self): + astroid = builder.string_build('a = 23 <<1', __name__, __file__) + self._test_const_infered(astroid['a'], 23<<1) + + + def test_binary_op_list_mul(self): + for code in ('a = [[]] * 2', 'a = 2 * [[]]'): + astroid = builder.string_build(code, __name__, __file__) + infered = list(astroid['a'].infer()) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.List) + self.assertEqual(len(infered[0].elts), 2) + self.assertIsInstance(infered[0].elts[0], nodes.List) + self.assertIsInstance(infered[0].elts[1], nodes.List) + + def test_binary_op_list_mul_none(self): + 'test correct handling on list multiplied by None' + astroid = builder.string_build( 'a = [1] * None\nb = [1] * "r"') + infered = astroid['a'].infered() + self.assertEqual(len(infered), 1) + self.assertEqual(infered[0], YES) + infered = astroid['b'].infered() + self.assertEqual(len(infered), 1) + self.assertEqual(infered[0], YES) + + + def test_binary_op_tuple_add(self): + astroid = builder.string_build('a = (1,) + (2,)', __name__, __file__) + infered = list(astroid['a'].infer()) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.Tuple) + self.assertEqual(len(infered[0].elts), 2) + self.assertEqual(infered[0].elts[0].value, 1) + self.assertEqual(infered[0].elts[1].value, 2) + + def test_binary_op_custom_class(self): + code = ''' +class myarray: + def __init__(self, array): + self.array = array + def __mul__(self, x): + return myarray([2,4,6]) + def astype(self): + return "ASTYPE" + +def randint(maximum): + if maximum is not None: + return myarray([1,2,3]) * 2 + else: + return int(5) + +x = randint(1) + ''' + astroid = builder.string_build(code, __name__, __file__) + infered = list(astroid.igetattr('x')) + self.assertEqual(len(infered), 2) + value = [str(v) for v in infered] + # The __name__ trick here makes it work when invoked directly + # (__name__ == '__main__') and through pytest (__name__ == + # 'unittest_inference') + self.assertEqual(value, ['Instance of %s.myarray' % __name__, + 'Instance of %s.int' % BUILTINS]) + + def test_nonregr_lambda_arg(self): + code = ''' +def f(g = lambda: None): + g().x +''' + astroid = builder.string_build(code, __name__, __file__) + callfuncnode = astroid['f'].body[0].value.expr # 'g()' + infered = list(callfuncnode.infer()) + self.assertEqual(len(infered), 2, infered) + infered.remove(YES) + self.assertIsInstance(infered[0], nodes.Const) + self.assertIsNone(infered[0].value) + + def test_nonregr_getitem_empty_tuple(self): + code = ''' +def f(x): + a = ()[x] + ''' + astroid = builder.string_build(code, __name__, __file__) + infered = list(astroid['f'].ilookup('a')) + self.assertEqual(len(infered), 1) + self.assertEqual(infered[0], YES) + + def test_nonregr_instance_attrs(self): + """non regression for instance_attrs infinite loop : pylint / #4""" + + code = """ +class Foo(object): + + def set_42(self): + self.attr = 42 + +class Bar(Foo): + + def __init__(self): + self.attr = 41 + """ + astroid = builder.string_build(code, __name__, __file__) + foo_class = astroid['Foo'] + bar_class = astroid['Bar'] + bar_self = astroid['Bar']['__init__']['self'] + assattr = bar_class.instance_attrs['attr'][0] + self.assertEqual(len(foo_class.instance_attrs['attr']), 1) + self.assertEqual(len(bar_class.instance_attrs['attr']), 1) + self.assertEqual(bar_class.instance_attrs, {'attr': [assattr]}) + # call 'instance_attr' via 'Instance.getattr' to trigger the bug: + instance = bar_self.infered()[0] + _attr = instance.getattr('attr') + self.assertEqual(len(bar_class.instance_attrs['attr']), 1) + self.assertEqual(len(foo_class.instance_attrs['attr']), 1) + self.assertEqual(bar_class.instance_attrs, {'attr': [assattr]}) + + def test_python25_generator_exit(self): + sys.stderr = six.StringIO() + data = "b = {}[str(0)+''].a" + astroid = builder.string_build(data, __name__, __file__) + list(astroid['b'].infer()) + output = sys.stderr.getvalue() + # I have no idea how to test for this in another way... + self.assertNotIn("RuntimeError", output, "Exception exceptions.RuntimeError: 'generator ignored GeneratorExit' in ignored") + sys.stderr = sys.__stderr__ + + def test_python25_relative_import(self): + data = "from ...logilab.common import date; print (date)" + # !! FIXME also this relative import would not work 'in real' (no __init__.py in test/) + # the test works since we pretend we have a package by passing the full modname + astroid = builder.string_build(data, 'astroid.test.unittest_inference', __file__) + infered = next(get_name_node(astroid, 'date').infer()) + self.assertIsInstance(infered, nodes.Module) + self.assertEqual(infered.name, 'logilab.common.date') + + def test_python25_no_relative_import(self): + fname = join(abspath(dirname(__file__)), 'regrtest_data', 'package', 'absimport.py') + astroid = builder.file_build(fname, 'absimport') + self.assertTrue(astroid.absolute_import_activated(), True) + infered = next(get_name_node(astroid, 'import_package_subpackage_module').infer()) + # failed to import since absolute_import is activated + self.assertIs(infered, YES) + + def test_nonregr_absolute_import(self): + fname = join(abspath(dirname(__file__)), 'regrtest_data', 'absimp', 'string.py') + astroid = builder.file_build(fname, 'absimp.string') + self.assertTrue(astroid.absolute_import_activated(), True) + infered = next(get_name_node(astroid, 'string').infer()) + self.assertIsInstance(infered, nodes.Module) + self.assertEqual(infered.name, 'string') + self.assertIn('ascii_letters', infered.locals) + + def test_mechanize_open(self): + try: + import mechanize + except ImportError: + self.skipTest('require mechanize installed') + data = '''from mechanize import Browser +print (Browser) +b = Browser() +''' + astroid = builder.string_build(data, __name__, __file__) + browser = next(get_name_node(astroid, 'Browser').infer()) + self.assertIsInstance(browser, nodes.Class) + bopen = list(browser.igetattr('open')) + self.skipTest('the commit said: "huum, see that later"') + self.assertEqual(len(bopen), 1) + self.assertIsInstance(bopen[0], nodes.Function) + self.assertTrue(bopen[0].callable()) + b = next(get_name_node(astroid, 'b').infer()) + self.assertIsInstance(b, Instance) + bopen = list(b.igetattr('open')) + self.assertEqual(len(bopen), 1) + self.assertIsInstance(bopen[0], BoundMethod) + self.assertTrue(bopen[0].callable()) + + def test_property(self): + code = ''' +from smtplib import SMTP +class SendMailController(object): + + @property + def smtp(self): + return SMTP(mailhost, port) + + @property + def me(self): + return self + +my_smtp = SendMailController().smtp +my_me = SendMailController().me +''' + decorators = set(['%s.property' % BUILTINS]) + astroid = builder.string_build(code, __name__, __file__) + self.assertEqual(astroid['SendMailController']['smtp'].decoratornames(), + decorators) + propinfered = list(astroid.body[2].value.infer()) + self.assertEqual(len(propinfered), 1) + propinfered = propinfered[0] + self.assertIsInstance(propinfered, Instance) + self.assertEqual(propinfered.name, 'SMTP') + self.assertEqual(propinfered.root().name, 'smtplib') + self.assertEqual(astroid['SendMailController']['me'].decoratornames(), + decorators) + propinfered = list(astroid.body[3].value.infer()) + self.assertEqual(len(propinfered), 1) + propinfered = propinfered[0] + self.assertIsInstance(propinfered, Instance) + self.assertEqual(propinfered.name, 'SendMailController') + self.assertEqual(propinfered.root().name, __name__) + + + def test_im_func_unwrap(self): + code = ''' +class EnvBasedTC: + def pactions(self): + pass +pactions = EnvBasedTC.pactions.im_func +print (pactions) + +class EnvBasedTC2: + pactions = EnvBasedTC.pactions.im_func + print (pactions) + +''' + astroid = builder.string_build(code, __name__, __file__) + pactions = get_name_node(astroid, 'pactions') + infered = list(pactions.infer()) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.Function) + pactions = get_name_node(astroid['EnvBasedTC2'], 'pactions') + infered = list(pactions.infer()) + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.Function) + + def test_augassign(self): + code = ''' +a = 1 +a += 2 +print (a) +''' + astroid = builder.string_build(code, __name__, __file__) + infered = list(get_name_node(astroid, 'a').infer()) + + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], nodes.Const) + self.assertEqual(infered[0].value, 3) + + def test_nonregr_func_arg(self): + code = ''' +def foo(self, bar): + def baz(): + pass + def qux(): + return baz + spam = bar(None, qux) + print (spam) +''' + astroid = builder.string_build(code, __name__, __file__) + infered = list(get_name_node(astroid['foo'], 'spam').infer()) + self.assertEqual(len(infered), 1) + self.assertIs(infered[0], YES) + + def test_nonregr_func_global(self): + code = ''' +active_application = None + +def get_active_application(): + global active_application + return active_application + +class Application(object): + def __init__(self): + global active_application + active_application = self + +class DataManager(object): + def __init__(self, app=None): + self.app = get_active_application() + def test(self): + p = self.app + print (p) + ''' + astroid = builder.string_build(code, __name__, __file__) + infered = list(Instance(astroid['DataManager']).igetattr('app')) + self.assertEqual(len(infered), 2, infered) # None / Instance(Application) + infered = list(get_name_node(astroid['DataManager']['test'], 'p').infer()) + self.assertEqual(len(infered), 2, infered) + for node in infered: + if isinstance(node, Instance) and node.name == 'Application': + break + else: + self.fail('expected to find an instance of Application in %s' % infered) + + def test_list_inference(self): + """#20464""" + code = ''' +import optparse + +A = [] +B = [] + +def test(): + xyz = [ + "foobar=%s" % options.ca, + ] + A + B + + if options.bind is not None: + xyz.append("bind=%s" % options.bind) + return xyz + +def main(): + global options + + parser = optparse.OptionParser() + (options, args) = parser.parse_args() + +Z = test() + ''' + astroid = builder.string_build(code, __name__, __file__) + infered = list(astroid['Z'].infer()) + self.assertEqual(len(infered), 1, infered) + self.assertIsInstance(infered[0], Instance) + 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() + ''' + astroid = builder.string_build(code, __name__, __file__) + self.assertRaises(InferenceError, list, astroid['NewTest'].igetattr('arg')) + n = next(astroid['n'].infer()) + infered = list(n.igetattr('arg')) + self.assertEqual(len(infered), 1, infered) + + + def test_two_parents_from_same_module(self): + module = 'data_py3' if sys.version[0] == 3 else 'data' + code = ''' +from %s import nonregr +class Xxx(nonregr.Aaa, nonregr.Ccc): + "doc" + ''' % (module,) + astroid = builder.string_build(code, __name__, __file__) + parents = list(astroid['Xxx'].ancestors()) + self.assertEqual(len(parents), 3, parents) # Aaa, Ccc, object + + def test_pluggable_inference(self): + code = ''' +from collections import namedtuple +A = namedtuple('A', ['a', 'b']) +B = namedtuple('B', 'a b') + ''' + astroid = builder.string_build(code, __name__, __file__) + aclass = astroid['A'].infered()[0] + self.assertIsInstance(aclass, nodes.Class) + self.assertIn('a', aclass.instance_attrs) + self.assertIn('b', aclass.instance_attrs) + bclass = astroid['B'].infered()[0] + self.assertIsInstance(bclass, nodes.Class) + self.assertIn('a', bclass.instance_attrs) + self.assertIn('b', bclass.instance_attrs) + + def test_infer_arguments(self): + code = ''' +class A(object): + def first(self, arg1, arg2): + return arg1 + @classmethod + def method(cls, arg1, arg2): + return arg2 + @classmethod + def empty(cls): + return 2 + @staticmethod + def static(arg1, arg2): + return arg1 + def empty_method(self): + return [] +x = A().first(1, []) +y = A.method(1, []) +z = A.static(1, []) +empty = A.empty() +empty_list = A().empty_method() + ''' + astroid = builder.string_build(code, __name__, __file__) + int_node = astroid['x'].infered()[0] + self.assertIsInstance(int_node, nodes.Const) + self.assertEqual(int_node.value, 1) + list_node = astroid['y'].infered()[0] + self.assertIsInstance(list_node, nodes.List) + int_node = astroid['z'].infered()[0] + self.assertIsInstance(int_node, nodes.Const) + self.assertEqual(int_node.value, 1) + empty = astroid['empty'].infered()[0] + self.assertIsInstance(empty, nodes.Const) + self.assertEqual(empty.value, 2) + empty_list = astroid['empty_list'].infered()[0] + self.assertIsInstance(empty_list, nodes.List) + + def test_infer_variable_arguments(self): + code = ''' +def test(*args, **kwargs): + vararg = args + kwarg = kwargs + ''' + astroid = builder.string_build(code, __name__, __file__) + func = astroid['test'] + vararg = func.body[0].value + kwarg = func.body[1].value + + kwarg_infered = kwarg.infered()[0] + self.assertIsInstance(kwarg_infered, nodes.Dict) + self.assertIs(kwarg_infered.parent, func.args) + + vararg_infered = vararg.infered()[0] + self.assertIsInstance(vararg_infered, nodes.Tuple) + self.assertIs(vararg_infered.parent, func.args) + + def test_infer_nested(self): + code = dedent(""" + def nested(): + from threading import Thread + + class NestedThread(Thread): + def __init__(self): + Thread.__init__(self) + """) + # Test that inferring Thread.__init__ looks up in + # the nested scope. + astroid = builder.string_build(code, __name__, __file__) + callfunc = next(astroid.nodes_of_class(nodes.CallFunc)) + func = callfunc.func + infered = func.infered()[0] + self.assertIsInstance(infered, UnboundMethod) + + def test_instance_binary_operations(self): + code = dedent(""" + class A(object): + def __mul__(self, other): + return 42 + a = A() + b = A() + sub = a - b + mul = a * b + """) + astroid = builder.string_build(code, __name__, __file__) + sub = astroid['sub'].infered()[0] + mul = astroid['mul'].infered()[0] + self.assertIs(sub, YES) + self.assertIsInstance(mul, nodes.Const) + self.assertEqual(mul.value, 42) + + def test_instance_binary_operations_parent(self): + code = dedent(""" + class A(object): + def __mul__(self, other): + return 42 + class B(A): + pass + a = B() + b = B() + sub = a - b + mul = a * b + """) + astroid = builder.string_build(code, __name__, __file__) + sub = astroid['sub'].infered()[0] + mul = astroid['mul'].infered()[0] + self.assertIs(sub, YES) + self.assertIsInstance(mul, nodes.Const) + self.assertEqual(mul.value, 42) + + def test_instance_binary_operations_multiple_methods(self): + code = dedent(""" + class A(object): + def __mul__(self, other): + return 42 + class B(A): + def __mul__(self, other): + return [42] + a = B() + b = B() + sub = a - b + mul = a * b + """) + astroid = builder.string_build(code, __name__, __file__) + sub = astroid['sub'].infered()[0] + mul = astroid['mul'].infered()[0] + self.assertIs(sub, YES) + self.assertIsInstance(mul, nodes.List) + self.assertIsInstance(mul.elts[0], nodes.Const) + self.assertEqual(mul.elts[0].value, 42) + + def test_infer_call_result_crash(self): + # Test for issue 11. + code = dedent(""" + class A(object): + def __mul__(self, other): + return type.__new__() + + a = A() + b = A() + c = a * b + """) + astroid = builder.string_build(code, __name__, __file__) + node = astroid['c'] + self.assertEqual(node.infered(), [YES]) + + def test_infer_empty_nodes(self): + # Should not crash when trying to infer EmptyNodes. + node = nodes.EmptyNode() + self.assertEqual(node.infered(), [YES]) + + def test_infinite_loop_for_decorators(self): + # Issue https://bitbucket.org/logilab/astroid/issue/50 + # A decorator that returns itself leads to an infinite loop. + code = dedent(""" + def decorator(): + def wrapper(): + return decorator() + return wrapper + + @decorator() + def do_a_thing(): + pass""") + astroid = builder.string_build(code, __name__, __file__) + node = astroid['do_a_thing'] + self.assertEqual(node.type, 'function') + + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_inspector.py b/astroid/tests/unittest_inspector.py new file mode 100644 index 0000000..5d5eaa2 --- /dev/null +++ b/astroid/tests/unittest_inspector.py @@ -0,0 +1,93 @@ +# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). +# http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# 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. +""" + for the visitors.diadefs module +""" + +import sys +from os.path import join, abspath, dirname +import unittest + + +from astroid import nodes, inspector +from astroid.bases import Instance, YES + +from astroid.manager import AstroidManager, _silent_no_wrap + +MANAGER = AstroidManager() + +def astroid_wrapper(func, modname): + return func(modname) + + +DATA2 = join(dirname(abspath(__file__)), 'data2') + + +class LinkerTC(unittest.TestCase): + + def setUp(self): + self.project = MANAGER.project_from_files([DATA2], astroid_wrapper) + self.linker = inspector.Linker(self.project) + self.linker.visit(self.project) + + def test_class_implements(self): + klass = self.project.get_module('data2.clientmodule_test')['Ancestor'] + self.assertTrue(hasattr(klass, 'implements')) + self.assertEqual(len(klass.implements), 1) + self.assertTrue(isinstance(klass.implements[0], nodes.Class)) + self.assertEqual(klass.implements[0].name, "Interface") + klass = self.project.get_module('data2.clientmodule_test')['Specialization'] + self.assertTrue(hasattr(klass, 'implements')) + self.assertEqual(len(klass.implements), 0) + + def test_locals_assignment_resolution(self): + klass = self.project.get_module('data2.clientmodule_test')['Specialization'] + self.assertTrue(hasattr(klass, 'locals_type')) + type_dict = klass.locals_type + self.assertEqual(len(type_dict), 2) + keys = sorted(type_dict.keys()) + self.assertEqual(keys, ['TYPE', 'top']) + self.assertEqual(len(type_dict['TYPE']), 1) + self.assertEqual(type_dict['TYPE'][0].value, 'final class') + self.assertEqual(len(type_dict['top']), 1) + self.assertEqual(type_dict['top'][0].value, 'class') + + def test_instance_attrs_resolution(self): + klass = self.project.get_module('data2.clientmodule_test')['Specialization'] + self.assertTrue(hasattr(klass, 'instance_attrs_type')) + type_dict = klass.instance_attrs_type + self.assertEqual(len(type_dict), 3) + keys = sorted(type_dict.keys()) + self.assertEqual(keys, ['_id', 'relation', 'toto']) + self.assertTrue(isinstance(type_dict['relation'][0], Instance), type_dict['relation']) + self.assertEqual(type_dict['relation'][0].name, 'DoNothing') + self.assertTrue(isinstance(type_dict['toto'][0], Instance), type_dict['toto']) + self.assertEqual(type_dict['toto'][0].name, 'Toto') + self.assertIs(type_dict['_id'][0], YES) + + +class LinkerTC2(LinkerTC): + + def setUp(self): + self.project = MANAGER.project_from_files([DATA2], func_wrapper=_silent_no_wrap) + self.linker = inspector.Linker(self.project) + self.linker.visit(self.project) + +__all__ = ('LinkerTC', 'LinkerTC2') + + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_lookup.py b/astroid/tests/unittest_lookup.py new file mode 100644 index 0000000..2ca818a --- /dev/null +++ b/astroid/tests/unittest_lookup.py @@ -0,0 +1,369 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""tests for the astroid variable lookup capabilities +""" +import sys +from os.path import join, abspath, dirname +from functools import partial +import unittest + +from astroid import builder, nodes, scoped_nodes, \ + InferenceError, NotFoundError, UnresolvableName +from astroid.scoped_nodes import builtin_lookup, Function +from astroid.bases import YES +from unittest_inference import get_name_node +from astroid.test_utils import require_version + +builder = builder.AstroidBuilder() +PY3K = sys.version_info >= (3, 0) + +if PY3K: + DATA = join(dirname(abspath(__file__)), 'data_py3') + import data_py3 as data +else: + DATA = join(dirname(abspath(__file__)), 'data') + import data + +MODULE = builder.file_build(join(DATA, 'module.py'), 'data.module') +MODULE2 = builder.file_build(join(DATA, 'module2.py'), 'data.module2') +NONREGR = builder.file_build(join(DATA, 'nonregr.py'), 'data.nonregr') + +class LookupTC(unittest.TestCase): + + def test_limit(self): + code = ''' +l = [a + for a,b in list] + +a = 1 +b = a +a = None + +def func(): + c = 1 + ''' + astroid = builder.string_build(code, __name__, __file__) + # a & b + a = next(astroid.nodes_of_class(nodes.Name)) + self.assertEqual(a.lineno, 2) + if sys.version_info < (3, 0): + self.assertEqual(len(astroid.lookup('b')[1]), 2) + self.assertEqual(len(astroid.lookup('a')[1]), 3) + b = astroid.locals['b'][1] + else: + self.assertEqual(len(astroid.lookup('b')[1]), 1) + self.assertEqual(len(astroid.lookup('a')[1]), 2) + b = astroid.locals['b'][0] + stmts = a.lookup('a')[1] + self.assertEqual(len(stmts), 1) + self.assertEqual(b.lineno, 6) + b_infer = b.infer() + b_value = next(b_infer) + self.assertEqual(b_value.value, 1) + # c + self.assertRaises(StopIteration, partial(next, b_infer)) + func = astroid.locals['func'][0] + self.assertEqual(len(func.lookup('c')[1]), 1) + + def test_module(self): + astroid = builder.string_build('pass', __name__, __file__) + # built-in objects + none = next(astroid.ilookup('None')) + self.assertIsNone(none.value) + obj = next(astroid.ilookup('object')) + self.assertIsInstance(obj, nodes.Class) + self.assertEqual(obj.name, 'object') + self.assertRaises(InferenceError, partial(next, astroid.ilookup('YOAA'))) + + # XXX + self.assertEqual(len(list(NONREGR.ilookup('enumerate'))), 2) + + def test_class_ancestor_name(self): + code = ''' +class A: + pass + +class A(A): + pass + ''' + astroid = builder.string_build(code, __name__, __file__) + cls1 = astroid.locals['A'][0] + cls2 = astroid.locals['A'][1] + name = next(cls2.nodes_of_class(nodes.Name)) + self.assertEqual(next(name.infer()), cls1) + + ### backport those test to inline code + def test_method(self): + method = MODULE['YOUPI']['method'] + my_dict = next(method.ilookup('MY_DICT')) + self.assertTrue(isinstance(my_dict, nodes.Dict), my_dict) + none = next(method.ilookup('None')) + self.assertIsNone(none.value) + self.assertRaises(InferenceError, partial(next, method.ilookup('YOAA'))) + + + def test_function_argument_with_default(self): + make_class = MODULE2['make_class'] + base = next(make_class.ilookup('base')) + self.assertTrue(isinstance(base, nodes.Class), base.__class__) + self.assertEqual(base.name, 'YO') + self.assertEqual(base.root().name, '%s.module' % (data.__name__,)) + + + def test_class(self): + klass = MODULE['YOUPI'] + my_dict = next(klass.ilookup('MY_DICT')) + self.assertIsInstance(my_dict, nodes.Dict) + none = next(klass.ilookup('None')) + self.assertIsNone(none.value) + obj = next(klass.ilookup('object')) + self.assertIsInstance(obj, nodes.Class) + self.assertEqual(obj.name, 'object') + self.assertRaises(InferenceError, partial(next, klass.ilookup('YOAA'))) + + + def test_inner_classes(self): + ddd = list(NONREGR['Ccc'].ilookup('Ddd')) + self.assertEqual(ddd[0].name, 'Ddd') + + + def test_loopvar_hiding(self): + astroid = builder.string_build(""" +x = 10 +for x in range(5): + print (x) + +if x > 0: + print ('#' * x) + """, __name__, __file__) + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'x'] + # inside the loop, only one possible assignment + self.assertEqual(len(xnames[0].lookup('x')[1]), 1) + # outside the loop, two possible assignments + self.assertEqual(len(xnames[1].lookup('x')[1]), 2) + self.assertEqual(len(xnames[2].lookup('x')[1]), 2) + + def test_list_comps(self): + astroid = builder.string_build(""" +print ([ i for i in range(10) ]) +print ([ i for i in range(10) ]) +print ( list( i for i in range(10) ) ) + """, __name__, __file__) + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'i'] + self.assertEqual(len(xnames[0].lookup('i')[1]), 1) + self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) + self.assertEqual(len(xnames[1].lookup('i')[1]), 1) + self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) + self.assertEqual(len(xnames[2].lookup('i')[1]), 1) + self.assertEqual(xnames[2].lookup('i')[1][0].lineno, 4) + + def test_list_comp_target(self): + """test the list comprehension target""" + astroid = builder.string_build(""" +ten = [ var for var in range(10) ] +var + """) + var = astroid.body[1].value + if sys.version_info < (3, 0): + self.assertEqual(var.infered(), [YES]) + else: + self.assertRaises(UnresolvableName, var.infered) + + @require_version('2.7') + def test_dict_comps(self): + astroid = builder.string_build(""" +print ({ i: j for i in range(10) for j in range(10) }) +print ({ i: j for i in range(10) for j in range(10) }) + """, __name__, __file__) + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'i'] + self.assertEqual(len(xnames[0].lookup('i')[1]), 1) + self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) + self.assertEqual(len(xnames[1].lookup('i')[1]), 1) + self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) + + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'j'] + self.assertEqual(len(xnames[0].lookup('i')[1]), 1) + self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) + self.assertEqual(len(xnames[1].lookup('i')[1]), 1) + self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) + + @require_version('2.7') + def test_set_comps(self): + astroid = builder.string_build(""" +print ({ i for i in range(10) }) +print ({ i for i in range(10) }) + """, __name__, __file__) + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'i'] + self.assertEqual(len(xnames[0].lookup('i')[1]), 1) + self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) + self.assertEqual(len(xnames[1].lookup('i')[1]), 1) + self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) + + @require_version('2.7') + def test_set_comp_closure(self): + astroid = builder.string_build(""" +ten = { var for var in range(10) } +var + """) + var = astroid.body[1].value + self.assertRaises(UnresolvableName, var.infered) + + def test_generator_attributes(self): + tree = builder.string_build(""" +def count(): + "test" + yield 0 + +iterer = count() +num = iterer.next() + """) + next = tree.body[2].value.func # Getattr + gener = next.expr.infered()[0] # Generator + if sys.version_info < (3, 0): + self.assertIsInstance(gener.getattr('next')[0], Function) + else: + self.assertIsInstance(gener.getattr('__next__')[0], Function) + self.assertIsInstance(gener.getattr('send')[0], Function) + self.assertIsInstance(gener.getattr('throw')[0], Function) + self.assertIsInstance(gener.getattr('close')[0], Function) + + def test_explicit___name__(self): + code = ''' +class Pouet: + __name__ = "pouet" +p1 = Pouet() + +class PouetPouet(Pouet): pass +p2 = Pouet() + +class NoName: pass +p3 = NoName() +''' + astroid = builder.string_build(code, __name__, __file__) + p1 = next(astroid['p1'].infer()) + self.assertTrue(p1.getattr('__name__')) + p2 = next(astroid['p2'].infer()) + self.assertTrue(p2.getattr('__name__')) + self.assertTrue(astroid['NoName'].getattr('__name__')) + p3 = next(astroid['p3'].infer()) + self.assertRaises(NotFoundError, p3.getattr, '__name__') + + + def test_function_module_special(self): + astroid = builder.string_build(''' +def initialize(linter): + """initialize linter with checkers in this package """ + package_load(linter, __path__[0]) + ''', 'data.__init__', 'data/__init__.py') + path = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == '__path__'][0] + self.assertEqual(len(path.lookup('__path__')[1]), 1) + + + def test_builtin_lookup(self): + self.assertEqual(builtin_lookup('__dict__')[1], ()) + intstmts = builtin_lookup('int')[1] + self.assertEqual(len(intstmts), 1) + self.assertIsInstance(intstmts[0], nodes.Class) + self.assertEqual(intstmts[0].name, 'int') + self.assertIs(intstmts[0], nodes.const_factory(1)._proxied) + + + def test_decorator_arguments_lookup(self): + code = ''' +def decorator(value): + def wrapper(function): + return function + return wrapper + +class foo: + member = 10 + + @decorator(member) #This will cause pylint to complain + def test(self): + pass + ''' + astroid = builder.string_build(code, __name__, __file__) + member = get_name_node(astroid['foo'], 'member') + it = member.infer() + obj = next(it) + self.assertIsInstance(obj, nodes.Const) + self.assertEqual(obj.value, 10) + self.assertRaises(StopIteration, partial(next, it)) + + + def test_inner_decorator_member_lookup(self): + code = ''' +class FileA: + def decorator(bla): + return bla + + @decorator + def funcA(): + return 4 + ''' + astroid = builder.string_build(code, __name__, __file__) + decname = get_name_node(astroid['FileA'], 'decorator') + it = decname.infer() + obj = next(it) + self.assertIsInstance(obj, nodes.Function) + self.assertRaises(StopIteration, partial(next, it)) + + + def test_static_method_lookup(self): + code = ''' +class FileA: + @staticmethod + def funcA(): + return 4 + + +class Test: + FileA = [1,2,3] + + def __init__(self): + print (FileA.funcA()) + ''' + astroid = builder.string_build(code, __name__, __file__) + it = astroid['Test']['__init__'].ilookup('FileA') + obj = next(it) + self.assertIsInstance(obj, nodes.Class) + self.assertRaises(StopIteration, partial(next, it)) + + + def test_global_delete(self): + code = ''' +def run2(): + f = Frobble() + +class Frobble: + pass +Frobble.mumble = True + +del Frobble + +def run1(): + f = Frobble() +''' + astroid = builder.string_build(code, __name__, __file__) + stmts = astroid['run2'].lookup('Frobbel')[1] + self.assertEqual(len(stmts), 0) + stmts = astroid['run1'].lookup('Frobbel')[1] + self.assertEqual(len(stmts), 0) + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_manager.py b/astroid/tests/unittest_manager.py new file mode 100644 index 0000000..4e0abcd --- /dev/null +++ b/astroid/tests/unittest_manager.py @@ -0,0 +1,231 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +import unittest + +import sys +from os.path import join, abspath, dirname +from astroid.manager import AstroidManager, _silent_no_wrap +from astroid.bases import BUILTINS +from astroid.exceptions import AstroidBuildingException + +PY3K = sys.version_info >= (3, 0) + +if PY3K: + DATA = join(dirname(abspath(__file__)), 'data_py3') +else: + DATA = join(dirname(abspath(__file__)), 'data') + + +class AstroidManagerTC(unittest.TestCase): + def setUp(self): + self.manager = AstroidManager() + self.manager.clear_cache() # take care of borg + + def test_ast_from_file(self): + """check if the method return a good astroid object""" + import unittest + filepath = unittest.__file__ + astroid = self.manager.ast_from_file(filepath) + self.assertEqual(astroid.name, 'unittest') + self.assertIn('unittest', self.manager.astroid_cache) + + def test_ast_from_file_cache(self): + """check if the cache works""" + import unittest + filepath = unittest.__file__ + self.manager.ast_from_file(filepath) + astroid = self.manager.ast_from_file('unhandledName', 'unittest') + self.assertEqual(astroid.name, 'unittest') + self.assertIn('unittest', self.manager.astroid_cache) + + def test_ast_from_file_astro_builder(self): + """check if the source is at True, AstroidBuilder build a good astroid""" + import unittest + filepath = unittest.__file__ + astroid = self.manager.ast_from_file(filepath, None, True, True) + self.assertEqual(astroid.name, 'unittest') + self.assertIn('unittest', self.manager.astroid_cache) + + def test_ast_from_file_name_astro_builder_exception(self): + """check if an exception is thrown if we give a wrong filepath""" + self.assertRaises(AstroidBuildingException, self.manager.ast_from_file, 'unhandledName') + + def test_do_not_expose_main(self): + obj = self.manager.ast_from_module_name('__main__') + self.assertEqual(obj.name, '__main__') + self.assertEqual(obj.items(), []) + + def test_ast_from_module_name(self): + """check if the ast_from_module_name method return a good astroid""" + astroid = self.manager.ast_from_module_name('unittest') + self.assertEqual(astroid.name, 'unittest') + self.assertIn('unittest', self.manager.astroid_cache) + + def test_ast_from_module_name_not_python_source(self): + """check if the ast_from_module_name method return a good astroid with a no python source module""" + astroid = self.manager.ast_from_module_name('time') + self.assertEqual(astroid.name, 'time') + self.assertIn('time', self.manager.astroid_cache) + self.assertEqual(astroid.pure_python, False) + + def test_ast_from_module_name_astro_builder_exception(self): + """check if the method raise an exception if we give a wrong module""" + self.assertRaises(AstroidBuildingException, self.manager.ast_from_module_name, 'unhandledModule') + + def _test_ast_from_zip(self, archive): + origpath = sys.path[:] + sys.modules.pop('mypypa', None) + archive_path = join(DATA, archive) + sys.path.insert(0, archive_path) + try: + module = self.manager.ast_from_module_name('mypypa') + self.assertEqual(module.name, 'mypypa') + end = join(archive, 'mypypa') + self.assertTrue(module.file.endswith(end), + "%s doesn't endswith %s" % (module.file, end)) + finally: + # remove the module, else after importing egg, we don't get the zip + if 'mypypa' in self.manager.astroid_cache: + del self.manager.astroid_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_ast_from_module_name_egg(self): + self._test_ast_from_zip('MyPyPa-0.1.0-py2.5.egg') + + def test_ast_from_module_name_zip(self): + self._test_ast_from_zip('MyPyPa-0.1.0-py2.5.zip') + + def test_zip_import_data(self): + """check if zip_import_data works""" + filepath = join(DATA, 'MyPyPa-0.1.0-py2.5.zip/mypypa') + astroid = self.manager.zip_import_data(filepath) + self.assertEqual(astroid.name, 'mypypa') + + def test_zip_import_data_without_zipimport(self): + """check if zip_import_data return None without zipimport""" + self.assertEqual(self.manager.zip_import_data('path'), None) + + def test_file_from_module(self): + """check if the unittest filepath is equals to the result of the method""" + import unittest + if PY3K: + unittest_file = unittest.__file__ + else: + unittest_file = unittest.__file__[:-1] + self.assertEqual(unittest_file, + self.manager.file_from_module_name('unittest', None)) + + def test_file_from_module_name_astro_building_exception(self): + """check if the method launch a exception with a wrong module name""" + self.assertRaises(AstroidBuildingException, self.manager.file_from_module_name, 'unhandledModule', None) + + def test_ast_from_module(self): + import unittest + astroid = self.manager.ast_from_module(unittest) + self.assertEqual(astroid.pure_python, True) + import time + astroid = self.manager.ast_from_module(time) + self.assertEqual(astroid.pure_python, False) + + def test_ast_from_module_cache(self): + """check if the module is in the cache manager""" + import unittest + astroid = self.manager.ast_from_module(unittest) + self.assertEqual(astroid.name, 'unittest') + self.assertIn('unittest', self.manager.astroid_cache) + + def test_ast_from_class(self): + astroid = self.manager.ast_from_class(int) + self.assertEqual(astroid.name, 'int') + self.assertEqual(astroid.parent.frame().name, BUILTINS) + + astroid = self.manager.ast_from_class(object) + self.assertEqual(astroid.name, 'object') + self.assertEqual(astroid.parent.frame().name, BUILTINS) + self.assertIn('__setattr__', astroid) + + def test_ast_from_class_with_module(self): + """check if the method works with the module name""" + astroid = self.manager.ast_from_class(int, int.__module__) + self.assertEqual(astroid.name, 'int') + self.assertEqual(astroid.parent.frame().name, BUILTINS) + + astroid = self.manager.ast_from_class(object, object.__module__) + self.assertEqual(astroid.name, 'object') + self.assertEqual(astroid.parent.frame().name, BUILTINS) + self.assertIn('__setattr__', astroid) + + def test_ast_from_class_attr_error(self): + """give a wrong class at the ast_from_class method""" + self.assertRaises(AstroidBuildingException, self.manager.ast_from_class, None) + + def test_from_directory(self): + obj = self.manager.project_from_files([DATA], _silent_no_wrap, 'data') + self.assertEqual(obj.name, 'data') + 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 = [ + 'data', + 'data.SSL1', + 'data.SSL1.Connection1', + 'data.absimport', + 'data.all', + 'data.appl', + 'data.appl.myConnection', + 'data.email', + 'data.find_test', + 'data.find_test.module', + 'data.find_test.module2', + 'data.find_test.noendingnewline', + 'data.find_test.nonregr', + 'data.format', + 'data.lmfp', + 'data.lmfp.foo', + 'data.module', + 'data.module1abs', + 'data.module1abs.core', + 'data.module2', + 'data.noendingnewline', + 'data.nonregr', + 'data.notall'] + if PY3K: + expected = [e.replace('data', 'data_py3') for e in expected] + self.assertListEqual(sorted(k for k in obj.keys()), expected) + +class BorgAstroidManagerTC(unittest.TestCase): + + def test_borg(self): + """test that the AstroidManager is really a borg, i.e. that two different + instances has same cache""" + first_manager = AstroidManager() + built = first_manager.ast_from_module_name(BUILTINS) + + second_manager = AstroidManager() + second_built = first_manager.ast_from_module_name(BUILTINS) + self.assertIs(built, second_built) + + +if __name__ == '__main__': + unittest.main() + + diff --git a/astroid/tests/unittest_modutils.py b/astroid/tests/unittest_modutils.py new file mode 100644 index 0000000..83b9e89 --- /dev/null +++ b/astroid/tests/unittest_modutils.py @@ -0,0 +1,280 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +""" +unit tests for module modutils (module manipulation utilities) +""" + +import sys +import unittest + +try: + __file__ +except NameError: + __file__ = sys.argv[0] + +from os import path, getcwd, sep +from astroid import modutils + +sys.path.insert(0, path.dirname(__file__)) +DATADIR = path.abspath(path.normpath(path.join(path.dirname(__file__), 'data'))) + + +class ModuleFileTC(unittest.TestCase): + package = "mypypa" + + def tearDown(self): + super(ModuleFileTC, self).tearDown() + for k in list(sys.path_importer_cache.keys()): + if 'MyPyPa' in k: + del sys.path_importer_cache[k] + + def test_find_zipped_module(self): + mtype, mfile = modutils._module_file([self.package], [path.join(DATADIR, 'MyPyPa-0.1.0-py2.5.zip')]) + self.assertEqual(mtype, modutils.ZIPFILE) + self.assertEqual(mfile.split(sep)[-4:], ["tests", "data", "MyPyPa-0.1.0-py2.5.zip", self.package]) + + def test_find_egg_module(self): + mtype, mfile = modutils._module_file([self.package], [path.join(DATADIR, 'MyPyPa-0.1.0-py2.5.egg')]) + self.assertEqual(mtype, modutils.ZIPFILE) + self.assertEqual(mfile.split(sep)[-4:], ["tests", "data", "MyPyPa-0.1.0-py2.5.egg", self.package]) + + +class load_module_from_name_tc(unittest.TestCase): + """ load a python module from it's name """ + + def test_knownValues_load_module_from_name_1(self): + self.assertEqual(modutils.load_module_from_name('sys'), sys) + + def test_knownValues_load_module_from_name_2(self): + self.assertEqual(modutils.load_module_from_name('os.path'), path) + + def test_raise_load_module_from_name_1(self): + self.assertRaises(ImportError, + modutils.load_module_from_name, 'os.path', use_sys=0) + + +class get_module_part_tc(unittest.TestCase): + """given a dotted name return the module part of the name""" + + def test_knownValues_get_module_part_1(self): + self.assertEqual(modutils.get_module_part('astroid.modutils'), + 'astroid.modutils') + + def test_knownValues_get_module_part_2(self): + self.assertEqual(modutils.get_module_part('astroid.modutils.get_module_part'), + 'astroid.modutils') + + def test_knownValues_get_module_part_3(self): + """relative import from given file""" + self.assertEqual(modutils.get_module_part('node_classes.AssName', + modutils.__file__), 'node_classes') + + def test_knownValues_get_compiled_module_part(self): + self.assertEqual(modutils.get_module_part('math.log10'), 'math') + self.assertEqual(modutils.get_module_part('math.log10', __file__), 'math') + + def test_knownValues_get_builtin_module_part(self): + self.assertEqual(modutils.get_module_part('sys.path'), 'sys') + self.assertEqual(modutils.get_module_part('sys.path', '__file__'), 'sys') + + def test_get_module_part_exception(self): + self.assertRaises(ImportError, modutils.get_module_part, 'unknown.module', + modutils.__file__) + + +class modpath_from_file_tc(unittest.TestCase): + """ given an absolute file path return the python module's path as a list """ + + def test_knownValues_modpath_from_file_1(self): + self.assertEqual(modutils.modpath_from_file(modutils.__file__), + ['astroid', 'modutils']) + + def test_knownValues_modpath_from_file_2(self): + self.assertEqual(modutils.modpath_from_file('unittest_modutils.py', + {getcwd(): 'arbitrary.pkg'}), + ['arbitrary', 'pkg', 'unittest_modutils']) + + def test_raise_modpath_from_file_Exception(self): + self.assertRaises(Exception, modutils.modpath_from_file, '/turlututu') + + +class load_module_from_path_tc(unittest.TestCase): + + def test_do_not_load_twice(self): + sys.path.insert(0, DATADIR) + foo = modutils.load_module_from_modpath(['lmfp', 'foo']) + lmfp = modutils.load_module_from_modpath(['lmfp']) + self.assertEqual(len(sys.just_once), 1) + sys.path.pop(0) + del sys.just_once + + +class file_from_modpath_tc(unittest.TestCase): + """given a mod path (i.e. splited module / package name), return the + corresponding file, giving priority to source file over precompiled file + if it exists""" + + def test_site_packages(self): + self.assertEqual(path.realpath(modutils.file_from_modpath(['astroid', 'modutils'])), + path.realpath(modutils.__file__.replace('.pyc', '.py'))) + + def test_std_lib(self): + from os import path + self.assertEqual(path.realpath(modutils.file_from_modpath(['os', 'path']).replace('.pyc', '.py')), + path.realpath(path.__file__.replace('.pyc', '.py'))) + + def test_xmlplus(self): + try: + # don't fail if pyxml isn't installed + from xml.dom import ext + except ImportError: + pass + else: + self.assertEqual(path.realpath(modutils.file_from_modpath(['xml', 'dom', 'ext']).replace('.pyc', '.py')), + path.realpath(ext.__file__.replace('.pyc', '.py'))) + + def test_builtin(self): + self.assertEqual(modutils.file_from_modpath(['sys']), + None) + + + def test_unexisting(self): + self.assertRaises(ImportError, modutils.file_from_modpath, ['turlututu']) + + def test_unicode_in_package_init(self): + # file_from_modpath should not crash when reading an __init__ + # file with unicode characters. + sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), + 'regrtest_data')) + self.addCleanup(sys.path.pop, 0) + + modutils.file_from_modpath(["unicode_package", "core"]) + + +class get_source_file_tc(unittest.TestCase): + + def test(self): + from os import path + self.assertEqual(modutils.get_source_file(path.__file__), + path.normpath(path.__file__.replace('.pyc', '.py'))) + + def test_raise(self): + self.assertRaises(modutils.NoSourceFile, modutils.get_source_file, 'whatever') + + +class is_standard_module_tc(unittest.TestCase): + """ + return true if the module may be considered as a module from the standard + library + """ + + def test_builtins(self): + if sys.version_info < (3, 0): + self.assertEqual(modutils.is_standard_module('__builtin__'), True) + self.assertEqual(modutils.is_standard_module('builtins'), False) + else: + self.assertEqual(modutils.is_standard_module('__builtin__'), False) + self.assertEqual(modutils.is_standard_module('builtins'), True) + + def test_builtin(self): + self.assertEqual(modutils.is_standard_module('sys'), True) + + def test_nonstandard(self): + self.assertEqual(modutils.is_standard_module('logilab'), False) + + def test_unknown(self): + self.assertEqual(modutils.is_standard_module('unknown'), False) + + def test_builtin(self): + self.assertEqual(modutils.is_standard_module('marshal'), True) + + def test_4(self): + import astroid + if sys.version_info > (3, 0): + skip = sys.platform.startswith('win') or '.tox' in astroid.__file__ + if skip: + self.skipTest('imp module has a broken behaviour in Python 3 on ' + 'Windows, returning the module path with different ' + 'case than it should be.') + self.assertEqual(modutils.is_standard_module('hashlib'), True) + self.assertEqual(modutils.is_standard_module('pickle'), True) + self.assertEqual(modutils.is_standard_module('email'), True) + self.assertEqual(modutils.is_standard_module('io'), sys.version_info >= (2, 6)) + self.assertEqual(modutils.is_standard_module('StringIO'), sys.version_info < (3, 0)) + + def test_custom_path(self): + if DATADIR.startswith(modutils.EXT_LIB_DIR): + self.skipTest('known breakage of is_standard_module on installed package') + self.assertEqual(modutils.is_standard_module('data.module', (DATADIR,)), True) + self.assertEqual(modutils.is_standard_module('data.module', (path.abspath(DATADIR),)), True) + + def test_failing_edge_cases(self): + from logilab import common + # using a subpackage/submodule path as std_path argument + self.assertEqual(modutils.is_standard_module('logilab.common', common.__path__), False) + # using a module + object name as modname argument + self.assertEqual(modutils.is_standard_module('sys.path'), True) + # this is because only the first package/module is considered + self.assertEqual(modutils.is_standard_module('sys.whatever'), True) + self.assertEqual(modutils.is_standard_module('logilab.whatever', common.__path__), False) + + +class is_relative_tc(unittest.TestCase): + + + def test_knownValues_is_relative_1(self): + import astroid + self.assertEqual(modutils.is_relative('modutils', astroid.__path__[0]), + True) + + def test_knownValues_is_relative_2(self): + from logilab.common import tree + self.assertEqual(modutils.is_relative('modutils', tree.__file__), + True) + + def test_knownValues_is_relative_3(self): + import astroid + self.assertEqual(modutils.is_relative('astroid', astroid.__path__[0]), + False) + + +class get_module_files_tc(unittest.TestCase): + + def test_knownValues_get_module_files_1(self): # XXXFIXME: TOWRITE + """given a directory return a list of all available python module's files, even + in subdirectories + """ + import data + modules = sorted(modutils.get_module_files(path.join(DATADIR, 'find_test'), + data.__path__[0])) + self.assertEqual(modules, + [path.join(DATADIR, 'find_test', x) for x in ['__init__.py', 'module.py', 'module2.py', 'noendingnewline.py', 'nonregr.py']]) + + def test_load_module_set_attribute(self): + import logilab.common.fileutils + import logilab + del logilab.common.fileutils + del sys.modules['logilab.common.fileutils'] + m = modutils.load_module_from_modpath(['logilab', 'common', 'fileutils']) + self.assertTrue( hasattr(logilab, 'common') ) + self.assertTrue( hasattr(logilab.common, 'fileutils') ) + self.assertTrue( m is logilab.common.fileutils ) + + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_nodes.py b/astroid/tests/unittest_nodes.py new file mode 100644 index 0000000..e9c688c --- /dev/null +++ b/astroid/tests/unittest_nodes.py @@ -0,0 +1,441 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""tests for specific behaviour of astroid nodes +""" +from os.path import join, abspath, dirname +import sys +import unittest + +from astroid.node_classes import unpack_infer +from astroid.bases import BUILTINS, YES, InferenceContext +from astroid.exceptions import AstroidBuildingException, NotFoundError +from astroid import builder, nodes +from astroid.test_utils import require_version + +PY3K = sys.version_info >= (3, 0) + +if PY3K: + from data_py3 import module as test_module + DATA = join(dirname(abspath(__file__)), 'data_py3') +else: + from data import module as test_module + DATA = join(dirname(abspath(__file__)), 'data') + +abuilder = builder.AstroidBuilder() + +class AsString(unittest.TestCase): + + def test_tuple_as_string(self): + def build(string): + return abuilder.string_build(string).body[0].value + + self.assertEqual(build('1,').as_string(), '(1, )') + self.assertEqual(build('1, 2, 3').as_string(), '(1, 2, 3)') + self.assertEqual(build('(1, )').as_string(), '(1, )') + self.assertEqual(build('1, 2, 3').as_string(), '(1, 2, 3)') + + def test_varargs_kwargs_as_string(self): + ast = abuilder.string_build( 'raise_string(*args, **kwargs)').body[0] + self.assertEqual(ast.as_string(), '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(MODULE.as_string(), data) + + def test_module2_as_string(self): + """check as_string on a whole module prepared to be returned identically + """ + data = open(join(DATA, 'module2.py')).read() + self.assertMultiLineEqual(MODULE2.as_string(), data) + + @require_version('2.7') + def test_2_7_as_string(self): + """check as_string for python syntax >= 2.7""" + 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(ast.as_string(), code) + + @require_version('3.0') + def test_3k_as_string(self): + """check as_string for python 3k syntax""" + 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(ast.as_string(), code) + + +class _NodeTC(unittest.TestCase): + """test transformation of If Node""" + CODE = None + @property + def astroid(self): + try: + return self.__class__.__dict__['CODE_Astroid'] + except KeyError: + astroid = abuilder.string_build(self.CODE) + self.__class__.CODE_Astroid = astroid + return astroid + + +class IfNodeTC(_NodeTC): + """test transformation of If Node""" + CODE = """ +if 0: + print() + +if True: + print() +else: + pass + +if "": + print() +elif []: + raise + +if 1: + print() +elif True: + print() +elif func(): + pass +else: + raise + """ + + def test_if_elif_else_node(self): + """test transformation for If node""" + self.assertEqual(len(self.astroid.body), 4) + for stmt in self.astroid.body: + self.assertIsInstance( stmt, nodes.If) + self.assertFalse(self.astroid.body[0].orelse) # simple If + self.assertIsInstance(self.astroid.body[1].orelse[0], nodes.Pass) # If / else + self.assertIsInstance(self.astroid.body[2].orelse[0], nodes.If) # If / elif + self.assertIsInstance(self.astroid.body[3].orelse[0].orelse[0], nodes.If) + + def test_block_range(self): + # XXX ensure expected values + self.assertEqual(self.astroid.block_range(1), (0, 22)) + self.assertEqual(self.astroid.block_range(10), (0, 22)) # XXX (10, 22) ? + self.assertEqual(self.astroid.body[1].block_range(5), (5, 6)) + self.assertEqual(self.astroid.body[1].block_range(6), (6, 6)) + self.assertEqual(self.astroid.body[1].orelse[0].block_range(7), (7, 8)) + self.assertEqual(self.astroid.body[1].orelse[0].block_range(8), (8, 8)) + + +class TryExceptNodeTC(_NodeTC): + CODE = """ +try: + print ('pouet') +except IOError: + pass +except UnicodeError: + print() +else: + print() + """ + def test_block_range(self): + # XXX ensure expected values + self.assertEqual(self.astroid.body[0].block_range(1), (1, 8)) + self.assertEqual(self.astroid.body[0].block_range(2), (2, 2)) + self.assertEqual(self.astroid.body[0].block_range(3), (3, 8)) + self.assertEqual(self.astroid.body[0].block_range(4), (4, 4)) + self.assertEqual(self.astroid.body[0].block_range(5), (5, 5)) + self.assertEqual(self.astroid.body[0].block_range(6), (6, 6)) + self.assertEqual(self.astroid.body[0].block_range(7), (7, 7)) + self.assertEqual(self.astroid.body[0].block_range(8), (8, 8)) + + +class TryFinallyNodeTC(_NodeTC): + CODE = """ +try: + print ('pouet') +finally: + print ('pouet') + """ + def test_block_range(self): + # XXX ensure expected values + self.assertEqual(self.astroid.body[0].block_range(1), (1, 4)) + self.assertEqual(self.astroid.body[0].block_range(2), (2, 2)) + self.assertEqual(self.astroid.body[0].block_range(3), (3, 4)) + self.assertEqual(self.astroid.body[0].block_range(4), (4, 4)) + + +class TryFinally25NodeTC(_NodeTC): + CODE = """ +try: + print('pouet') +except Exception: + print ('oops') +finally: + print ('pouet') + """ + def test_block_range(self): + # XXX ensure expected values + self.assertEqual(self.astroid.body[0].block_range(1), (1, 6)) + self.assertEqual(self.astroid.body[0].block_range(2), (2, 2)) + self.assertEqual(self.astroid.body[0].block_range(3), (3, 4)) + self.assertEqual(self.astroid.body[0].block_range(4), (4, 4)) + self.assertEqual(self.astroid.body[0].block_range(5), (5, 5)) + self.assertEqual(self.astroid.body[0].block_range(6), (6, 6)) + + +class TryExcept2xNodeTC(_NodeTC): + CODE = """ +try: + hello +except AttributeError, (retval, desc): + pass + """ + def test_tuple_attribute(self): + if sys.version_info >= (3, 0): + self.skipTest('syntax removed from py3.x') + handler = self.astroid.body[0].handlers[0] + self.assertIsInstance(handler.name, nodes.Tuple) + + +MODULE = abuilder.module_build(test_module) +MODULE2 = abuilder.file_build(join(DATA, 'module2.py'), 'data.module2') + + +class ImportNodeTC(unittest.TestCase): + + def test_import_self_resolve(self): + myos = next(MODULE2.igetattr('myos')) + self.assertTrue(isinstance(myos, nodes.Module), myos) + self.assertEqual(myos.name, 'os') + self.assertEqual(myos.qname(), 'os') + self.assertEqual(myos.pytype(), '%s.module' % BUILTINS) + + def test_from_self_resolve(self): + pb = next(MODULE.igetattr('pb')) + self.assertTrue(isinstance(pb, nodes.Class), pb) + self.assertEqual(pb.root().name, 'logilab.common.shellutils') + self.assertEqual(pb.qname(), 'logilab.common.shellutils.ProgressBar') + if pb.newstyle: + self.assertEqual(pb.pytype(), '%s.type' % BUILTINS) + else: + self.assertEqual(pb.pytype(), '%s.classobj' % BUILTINS) + abspath = next(MODULE2.igetattr('abspath')) + self.assertTrue(isinstance(abspath, nodes.Function), abspath) + self.assertEqual(abspath.root().name, 'os.path') + self.assertEqual(abspath.qname(), 'os.path.abspath') + self.assertEqual(abspath.pytype(), '%s.function' % BUILTINS) + + def test_real_name(self): + from_ = MODULE['pb'] + self.assertEqual(from_.real_name('pb'), 'ProgressBar') + imp_ = MODULE['os'] + self.assertEqual(imp_.real_name('os'), 'os') + self.assertRaises(NotFoundError, imp_.real_name, 'os.path') + imp_ = MODULE['pb'] + self.assertEqual(imp_.real_name('pb'), 'ProgressBar') + self.assertRaises(NotFoundError, imp_.real_name, 'ProgressBar') + imp_ = MODULE2['YO'] + self.assertEqual(imp_.real_name('YO'), 'YO') + self.assertRaises(NotFoundError, imp_.real_name, 'data') + + def test_as_string(self): + ast = MODULE['modutils'] + self.assertEqual(ast.as_string(), "from astroid import modutils") + ast = MODULE['pb'] + self.assertEqual(ast.as_string(), "from logilab.common.shellutils import ProgressBar as pb") + ast = MODULE['os'] + self.assertEqual(ast.as_string(), "import os.path") + code = """from . import here +from .. import door +from .store import bread +from ..cave import wine\n\n""" + ast = abuilder.string_build(code) + self.assertMultiLineEqual(ast.as_string(), code) + + def test_bad_import_inference(self): + # Explication of bug + '''When we import PickleError from nonexistent, a call to the infer + method of this From node will be made by unpack_infer. + inference.infer_from will try to import this module, which will fail and + raise a InferenceException (by mixins.do_import_module). The infer_name + will catch this exception and yield and YES instead. + ''' + + code = '''try: + from pickle import PickleError +except ImportError: + from nonexistent import PickleError + +try: + pass +except PickleError: + pass + ''' + + astroid = abuilder.string_build(code) + from_node = astroid.body[1].handlers[0].body[0] + handler_type = astroid.body[1].handlers[0].type + + excs = list(unpack_infer(handler_type)) + + def test_absolute_import(self): + astroid = abuilder.file_build(join(DATA, 'absimport.py')) + ctx = InferenceContext() + # will fail if absolute import failed + next(astroid['message'].infer(ctx, lookupname='message')) + m = next(astroid['email'].infer(ctx, lookupname='email')) + self.assertFalse(m.file.startswith(join(DATA, 'email.py'))) + + def test_more_absolute_import(self): + sys.path.insert(0, join(DATA, 'moreabsimport')) + try: + astroid = abuilder.file_build(join(DATA, 'module1abs/__init__.py')) + self.assertIn('sys', astroid.locals) + finally: + sys.path.pop(0) + + +class CmpNodeTC(unittest.TestCase): + def test_as_string(self): + ast = abuilder.string_build("a == 2").body[0] + self.assertEqual(ast.as_string(), "a == 2") + + +class ConstNodeTC(unittest.TestCase): + + def _test(self, value): + node = nodes.const_factory(value) + self.assertIsInstance(node._proxied, nodes.Class) + self.assertEqual(node._proxied.name, value.__class__.__name__) + self.assertIs(node.value, value) + self.assertTrue(node._proxied.parent) + self.assertEqual(node._proxied.root().name, value.__class__.__module__) + + def test_none(self): + self._test(None) + + def test_bool(self): + self._test(True) + + def test_int(self): + self._test(1) + + def test_float(self): + self._test(1.0) + + def test_complex(self): + self._test(1.0j) + + def test_str(self): + self._test('a') + + def test_unicode(self): + self._test(u'a') + + +class NameNodeTC(unittest.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 AstroidBuildingException + 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(unittest.TestCase): + def test_linenumbering(self): + ast = abuilder.string_build(''' +def func(a, + b): pass +x = lambda x: None + ''') + self.assertEqual(ast['func'].args.fromlineno, 2) + self.assertFalse(ast['func'].args.is_statement) + xlambda = next(ast['x'].infer()) + self.assertEqual(xlambda.args.fromlineno, 4) + self.assertEqual(xlambda.args.tolineno, 4) + self.assertFalse(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(unittest.TestCase): + def test(self): + for code in ('a[0]', 'a[1:3]', 'a[:-1:step]', 'a[:,newaxis]', + 'a[newaxis,:]', 'del L[::2]', 'del A[1]', 'del Br[:]'): + ast = abuilder.string_build(code).body[0] + self.assertEqual(ast.as_string(), code) + + def test_slice_and_subscripts(self): + code = """a[:1] = bord[2:] +a[:1] = bord[2:] +del bree[3:d] +bord[2:] +del av[d::f], a[df:] +a[:1] = bord[2:] +del SRC[::1,newaxis,1:] +tous[vals] = 1010 +del thousand[key] +del a[::2], a[:-1:step] +del Fee.form[left:] +aout.vals = miles.of_stuff +del (ccok, (name.thing, foo.attrib.value)), Fee.form[left:] +if all[1] == bord[0:]: + pass\n\n""" + ast = abuilder.string_build(code) + self.assertEqual(ast.as_string(), code) + +class EllipsisNodeTC(unittest.TestCase): + def test(self): + ast = abuilder.string_build('a[...]').body[0] + self.assertEqual(ast.as_string(), 'a[...]') + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_python3.py b/astroid/tests/unittest_python3.py new file mode 100644 index 0000000..e150293 --- /dev/null +++ b/astroid/tests/unittest_python3.py @@ -0,0 +1,220 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +from textwrap import dedent +import unittest + +from astroid.node_classes import Assign, Discard, YieldFrom, Name, Const +from astroid.manager import AstroidManager +from astroid.builder import AstroidBuilder +from astroid.scoped_nodes import Class, Function +from astroid.test_utils import require_version + + +class Python3TC(unittest.TestCase): + def setUp(self): + self.manager = AstroidManager() + self.manager.clear_cache() # take care of borg + self.builder = AstroidBuilder(self.manager) + + @require_version('3.0') + def test_starred_notation(self): + astroid = self.builder.string_build("*a, b = [1, 2, 3]", 'test', 'test') + + # Get the star node + node = next(next(next(astroid.get_children()).get_children()).get_children()) + + self.assertTrue(isinstance(node.ass_type(), Assign)) + + @require_version('3.3') + def test_yield_from(self): + body = dedent(""" + def func(): + yield from iter([1, 2]) + """) + astroid = self.builder.string_build(body) + func = astroid.body[0] + self.assertIsInstance(func, Function) + yieldfrom_stmt = func.body[0] + + self.assertIsInstance(yieldfrom_stmt, Discard) + self.assertIsInstance(yieldfrom_stmt.value, YieldFrom) + self.assertEqual(yieldfrom_stmt.as_string(), + 'yield from iter([1, 2])') + + @require_version('3.3') + def test_yield_from_is_generator(self): + body = dedent(""" + def func(): + yield from iter([1, 2]) + """) + astroid = self.builder.string_build(body) + func = astroid.body[0] + self.assertIsInstance(func, Function) + self.assertTrue(func.is_generator()) + + @require_version('3.3') + def test_yield_from_as_string(self): + body = dedent(""" + def func(): + yield from iter([1, 2]) + value = yield from other() + """) + astroid = self.builder.string_build(body) + func = astroid.body[0] + self.assertEqual(func.as_string().strip(), body.strip()) + + # metaclass tests + + @require_version('3.0') + def test_simple_metaclass(self): + astroid = self.builder.string_build("class Test(metaclass=type): pass") + klass = astroid.body[0] + + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, Class) + self.assertEqual(metaclass.name, 'type') + + @require_version('3.0') + def test_metaclass_error(self): + astroid = self.builder.string_build("class Test(metaclass=typ): pass") + klass = astroid.body[0] + self.assertFalse(klass.metaclass()) + + @require_version('3.0') + def test_metaclass_imported(self): + astroid = self.builder.string_build(dedent(""" + from abc import ABCMeta + class Test(metaclass=ABCMeta): pass""")) + klass = astroid.body[1] + + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, Class) + self.assertEqual(metaclass.name, 'ABCMeta') + + @require_version('3.0') + def test_as_string(self): + body = dedent(""" + from abc import ABCMeta + class Test(metaclass=ABCMeta): pass""") + astroid = self.builder.string_build(body) + klass = astroid.body[1] + + self.assertEqual(klass.as_string(), + '\n\nclass Test(metaclass=ABCMeta):\n pass\n') + + @require_version('3.0') + def test_old_syntax_works(self): + astroid = self.builder.string_build(dedent(""" + class Test: + __metaclass__ = type + class SubTest(Test): pass + """)) + klass = astroid['SubTest'] + metaclass = klass.metaclass() + self.assertIsNone(metaclass) + + @require_version('3.0') + def test_metaclass_yes_leak(self): + astroid = self.builder.string_build(dedent(""" + # notice `ab` instead of `abc` + from ab import ABCMeta + + class Meta(metaclass=ABCMeta): pass + """)) + klass = astroid['Meta'] + self.assertIsNone(klass.metaclass()) + + @require_version('3.0') + def test_parent_metaclass(self): + astroid = self.builder.string_build(dedent(""" + from abc import ABCMeta + class Test(metaclass=ABCMeta): pass + class SubTest(Test): pass + """)) + klass = astroid['SubTest'] + self.assertTrue(klass.newstyle) + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, Class) + self.assertEqual(metaclass.name, 'ABCMeta') + + @require_version('3.0') + def test_metaclass_ancestors(self): + astroid = self.builder.string_build(dedent(""" + from abc import ABCMeta + + class FirstMeta(metaclass=ABCMeta): pass + class SecondMeta(metaclass=type): + pass + + class Simple: + pass + + class FirstImpl(FirstMeta): pass + class SecondImpl(FirstImpl): pass + class ThirdImpl(Simple, SecondMeta): + pass + """)) + classes = { + 'ABCMeta': ('FirstImpl', 'SecondImpl'), + 'type': ('ThirdImpl', ) + } + for metaclass, names in classes.items(): + for name in names: + impl = astroid[name] + meta = impl.metaclass() + self.assertIsInstance(meta, Class) + self.assertEqual(meta.name, metaclass) + + @require_version('3.0') + def test_annotation_support(self): + astroid = self.builder.string_build(dedent(""" + def test(a: int, b: str, c: None, d, e, + *args: float, **kwargs: int)->int: + pass + """)) + func = astroid['test'] + self.assertIsInstance(func.args.varargannotation, Name) + self.assertEqual(func.args.varargannotation.name, 'float') + self.assertIsInstance(func.args.kwargannotation, Name) + self.assertEqual(func.args.kwargannotation.name, 'int') + self.assertIsInstance(func.returns, Name) + self.assertEqual(func.returns.name, 'int') + arguments = func.args + self.assertIsInstance(arguments.annotations[0], Name) + self.assertEqual(arguments.annotations[0].name, 'int') + self.assertIsInstance(arguments.annotations[1], Name) + self.assertEqual(arguments.annotations[1].name, 'str') + self.assertIsInstance(arguments.annotations[2], Const) + self.assertIsNone(arguments.annotations[2].value) + self.assertIsNone(arguments.annotations[3]) + self.assertIsNone(arguments.annotations[4]) + + astroid = self.builder.string_build(dedent(""" + def test(a: int=1, b: str=2): + pass + """)) + func = astroid['test'] + self.assertIsInstance(func.args.annotations[0], Name) + self.assertEqual(func.args.annotations[0].name, 'int') + self.assertIsInstance(func.args.annotations[1], Name) + self.assertEqual(func.args.annotations[1].name, 'str') + self.assertIsNone(func.returns) + + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_raw_building.py b/astroid/tests/unittest_raw_building.py new file mode 100644 index 0000000..17057d5 --- /dev/null +++ b/astroid/tests/unittest_raw_building.py @@ -0,0 +1,48 @@ +import unittest + +from astroid.raw_building import (attach_dummy_node, build_module, build_class, build_function, build_from_import) + +class RawBuildingTC(unittest.TestCase): + + def test_attach_dummy_node(self): + node = build_module('MyModule') + dummy = attach_dummy_node(node, 'DummyNode') + self.assertEqual(1, len(list(node.get_children()))) + + def test_build_module(self): + node = build_module('MyModule') + self.assertEqual(node.name, 'MyModule') + self.assertEqual(node.pure_python, False) + self.assertEqual(node.package, False) + self.assertEqual(node.parent, None) + + def test_build_class(self): + node = build_class('MyClass') + self.assertEqual(node.name, 'MyClass') + self.assertEqual(node.doc, None) + + def test_build_function(self): + node = build_function('MyFunction') + self.assertEqual(node.name, 'MyFunction') + self.assertEqual(node.doc, None) + + def test_build_function_args(self): + args = ['myArgs1', 'myArgs2'] + node = build_function('MyFunction', args) + self.assertEqual('myArgs1', node.args.args[0].name) + self.assertEqual('myArgs2', node.args.args[1].name) + self.assertEqual(2, len(node.args.args)) + + def test_build_function_defaults(self): + defaults = [ 'defaults1', 'defaults2' ] + node = build_function('MyFunction', None, defaults) + self.assertEqual(2, len(node.args.defaults)) + + def test_build_from_import(self): + names = ['exceptions, inference, inspector'] + node = build_from_import('astroid', names) + self.assertEqual(len(names), len(node.names)) + + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_regrtest.py b/astroid/tests/unittest_regrtest.py new file mode 100644 index 0000000..f40991f --- /dev/null +++ b/astroid/tests/unittest_regrtest.py @@ -0,0 +1,224 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +from os.path import join, abspath, dirname +import sys +import unittest + +from astroid import ResolveError, MANAGER, Instance, nodes, YES, InferenceError +from astroid.builder import AstroidBuilder +from astroid.raw_building import build_module +from astroid.manager import AstroidManager +from astroid.test_utils import require_version + + +class NonRegressionTC(unittest.TestCase): + + def setUp(self): + sys.path.insert(0, join(dirname(abspath(__file__)), 'regrtest_data')) + + def tearDown(self): + # Since we may have created a brainless manager, leading + # to a new cache builtin module and proxy classes in the constants, + # clear out the global manager cache. + MANAGER.clear_cache() + sys.path.pop(0) + + def brainless_manager(self): + manager = AstroidManager() + # avoid caching into the AstroidManager borg since we get problems + # with other tests : + manager.__dict__ = {} + manager.astroid_cache = {} + manager._mod_file_cache = {} + manager.transforms = {} + manager.clear_cache() # trigger proper bootstraping + return manager + + def test_module_path(self): + man = self.brainless_manager() + mod = man.ast_from_module_name('package.import_package_subpackage_module') + package = next(mod.igetattr('package')) + self.assertEqual(package.name, 'package') + subpackage = next(package.igetattr('subpackage')) + self.assertIsInstance(subpackage, nodes.Module) + self.assertTrue(subpackage.package) + self.assertEqual(subpackage.name, 'package.subpackage') + module = next(subpackage.igetattr('module')) + self.assertEqual(module.name, 'package.subpackage.module') + + + def test_package_sidepackage(self): + manager = self.brainless_manager() + assert 'package.sidepackage' not in MANAGER.astroid_cache + package = manager.ast_from_module_name('absimp') + self.assertIsInstance(package, nodes.Module) + self.assertTrue(package.package) + subpackage = next(package.getattr('sidepackage')[0].infer()) + self.assertIsInstance(subpackage, nodes.Module) + self.assertTrue(subpackage.package) + self.assertEqual(subpackage.name, 'absimp.sidepackage') + + + def test_living_property(self): + builder = AstroidBuilder() + builder._done = {} + builder._module = sys.modules[__name__] + builder.object_build(build_module('module_name', ''), Whatever) + + + def test_new_style_class_detection(self): + try: + import pygtk + except ImportError: + self.skipTest('test skipped: pygtk is not available') + # XXX may fail on some pygtk version, because objects in + # gobject._gobject have __module__ set to gobject :( + builder = AstroidBuilder() + data = """ +import pygtk +pygtk.require("2.6") +import gobject + +class A(gobject.GObject): + pass +""" + astroid = builder.string_build(data, __name__, __file__) + a = astroid['A'] + self.assertTrue(a.newstyle) + + + def test_pylint_config_attr(self): + try: + from pylint import lint + except ImportError: + self.skipTest('pylint not available') + mod = MANAGER.ast_from_module_name('pylint.lint') + pylinter = mod['PyLinter'] + expect = ['OptionsManagerMixIn', 'object', 'MessagesHandlerMixIn', + 'ReportsHandlerMixIn', 'BaseTokenChecker', 'BaseChecker', + 'OptionsProviderMixIn'] + self.assertListEqual([c.name for c in pylinter.ancestors()], + expect) + self.assertTrue(list(Instance(pylinter).getattr('config'))) + infered = list(Instance(pylinter).igetattr('config')) + self.assertEqual(len(infered), 1) + self.assertEqual(infered[0].root().name, 'optparse') + self.assertEqual(infered[0].name, 'Values') + + def test_numpy_crash(self): + """test don't crash on numpy""" + #a crash occured somewhere in the past, and an + # InferenceError instead of a crash was better, but now we even infer! + try: + import numpy + except ImportError: + self.skipTest('test skipped: numpy is not available') + builder = AstroidBuilder() + data = """ +from numpy import multiply + +multiply(1, 2, 3) +""" + astroid = builder.string_build(data, __name__, __file__) + callfunc = astroid.body[1].value.func + infered = callfunc.infered() + self.assertEqual(len(infered), 1) + self.assertIsInstance(infered[0], Instance) + + @require_version('3.0') + def test_nameconstant(self): + # used to fail for Python 3.4 + builder = AstroidBuilder() + astroid = builder.string_build("def test(x=True): pass") + default = astroid.body[0].args.args[0] + self.assertEqual(default.name, 'x') + self.assertEqual(next(default.infer()).value, True) + + @require_version('2.7') + def test_with_infer_assnames(self): + builder = AstroidBuilder() + data = """ +with open('a.txt') as stream, open('b.txt'): + stream.read() +""" + astroid = builder.string_build(data, __name__, __file__) + # Used to crash due to the fact that the second + # context manager didn't use an assignment name. + list(astroid.nodes_of_class(nodes.CallFunc))[-1].infered() + + def test_recursion_regression_issue25(self): + builder = AstroidBuilder() + data = """ +import recursion as base + +_real_Base = base.Base + +class Derived(_real_Base): + pass + +def run(): + base.Base = Derived +""" + astroid = builder.string_build(data, __name__, __file__) + # Used to crash in _is_metaclass, due to wrong + # ancestors chain + classes = astroid.nodes_of_class(nodes.Class) + for klass in classes: + # triggers the _is_metaclass call + klass.type + + def test_decorator_callchain_issue42(self): + builder = AstroidBuilder() + data = """ + +def test(): + def factory(func): + def newfunc(): + func() + return newfunc + return factory + +@test() +def crash(): + pass +""" + astroid = builder.string_build(data, __name__, __file__) + self.assertEqual(astroid['crash'].type, 'function') + + def test_filter_stmts_scoping(self): + builder = AstroidBuilder() + data = """ +def test(): + compiler = int() + class B(compiler.__class__): + pass + compiler = B() + return compiler +""" + astroid = builder.string_build(data, __name__, __file__) + test = astroid['test'] + result = next(test.infer_call_result(astroid)) + self.assertIsInstance(result, Instance) + base = next(result._proxied.bases[0].infer()) + self.assertEqual(base.name, 'int') + +class Whatever(object): + a = property(lambda x: x, lambda x: x) + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_scoped_nodes.py b/astroid/tests/unittest_scoped_nodes.py new file mode 100644 index 0000000..824e35f --- /dev/null +++ b/astroid/tests/unittest_scoped_nodes.py @@ -0,0 +1,1008 @@ +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""tests for specific behaviour of astroid scoped nodes (i.e. module, class and +function) +""" +import sys +from os.path import join, abspath, dirname +from functools import partial +from textwrap import dedent +import unittest + +from astroid import YES, builder, nodes, scoped_nodes, \ + InferenceError, NotFoundError, NoDefault +from astroid.bases import BUILTINS, Instance, BoundMethod, UnboundMethod +from astroid.test_utils import extract_node, require_version + +abuilder = builder.AstroidBuilder() +PY3K = sys.version_info >= (3, 0) + +if PY3K: + DATA = join(dirname(abspath(__file__)), 'data_py3') + import data_py3 as data +else: + DATA = join(dirname(abspath(__file__)), 'data') + import data + +REGRTEST_DATA = join(dirname(abspath(__file__)), 'regrtest_data') +MODULE = abuilder.file_build(join(DATA, 'module.py'), 'data.module') +MODULE2 = abuilder.file_build(join(DATA, 'module2.py'), 'data.module2') +NONREGR = abuilder.file_build(join(DATA, 'nonregr.py'), 'data.nonregr') + +PACK = abuilder.file_build(join(DATA, '__init__.py'), 'data') + +def _test_dict_interface(self, node, test_attr): + self.assertIs(node[test_attr], node[test_attr]) + self.assertIn(test_attr, node) + node.keys() + node.values() + node.items() + iter(node) + + +class ModuleNodeTC(unittest.TestCase): + + def test_special_attributes(self): + self.assertEqual(len(MODULE.getattr('__name__')), 1) + self.assertIsInstance(MODULE.getattr('__name__')[0], nodes.Const) + self.assertEqual(MODULE.getattr('__name__')[0].value, 'data.module') + self.assertEqual(len(MODULE.getattr('__doc__')), 1) + self.assertIsInstance(MODULE.getattr('__doc__')[0], nodes.Const) + self.assertEqual(MODULE.getattr('__doc__')[0].value, 'test module for astroid\n') + self.assertEqual(len(MODULE.getattr('__file__')), 1) + self.assertIsInstance(MODULE.getattr('__file__')[0], nodes.Const) + self.assertEqual(MODULE.getattr('__file__')[0].value, join(DATA, 'module.py')) + self.assertEqual(len(MODULE.getattr('__dict__')), 1) + self.assertIsInstance(MODULE.getattr('__dict__')[0], nodes.Dict) + self.assertRaises(NotFoundError, MODULE.getattr, '__path__') + self.assertEqual(len(PACK.getattr('__path__')), 1) + self.assertIsInstance(PACK.getattr('__path__')[0], nodes.List) + + def test_dict_interface(self): + _test_dict_interface(self, MODULE, 'YO') + + def test_getattr(self): + yo = MODULE.getattr('YO')[0] + self.assertIsInstance(yo, nodes.Class) + self.assertEqual(yo.name, 'YO') + red = next(MODULE.igetattr('redirect')) + self.assertIsInstance(red, nodes.Function) + self.assertEqual(red.name, 'four_args') + pb = next(MODULE.igetattr('pb')) + self.assertIsInstance(pb, nodes.Class) + self.assertEqual(pb.name, 'ProgressBar') + # resolve packageredirection + sys.path.insert(1, DATA) + mod = abuilder.file_build(join(DATA, 'appl/myConnection.py'), + 'appl.myConnection') + try: + ssl = next(mod.igetattr('SSL1')) + cnx = next(ssl.igetattr('Connection')) + self.assertEqual(cnx.__class__, nodes.Class) + self.assertEqual(cnx.name, 'Connection') + self.assertEqual(cnx.root().name, 'SSL1.Connection1') + finally: + del sys.path[1] + self.assertEqual(len(NONREGR.getattr('enumerate')), 2) + # raise ResolveError + self.assertRaises(InferenceError, MODULE.igetattr, 'YOAA') + + def test_wildard_import_names(self): + m = abuilder.file_build(join(DATA, 'all.py'), 'all') + self.assertEqual(m.wildcard_import_names(), ['Aaa', '_bla', 'name']) + m = abuilder.file_build(join(DATA, 'notall.py'), 'notall') + res = sorted(m.wildcard_import_names()) + self.assertEqual(res, ['Aaa', 'func', 'name', 'other']) + + m = abuilder.string_build(dedent(''' + from missing import tzop + trop = "test" + __all__ = (trop, "test1", tzop, 42) + ''')) + res = sorted(m.wildcard_import_names()) + self.assertEqual(res, ["test", "test1"]) + + m = abuilder.string_build(dedent(''' + test = tzop = 42 + __all__ = ('test', ) + ('tzop', ) + ''')) + res = sorted(m.wildcard_import_names()) + self.assertEqual(res, ['test', 'tzop']) + + def test_module_getattr(self): + data = ''' +appli = application +appli += 2 +del appli + ''' + astroid = abuilder.string_build(data, __name__, __file__) + # test del statement not returned by getattr + self.assertEqual(len(astroid.getattr('appli')), 2, + astroid.getattr('appli')) + + def test_relative_to_absolute_name(self): + # package + mod = nodes.Module('very.multi.package', 'doc') + mod.package = True + modname = mod.relative_to_absolute_name('utils', 1) + self.assertEqual(modname, 'very.multi.package.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.package.utils') + modname = mod.relative_to_absolute_name('', 1) + self.assertEqual(modname, 'very.multi.package') + # non package + mod = nodes.Module('very.multi.module', 'doc') + 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') + modname = mod.relative_to_absolute_name('', 1) + self.assertEqual(modname, 'very.multi') + + def test_import_1(self): + data = '''from . import subpackage''' + astroid = abuilder.string_build(data, 'package', join(REGRTEST_DATA, 'package', '__init__.py')) + sys.path.insert(1, REGRTEST_DATA) + try: + m = astroid.import_module('', level=1) + self.assertEqual(m.name, 'package') + infered = list(astroid.igetattr('subpackage')) + self.assertEqual(len(infered), 1) + self.assertEqual(infered[0].name, 'package.subpackage') + finally: + del sys.path[1] + + + def test_import_2(self): + data = '''from . import subpackage as pouet''' + astroid = abuilder.string_build(data, 'package', join(dirname(abspath(__file__)), 'regrtest_data', 'package', '__init__.py')) + sys.path.insert(1, REGRTEST_DATA) + try: + m = astroid.import_module('', level=1) + self.assertEqual(m.name, 'package') + infered = list(astroid.igetattr('pouet')) + self.assertEqual(len(infered), 1) + self.assertEqual(infered[0].name, 'package.subpackage') + finally: + del sys.path[1] + + + def test_file_stream_in_memory(self): + data = '''irrelevant_variable is irrelevant''' + astroid = abuilder.string_build(data, 'in_memory') + self.assertEqual(astroid.file_stream.read().decode(), data) + + def test_file_stream_physical(self): + path = join(DATA, 'all.py') + astroid = abuilder.file_build(path, 'all') + with open(path, 'rb') as file_io: + self.assertEqual(astroid.file_stream.read(), file_io.read()) + + +class FunctionNodeTC(unittest.TestCase): + + def test_special_attributes(self): + func = MODULE2['make_class'] + self.assertEqual(len(func.getattr('__name__')), 1) + self.assertIsInstance(func.getattr('__name__')[0], nodes.Const) + self.assertEqual(func.getattr('__name__')[0].value, 'make_class') + self.assertEqual(len(func.getattr('__doc__')), 1) + self.assertIsInstance(func.getattr('__doc__')[0], nodes.Const) + self.assertEqual(func.getattr('__doc__')[0].value, 'check base is correctly resolved to Concrete0') + self.assertEqual(len(MODULE.getattr('__dict__')), 1) + self.assertIsInstance(MODULE.getattr('__dict__')[0], nodes.Dict) + + def test_dict_interface(self): + _test_dict_interface(self, MODULE['global_access'], 'local') + + def test_default_value(self): + func = MODULE2['make_class'] + self.assertIsInstance(func.args.default_value('base'), nodes.Getattr) + self.assertRaises(NoDefault, func.args.default_value, 'args') + self.assertRaises(NoDefault, func.args.default_value, 'kwargs') + self.assertRaises(NoDefault, func.args.default_value, 'any') + #self.assertIsInstance(func.mularg_class('args'), nodes.Tuple) + #self.assertIsInstance(func.mularg_class('kwargs'), nodes.Dict) + #self.assertIsNone(func.mularg_class('base')) + + def test_navigation(self): + function = MODULE['global_access'] + self.assertEqual(function.statement(), function) + l_sibling = function.previous_sibling() + # check taking parent if child is not a stmt + self.assertIsInstance(l_sibling, nodes.Assign) + child = function.args.args[0] + self.assertIs(l_sibling, child.previous_sibling()) + r_sibling = function.next_sibling() + self.assertIsInstance(r_sibling, nodes.Class) + self.assertEqual(r_sibling.name, 'YO') + self.assertIs(r_sibling, child.next_sibling()) + last = r_sibling.next_sibling().next_sibling().next_sibling() + self.assertIsInstance(last, nodes.Assign) + self.assertIsNone(last.next_sibling()) + first = l_sibling.previous_sibling().previous_sibling().previous_sibling().previous_sibling().previous_sibling() + self.assertIsNone(first.previous_sibling()) + + def test_nested_args(self): + if sys.version_info >= (3, 0): + self.skipTest("nested args has been removed in py3.x") + code = ''' +def nested_args(a, (b, c, d)): + "nested arguments test" + ''' + tree = abuilder.string_build(code) + func = tree['nested_args'] + self.assertEqual(sorted(func.locals), ['a', 'b', 'c', 'd']) + self.assertEqual(func.args.format_args(), 'a, (b, c, d)') + + def test_four_args(self): + func = MODULE['four_args'] + #self.assertEqual(func.args.args, ['a', ('b', 'c', 'd')]) + local = sorted(func.keys()) + self.assertEqual(local, ['a', 'b', 'c', 'd']) + self.assertEqual(func.type, 'function') + + def test_format_args(self): + func = MODULE2['make_class'] + self.assertEqual(func.args.format_args(), + 'any, base=%s.module.YO, *args, **kwargs' % (data.__name__,)) + func = MODULE['four_args'] + self.assertEqual(func.args.format_args(), 'a, b, c, d') + + def test_is_generator(self): + self.assertTrue(MODULE2['generator'].is_generator()) + self.assertFalse(MODULE2['not_a_generator'].is_generator()) + self.assertFalse(MODULE2['make_class'].is_generator()) + + def test_is_abstract(self): + method = MODULE2['AbstractClass']['to_override'] + self.assertTrue(method.is_abstract(pass_is_abstract=False)) + self.assertEqual(method.qname(), 'data.module2.AbstractClass.to_override') + self.assertEqual(method.pytype(), '%s.instancemethod' % BUILTINS) + method = MODULE2['AbstractClass']['return_something'] + self.assertFalse(method.is_abstract(pass_is_abstract=False)) + # non regression : test raise "string" doesn't cause an exception in is_abstract + func = MODULE2['raise_string'] + self.assertFalse(func.is_abstract(pass_is_abstract=False)) + + def test_is_abstract_decorated(self): + methods = extract_node(""" + import abc + + class Klass(object): + @abc.abstractproperty + def prop(self): #@ + pass + + @abc.abstractmethod + def method1(self): #@ + pass + + some_other_decorator = lambda x: x + @some_other_decorator + def method2(self): #@ + pass + + """) + self.assertTrue(methods[0].is_abstract(pass_is_abstract=False)) + self.assertTrue(methods[1].is_abstract(pass_is_abstract=False)) + self.assertFalse(methods[2].is_abstract(pass_is_abstract=False)) + +## def test_raises(self): +## method = MODULE2['AbstractClass']['to_override'] +## self.assertEqual([str(term) for term in method.raises()], +## ["CallFunc(Name('NotImplementedError'), [], None, None)"] ) + +## def test_returns(self): +## method = MODULE2['AbstractClass']['return_something'] +## # use string comp since Node doesn't handle __cmp__ +## self.assertEqual([str(term) for term in method.returns()], +## ["Const('toto')", "Const(None)"]) + + def test_lambda_pytype(self): + data = ''' +def f(): + g = lambda: None + ''' + astroid = abuilder.string_build(data, __name__, __file__) + g = list(astroid['f'].ilookup('g'))[0] + self.assertEqual(g.pytype(), '%s.function' % BUILTINS) + + def test_lambda_qname(self): + astroid = abuilder.string_build(''' +lmbd = lambda: None +''', __name__, __file__) + self.assertEqual('%s.' % __name__, astroid['lmbd'].parent.value.qname()) + + def test_is_method(self): + data = ''' +class A: + def meth1(self): + return 1 + @classmethod + def meth2(cls): + return 2 + @staticmethod + def meth3(): + return 3 + +def function(): + return 0 + +@staticmethod +def sfunction(): + return -1 + ''' + astroid = abuilder.string_build(data, __name__, __file__) + self.assertTrue(astroid['A']['meth1'].is_method()) + self.assertTrue(astroid['A']['meth2'].is_method()) + self.assertTrue(astroid['A']['meth3'].is_method()) + self.assertFalse(astroid['function'].is_method()) + self.assertFalse(astroid['sfunction'].is_method()) + + def test_argnames(self): + if sys.version_info < (3, 0): + code = 'def f(a, (b, c), *args, **kwargs): pass' + else: + code = 'def f(a, b, c, *args, **kwargs): pass' + astroid = abuilder.string_build(code, __name__, __file__) + self.assertEqual(astroid['f'].argnames(), ['a', 'b', 'c', 'args', 'kwargs']) + + def test_return_nothing(self): + """test infered value on a function with empty return""" + data = ''' +def func(): + return + +a = func() +''' + astroid = abuilder.string_build(data, __name__, __file__) + call = astroid.body[1].value + func_vals = call.infered() + self.assertEqual(len(func_vals), 1) + self.assertIsInstance(func_vals[0], nodes.Const) + self.assertIsNone(func_vals[0].value) + + def test_func_instance_attr(self): + """test instance attributes for functions""" + data= """ +def test(): + print(test.bar) + +test.bar = 1 +test() + """ + astroid = abuilder.string_build(data, 'mod', __file__) + func = astroid.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) + + def test_type_builtin_descriptor_subclasses(self): + astroid = abuilder.string_build(dedent(""" + class classonlymethod(classmethod): + pass + class staticonlymethod(staticmethod): + pass + + class Node: + @classonlymethod + def clsmethod_subclass(cls): + pass + @classmethod + def clsmethod(cls): + pass + @staticonlymethod + def staticmethod_subclass(cls): + pass + @staticmethod + def stcmethod(cls): + pass + """)) + node = astroid.locals['Node'][0] + self.assertEqual(node.locals['clsmethod_subclass'][0].type, + 'classmethod') + self.assertEqual(node.locals['clsmethod'][0].type, + 'classmethod') + self.assertEqual(node.locals['staticmethod_subclass'][0].type, + 'staticmethod') + self.assertEqual(node.locals['stcmethod'][0].type, + 'staticmethod') + + def test_decorator_builtin_descriptors(self): + astroid = abuilder.string_build(dedent(""" + def static_decorator(platform=None, order=50): + def wrapper(f): + f.cgm_module = True + f.cgm_module_order = order + f.cgm_module_platform = platform + return staticmethod(f) + return wrapper + + def classmethod_decorator(platform=None): + def wrapper(f): + f.platform = platform + return classmethod(f) + return wrapper + + class SomeClass(object): + @static_decorator() + def static(node, cfg): + pass + @classmethod_decorator() + def classmethod(cls): + pass + @static_decorator + def not_so_static(node): + pass + @classmethod_decorator + def not_so_classmethod(node): + pass + + """)) + node = astroid.locals['SomeClass'][0] + self.assertEqual(node.locals['static'][0].type, + 'staticmethod') + self.assertEqual(node.locals['classmethod'][0].type, + 'classmethod') + self.assertEqual(node.locals['not_so_static'][0].type, + 'method') + self.assertEqual(node.locals['not_so_classmethod'][0].type, + 'method') + + +class ClassNodeTC(unittest.TestCase): + + def test_dict_interface(self): + _test_dict_interface(self, MODULE['YOUPI'], 'method') + + def test_cls_special_attributes_1(self): + cls = MODULE['YO'] + self.assertEqual(len(cls.getattr('__bases__')), 1) + self.assertEqual(len(cls.getattr('__name__')), 1) + self.assertIsInstance(cls.getattr('__name__')[0], nodes.Const) + self.assertEqual(cls.getattr('__name__')[0].value, 'YO') + self.assertEqual(len(cls.getattr('__doc__')), 1) + self.assertIsInstance(cls.getattr('__doc__')[0], nodes.Const) + self.assertEqual(cls.getattr('__doc__')[0].value, 'hehe') + self.assertEqual(len(cls.getattr('__module__')), 1) + self.assertIsInstance(cls.getattr('__module__')[0], nodes.Const) + self.assertEqual(cls.getattr('__module__')[0].value, 'data.module') + self.assertEqual(len(cls.getattr('__dict__')), 1) + if not cls.newstyle: + self.assertRaises(NotFoundError, cls.getattr, '__mro__') + for cls in (nodes.List._proxied, nodes.Const(1)._proxied): + self.assertEqual(len(cls.getattr('__bases__')), 1) + self.assertEqual(len(cls.getattr('__name__')), 1) + self.assertEqual(len(cls.getattr('__doc__')), 1, (cls, cls.getattr('__doc__'))) + self.assertEqual(cls.getattr('__doc__')[0].value, cls.doc) + self.assertEqual(len(cls.getattr('__module__')), 1) + self.assertEqual(len(cls.getattr('__dict__')), 1) + self.assertEqual(len(cls.getattr('__mro__')), 1) + + def test_cls_special_attributes_2(self): + astroid = abuilder.string_build(''' +class A: pass +class B: pass + +A.__bases__ += (B,) +''', __name__, __file__) + self.assertEqual(len(astroid['A'].getattr('__bases__')), 2) + self.assertIsInstance(astroid['A'].getattr('__bases__')[0], nodes.Tuple) + self.assertIsInstance(astroid['A'].getattr('__bases__')[1], nodes.AssAttr) + + def test_instance_special_attributes(self): + for inst in (Instance(MODULE['YO']), nodes.List(), nodes.Const(1)): + self.assertRaises(NotFoundError, inst.getattr, '__mro__') + self.assertRaises(NotFoundError, inst.getattr, '__bases__') + self.assertRaises(NotFoundError, inst.getattr, '__name__') + self.assertEqual(len(inst.getattr('__dict__')), 1) + self.assertEqual(len(inst.getattr('__doc__')), 1) + + def test_navigation(self): + klass = MODULE['YO'] + self.assertEqual(klass.statement(), klass) + l_sibling = klass.previous_sibling() + self.assertTrue(isinstance(l_sibling, nodes.Function), l_sibling) + self.assertEqual(l_sibling.name, 'global_access') + r_sibling = klass.next_sibling() + self.assertIsInstance(r_sibling, nodes.Class) + self.assertEqual(r_sibling.name, 'YOUPI') + + def test_local_attr_ancestors(self): + klass2 = MODULE['YOUPI'] + it = klass2.local_attr_ancestors('__init__') + anc_klass = next(it) + self.assertIsInstance(anc_klass, nodes.Class) + self.assertEqual(anc_klass.name, 'YO') + self.assertRaises(StopIteration, partial(next, it)) + it = klass2.local_attr_ancestors('method') + self.assertRaises(StopIteration, partial(next, it)) + + def test_instance_attr_ancestors(self): + klass2 = MODULE['YOUPI'] + it = klass2.instance_attr_ancestors('yo') + anc_klass = next(it) + self.assertIsInstance(anc_klass, nodes.Class) + self.assertEqual(anc_klass.name, 'YO') + self.assertRaises(StopIteration, partial(next, it)) + klass2 = MODULE['YOUPI'] + it = klass2.instance_attr_ancestors('member') + self.assertRaises(StopIteration, partial(next, it)) + + def test_methods(self): + klass2 = MODULE['YOUPI'] + methods = sorted([m.name for m in klass2.methods()]) + self.assertEqual(methods, ['__init__', 'class_method', + 'method', 'static_method']) + methods = [m.name for m in klass2.mymethods()] + methods.sort() + self.assertEqual(methods, ['__init__', 'class_method', + 'method', 'static_method']) + klass2 = MODULE2['Specialization'] + methods = [m.name for m in klass2.mymethods()] + methods.sort() + self.assertEqual(methods, []) + method_locals = klass2.local_attr('method') + self.assertEqual(len(method_locals), 1) + self.assertEqual(method_locals[0].name, 'method') + self.assertRaises(NotFoundError, klass2.local_attr, 'nonexistant') + methods = [m.name for m in klass2.methods()] + methods.sort() + self.assertEqual(methods, ['__init__', 'class_method', + 'method', 'static_method']) + + #def test_rhs(self): + # my_dict = MODULE['MY_DICT'] + # self.assertIsInstance(my_dict.rhs(), nodes.Dict) + # a = MODULE['YO']['a'] + # value = a.rhs() + # self.assertIsInstance(value, nodes.Const) + # self.assertEqual(value.value, 1) + + def test_ancestors(self): + klass = MODULE['YOUPI'] + ancs = [a.name for a in klass.ancestors()] + self.assertEqual(ancs, ['YO']) + klass = MODULE2['Specialization'] + ancs = [a.name for a in klass.ancestors()] + self.assertEqual(ancs, ['YOUPI', 'YO']) + + def test_type(self): + klass = MODULE['YOUPI'] + self.assertEqual(klass.type, 'class') + klass = MODULE2['Metaclass'] + self.assertEqual(klass.type, 'metaclass') + klass = MODULE2['MyException'] + self.assertEqual(klass.type, 'exception') + klass = MODULE2['MyIFace'] + self.assertEqual(klass.type, 'interface') + klass = MODULE2['MyError'] + self.assertEqual(klass.type, 'exception') + # the following class used to be detected as a metaclass + # after the fix which used instance._proxied in .ancestors(), + # when in fact it is a normal class + klass = MODULE2['NotMetaclass'] + self.assertEqual(klass.type, 'class') + + def test_interfaces(self): + for klass, interfaces in (('Concrete0', ['MyIFace']), + ('Concrete1', ['MyIFace', 'AnotherIFace']), + ('Concrete2', ['MyIFace', 'AnotherIFace']), + ('Concrete23', ['MyIFace', 'AnotherIFace'])): + klass = MODULE2[klass] + self.assertEqual([i.name for i in klass.interfaces()], + interfaces) + + def test_concat_interfaces(self): + astroid = abuilder.string_build(''' +class IMachin: pass + +class Correct2: + """docstring""" + __implements__ = (IMachin,) + +class BadArgument: + """docstring""" + __implements__ = (IMachin,) + +class InterfaceCanNowBeFound: + """docstring""" + __implements__ = BadArgument.__implements__ + Correct2.__implements__ + + ''') + self.assertEqual([i.name for i in astroid['InterfaceCanNowBeFound'].interfaces()], + ['IMachin']) + + def test_inner_classes(self): + eee = NONREGR['Ccc']['Eee'] + self.assertEqual([n.name for n in eee.ancestors()], ['Ddd', 'Aaa', 'object']) + + + def test_classmethod_attributes(self): + data = ''' +class WebAppObject(object): + def registered(cls, application): + cls.appli = application + cls.schema = application.schema + cls.config = application.config + return cls + registered = classmethod(registered) + ''' + astroid = abuilder.string_build(data, __name__, __file__) + cls = astroid['WebAppObject'] + self.assertEqual(sorted(cls.locals.keys()), + ['appli', 'config', 'registered', 'schema']) + + + def test_class_getattr(self): + data = ''' +class WebAppObject(object): + appli = application + appli += 2 + del self.appli + ''' + astroid = abuilder.string_build(data, __name__, __file__) + cls = astroid['WebAppObject'] + # test del statement not returned by getattr + self.assertEqual(len(cls.getattr('appli')), 2) + + + def test_instance_getattr(self): + data = ''' +class WebAppObject(object): + def __init__(self, application): + self.appli = application + self.appli += 2 + del self.appli + ''' + astroid = abuilder.string_build(data, __name__, __file__) + inst = Instance(astroid['WebAppObject']) + # test del statement not returned by getattr + self.assertEqual(len(inst.getattr('appli')), 2) + + + def test_instance_getattr_with_class_attr(self): + data = ''' +class Parent: + aa = 1 + cc = 1 + +class Klass(Parent): + aa = 0 + bb = 0 + + def incr(self, val): + self.cc = self.aa + if val > self.aa: + val = self.aa + if val < self.bb: + val = self.bb + self.aa += val + ''' + astroid = abuilder.string_build(data, __name__, __file__) + inst = Instance(astroid['Klass']) + self.assertEqual(len(inst.getattr('aa')), 3, inst.getattr('aa')) + self.assertEqual(len(inst.getattr('bb')), 1, inst.getattr('bb')) + self.assertEqual(len(inst.getattr('cc')), 2, inst.getattr('cc')) + + + def test_getattr_method_transform(self): + data = ''' +class Clazz(object): + + def m1(self, value): + self.value = value + m2 = m1 + +def func(arg1, arg2): + "function that will be used as a method" + return arg1.value + arg2 + +Clazz.m3 = func +inst = Clazz() +inst.m4 = func + ''' + astroid = abuilder.string_build(data, __name__, __file__) + cls = astroid['Clazz'] + # test del statement not returned by getattr + for method in ('m1', 'm2', 'm3'): + inferred = list(cls.igetattr(method)) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], UnboundMethod) + inferred = list(Instance(cls).igetattr(method)) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], BoundMethod) + inferred = list(Instance(cls).igetattr('m4')) + self.assertEqual(len(inferred), 1) + self.assertIsInstance(inferred[0], nodes.Function) + + def test_getattr_from_grandpa(self): + data = ''' +class Future: + attr = 1 + +class Present(Future): + pass + +class Past(Present): + pass +''' + astroid = abuilder.string_build(data) + past = astroid['Past'] + attr = past.getattr('attr') + self.assertEqual(len(attr), 1) + attr1 = attr[0] + self.assertIsInstance(attr1, nodes.AssName) + self.assertEqual(attr1.name, 'attr') + + def test_function_with_decorator_lineno(self): + data = ''' +@f(a=2, + b=3) +def g1(x): + print(x) + +@f(a=2, + b=3) +def g2(): + pass +''' + astroid = abuilder.string_build(data) + self.assertEqual(astroid['g1'].fromlineno, 4) + self.assertEqual(astroid['g1'].tolineno, 5) + self.assertEqual(astroid['g2'].fromlineno, 9) + self.assertEqual(astroid['g2'].tolineno, 10) + + def test_simple_metaclass(self): + if PY3K: + self.skipTest('__metaclass__ syntax is python2-specific') + astroid = abuilder.string_build(dedent(""" + class Test(object): + __metaclass__ = type + """)) + klass = astroid['Test'] + + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, scoped_nodes.Class) + self.assertEqual(metaclass.name, 'type') + + def test_metaclass_error(self): + astroid = abuilder.string_build(dedent(""" + class Test(object): + __metaclass__ = typ + """)) + klass = astroid['Test'] + self.assertFalse(klass.metaclass()) + + def test_metaclass_imported(self): + if PY3K: + self.skipTest('__metaclass__ syntax is python2-specific') + astroid = abuilder.string_build(dedent(""" + from abc import ABCMeta + class Test(object): + __metaclass__ = ABCMeta + """)) + klass = astroid['Test'] + + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, scoped_nodes.Class) + self.assertEqual(metaclass.name, 'ABCMeta') + + def test_metaclass_yes_leak(self): + astroid = abuilder.string_build(dedent(""" + # notice `ab` instead of `abc` + from ab import ABCMeta + + class Meta(object): + __metaclass__ = ABCMeta + """)) + klass = astroid['Meta'] + self.assertIsNone(klass.metaclass()) + + def test_newstyle_and_metaclass_good(self): + if PY3K: + self.skipTest('__metaclass__ syntax is python2-specific') + astroid = abuilder.string_build(dedent(""" + from abc import ABCMeta + class Test: + __metaclass__ = ABCMeta + """)) + klass = astroid['Test'] + self.assertTrue(klass.newstyle) + self.assertEqual(klass.metaclass().name, 'ABCMeta') + astroid = abuilder.string_build(dedent(""" + from abc import ABCMeta + __metaclass__ = ABCMeta + class Test: + pass + """)) + klass = astroid['Test'] + self.assertTrue(klass.newstyle) + self.assertEqual(klass.metaclass().name, 'ABCMeta') + + def test_nested_metaclass(self): + if PY3K: + self.skipTest('__metaclass__ syntax is python2-specific') + astroid = abuilder.string_build(dedent(""" + from abc import ABCMeta + class A(object): + __metaclass__ = ABCMeta + class B: pass + + __metaclass__ = ABCMeta + class C: + __metaclass__ = type + class D: pass + """)) + a = astroid['A'] + b = a.locals['B'][0] + c = astroid['C'] + d = c.locals['D'][0] + self.assertEqual(a.metaclass().name, 'ABCMeta') + self.assertFalse(b.newstyle) + self.assertIsNone(b.metaclass()) + self.assertEqual(c.metaclass().name, 'type') + self.assertEqual(d.metaclass().name, 'ABCMeta') + + def test_parent_metaclass(self): + if PY3K: + self.skipTest('__metaclass__ syntax is python2-specific') + astroid = abuilder.string_build(dedent(""" + from abc import ABCMeta + class Test: + __metaclass__ = ABCMeta + class SubTest(Test): pass + """)) + klass = astroid['SubTest'] + self.assertTrue(klass.newstyle) + metaclass = klass.metaclass() + self.assertIsInstance(metaclass, scoped_nodes.Class) + self.assertEqual(metaclass.name, 'ABCMeta') + + def test_metaclass_ancestors(self): + if PY3K: + self.skipTest('__metaclass__ syntax is python2-specific') + astroid = abuilder.string_build(dedent(""" + from abc import ABCMeta + + class FirstMeta(object): + __metaclass__ = ABCMeta + + class SecondMeta(object): + __metaclass__ = type + + class Simple(object): + pass + + class FirstImpl(FirstMeta): pass + class SecondImpl(FirstImpl): pass + class ThirdImpl(Simple, SecondMeta): + pass + """)) + classes = { + 'ABCMeta': ('FirstImpl', 'SecondImpl'), + 'type': ('ThirdImpl', ) + } + for metaclass, names in classes.items(): + for name in names: + impl = astroid[name] + meta = impl.metaclass() + self.assertIsInstance(meta, nodes.Class) + self.assertEqual(meta.name, metaclass) + + def test_metaclass_type(self): + klass = extract_node(""" + def with_metaclass(meta, base=object): + return meta("NewBase", (base, ), {}) + + class ClassWithMeta(with_metaclass(type)): #@ + pass + """) + self.assertEqual( + ['NewBase', 'object'], + [base.name for base in klass.ancestors()]) + + def test_nonregr_infer_callresult(self): + astroid = abuilder.string_build(dedent(""" + class Delegate(object): + def __get__(self, obj, cls): + return getattr(obj._subject, self.attribute) + + class CompositeBuilder(object): + __call__ = Delegate() + + builder = CompositeBuilder(result, composite) + tgts = builder() + """)) + instance = astroid['tgts'] + # used to raise "'_Yes' object is not iterable", see + # https://bitbucket.org/logilab/astroid/issue/17 + self.assertEqual(list(instance.infer()), [YES]) + + def test_slots(self): + astroid = abuilder.string_build(dedent(""" + from collections import deque + from textwrap import dedent + + class First(object): + __slots__ = ("a", "b", 1) + class Second(object): + __slots__ = "a" + class Third(object): + __slots__ = deque(["a", "b", "c"]) + class Fourth(object): + __slots__ = {"a": "a", "b": "b"} + class Fifth(object): + __slots__ = list + class Sixth(object): + __slots__ = "" + class Seventh(object): + __slots__ = dedent.__name__ + class Eight(object): + __slots__ = ("parens") + """)) + first = astroid['First'] + first_slots = first.slots() + self.assertEqual(len(first_slots), 2) + self.assertIsInstance(first_slots[0], nodes.Const) + self.assertIsInstance(first_slots[1], nodes.Const) + self.assertEqual(first_slots[0].value, "a") + self.assertEqual(first_slots[1].value, "b") + + second_slots = astroid['Second'].slots() + self.assertEqual(len(second_slots), 1) + self.assertIsInstance(second_slots[0], nodes.Const) + self.assertEqual(second_slots[0].value, "a") + + third_slots = astroid['Third'].slots() + self.assertEqual(third_slots, []) + + fourth_slots = astroid['Fourth'].slots() + self.assertEqual(len(fourth_slots), 2) + self.assertIsInstance(fourth_slots[0], nodes.Const) + self.assertIsInstance(fourth_slots[1], nodes.Const) + self.assertEqual(fourth_slots[0].value, "a") + self.assertEqual(fourth_slots[1].value, "b") + + fifth_slots = astroid['Fifth'].slots() + self.assertEqual(fifth_slots, []) + + sixth_slots = astroid['Sixth'].slots() + self.assertEqual(sixth_slots, []) + + seventh_slots = astroid['Seventh'].slots() + self.assertEqual(len(seventh_slots), 0) + + eight_slots = astroid['Eight'].slots() + self.assertEqual(len(eight_slots), 1) + self.assertIsInstance(eight_slots[0], nodes.Const) + self.assertEqual(eight_slots[0].value, "parens") + + +__all__ = ('ModuleNodeTC', 'ImportNodeTC', 'FunctionNodeTC', 'ClassNodeTC') + +if __name__ == '__main__': + unittest.main() diff --git a/astroid/tests/unittest_utils.py b/astroid/tests/unittest_utils.py new file mode 100644 index 0000000..999f7ed --- /dev/null +++ b/astroid/tests/unittest_utils.py @@ -0,0 +1,102 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +import unittest + +from astroid import builder, nodes +from astroid.node_classes import are_exclusive + +builder = builder.AstroidBuilder() + +class AreExclusiveTC(unittest.TestCase): + def test_not_exclusive(self): + astroid = builder.string_build(""" +x = 10 +for x in range(5): + print (x) + +if x > 0: + print ('#' * x) + """, __name__, __file__) + xass1 = astroid.locals['x'][0] + assert xass1.lineno == 2 + xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'x'] + assert len(xnames) == 3 + assert xnames[1].lineno == 6 + self.assertEqual(are_exclusive(xass1, xnames[1]), False) + self.assertEqual(are_exclusive(xass1, xnames[2]), False) + + def test_if(self): + astroid = builder.string_build(''' + +if 1: + a = 1 + a = 2 +elif 2: + a = 12 + a = 13 +else: + a = 3 + a = 4 + ''') + a1 = astroid.locals['a'][0] + a2 = astroid.locals['a'][1] + a3 = astroid.locals['a'][2] + a4 = astroid.locals['a'][3] + a5 = astroid.locals['a'][4] + a6 = astroid.locals['a'][5] + self.assertEqual(are_exclusive(a1, a2), False) + self.assertEqual(are_exclusive(a1, a3), True) + self.assertEqual(are_exclusive(a1, a5), True) + self.assertEqual(are_exclusive(a3, a5), True) + self.assertEqual(are_exclusive(a3, a4), False) + self.assertEqual(are_exclusive(a5, a6), False) + + def test_try_except(self): + astroid = builder.string_build(''' +try: + def exclusive_func2(): + "docstring" +except TypeError: + def exclusive_func2(): + "docstring" +except: + def exclusive_func2(): + "docstring" +else: + def exclusive_func2(): + "this one redefine the one defined line 42" + + ''') + f1 = astroid.locals['exclusive_func2'][0] + f2 = astroid.locals['exclusive_func2'][1] + f3 = astroid.locals['exclusive_func2'][2] + f4 = astroid.locals['exclusive_func2'][3] + self.assertEqual(are_exclusive(f1, f2), True) + self.assertEqual(are_exclusive(f1, f3), True) + self.assertEqual(are_exclusive(f1, f4), False) + self.assertEqual(are_exclusive(f2, f4), True) + self.assertEqual(are_exclusive(f3, f4), True) + self.assertEqual(are_exclusive(f3, f2), True) + + self.assertEqual(are_exclusive(f2, f1), True) + self.assertEqual(are_exclusive(f4, f1), False) + self.assertEqual(are_exclusive(f4, f2), True) + +if __name__ == '__main__': + unittest.main() + diff --git a/astroid/utils.py b/astroid/utils.py new file mode 100644 index 0000000..ae72a92 --- /dev/null +++ b/astroid/utils.py @@ -0,0 +1,239 @@ +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr +# +# This file is part of astroid. +# +# astroid 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. +# +# astroid 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 astroid. If not, see . +"""this module contains some utilities to navigate in the tree or to +extract information from it +""" +from __future__ import print_function + +__docformat__ = "restructuredtext en" + +from astroid.exceptions import AstroidBuildingException +from astroid.builder import parse + + +class ASTWalker(object): + """a walker visiting a tree in preorder, calling on the handler: + + * visit_ on entering a node, where class name is the class of + the node in lower case + + * leave_ on leaving a node, where class name is the class of + the node in lower case + """ + + def __init__(self, handler): + self.handler = handler + self._cache = {} + + def walk(self, node, _done=None): + """walk on the tree from , getting callbacks from handler""" + if _done is None: + _done = set() + if node in _done: + raise AssertionError((id(node), node, node.parent)) + _done.add(node) + 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 + + def get_callbacks(self, node): + """get callbacks from handler for the visited node""" + klass = node.__class__ + methods = self._cache.get(klass) + if methods is None: + handler = self.handler + kid = klass.__name__.lower() + e_method = getattr(handler, 'visit_%s' % kid, + getattr(handler, 'visit_default', None)) + l_method = getattr(handler, 'leave_%s' % kid, + getattr(handler, 'leave_default', None)) + self._cache[klass] = (e_method, l_method) + else: + e_method, l_method = methods + return e_method, l_method + + def visit(self, node): + """walk on the tree from , getting callbacks from handler""" + method = self.get_callbacks(node)[0] + if method is not None: + method(node) + + def leave(self, node): + """walk on the tree from , getting callbacks from handler""" + method = self.get_callbacks(node)[1] + if method is not None: + method(node) + + +class LocalsVisitor(ASTWalker): + """visit a project by traversing the locals dictionary""" + def __init__(self): + ASTWalker.__init__(self, self) + self._visited = {} + + def visit(self, node): + """launch the visit starting from the given node""" + if node in self._visited: + return + self._visited[node] = 1 # FIXME: use set ? + methods = self.get_callbacks(node) + if methods[0] is not None: + methods[0](node) + if 'locals' in node.__dict__: # skip Instance and other proxy + for local_node in node.values(): + self.visit(local_node) + if methods[1] is not None: + return methods[1](node) + + +def _check_children(node): + """a helper function to check children - parent relations""" + for child in node.get_children(): + ok = False + if child is None: + print("Hm, child of %s is None" % node) + continue + if not hasattr(child, 'parent'): + print(" ERROR: %s has child %s %x with no parent" % ( + node, child, id(child))) + elif not child.parent: + print(" ERROR: %s has child %s %x with parent %r" % ( + node, child, id(child), child.parent)) + elif child.parent is not node: + print(" ERROR: %s %x has child %s %x with wrong parent %s" % ( + node, id(node), child, id(child), child.parent)) + else: + ok = True + if not ok: + print("lines;", node.lineno, child.lineno) + print("of module", node.root(), node.root().name) + raise AstroidBuildingException + _check_children(child) + + +class TreeTester(object): + '''A helper class to see _ast tree and compare with astroid tree + + indent: string for tree indent representation + lineno: bool to tell if we should print the line numbers + + >>> tester = TreeTester('print') + >>> print tester.native_tree_repr() + + + . body = [ + . + . . nl = True + . ] + >>> print tester.astroid_tree_repr() + Module() + body = [ + Print() + dest = + values = [ + ] + ] + ''' + + indent = '. ' + lineno = False + + def __init__(self, sourcecode): + self._string = '' + self.sourcecode = sourcecode + self._ast_node = None + self.build_ast() + + def build_ast(self): + """build the _ast tree from the source code""" + self._ast_node = parse(self.sourcecode) + + def native_tree_repr(self, node=None, indent=''): + """get a nice representation of the _ast tree""" + self._string = '' + if node is None: + node = self._ast_node + self._native_repr_tree(node, indent) + return self._string + + + def _native_repr_tree(self, node, indent, _done=None): + """recursive method for the native tree representation""" + from _ast import Load as _Load, Store as _Store, Del as _Del + from _ast import AST as Node + if _done is None: + _done = set() + if node in _done: + self._string += '\nloop in tree: %r (%s)' % ( + node, getattr(node, 'lineno', None)) + return + _done.add(node) + self._string += '\n' + indent + '<%s>' % node.__class__.__name__ + indent += self.indent + if not hasattr(node, '__dict__'): + self._string += '\n' + self.indent + " ** node has no __dict__ " + str(node) + return + node_dict = node.__dict__ + if hasattr(node, '_attributes'): + for a in node._attributes: + attr = node_dict[a] + if attr is None: + continue + if a in ("lineno", "col_offset") and not self.lineno: + continue + self._string += '\n' + indent + a + " = " + repr(attr) + for field in node._fields or (): + attr = node_dict[field] + if attr is None: + continue + if isinstance(attr, list): + if not attr: + continue + self._string += '\n' + indent + field + ' = [' + for elt in attr: + self._native_repr_tree(elt, indent, _done) + self._string += '\n' + indent + ']' + continue + if isinstance(attr, (_Load, _Store, _Del)): + continue + if isinstance(attr, Node): + self._string += '\n' + indent + field + " = " + self._native_repr_tree(attr, indent, _done) + else: + self._string += '\n' + indent + field + " = " + repr(attr) + + + def build_astroid_tree(self): + """build astroid tree from the _ast tree + """ + from astroid.builder import AstroidBuilder + tree = AstroidBuilder().string_build(self.sourcecode) + return tree + + def astroid_tree_repr(self, ids=False): + """build the astroid tree and return a nice tree representation""" + mod = self.build_astroid_tree() + return mod.repr_tree(ids) + + +__all__ = ('LocalsVisitor', 'ASTWalker',) + diff --git a/bases.py b/bases.py deleted file mode 100644 index f1f4cc4..0000000 --- a/bases.py +++ /dev/null @@ -1,665 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""This module contains base classes and functions for the nodes and some -inference utils. -""" - -__docformat__ = "restructuredtext en" - -import sys -from contextlib import contextmanager - -from logilab.common.decorators import cachedproperty - -from astroid.exceptions import (InferenceError, AstroidError, NotFoundError, - UnresolvableName, UseInferenceDefault) - - -if sys.version_info >= (3, 0): - BUILTINS = 'builtins' -else: - BUILTINS = '__builtin__' - - -class Proxy(object): - """a simple proxy object""" - - _proxied = None # proxied object may be set by class or by instance - - def __init__(self, proxied=None): - if proxied is not None: - self._proxied = proxied - - def __getattr__(self, name): - if name == '_proxied': - return getattr(self.__class__, '_proxied') - if name in self.__dict__: - return self.__dict__[name] - return getattr(self._proxied, name) - - def infer(self, context=None): - yield self - - -# Inference ################################################################## - -MISSING = object() - - -class InferenceContext(object): - __slots__ = ('path', 'callcontext', 'boundnode', 'infered') - - def __init__(self, - path=None, callcontext=None, boundnode=None, infered=None): - if path is None: - self.path = frozenset() - else: - self.path = path - self.callcontext = callcontext - self.boundnode = boundnode - if infered is None: - self.infered = {} - else: - self.infered = infered - - def push(self, key): - # This returns a NEW context with the same attributes, but a new key - # added to `path`. The intention is that it's only passed to callees - # and then destroyed; otherwise scope() may not work correctly. - # The cache will be shared, since it's the same exact dict. - if key in self.path: - # End the containing generator - raise StopIteration - - return InferenceContext( - self.path.union([key]), - self.callcontext, - self.boundnode, - self.infered, - ) - - @contextmanager - def scope(self, callcontext=MISSING, boundnode=MISSING): - try: - orig = self.callcontext, self.boundnode - if callcontext is not MISSING: - self.callcontext = callcontext - if boundnode is not MISSING: - self.boundnode = boundnode - yield - finally: - self.callcontext, self.boundnode = orig - - def cache_generator(self, key, generator): - results = [] - for result in generator: - results.append(result) - yield result - - self.infered[key] = tuple(results) - return - - -def _infer_stmts(stmts, context, frame=None, lookupname=None): - """return an iterator on statements inferred by each statement in - """ - stmt = None - infered = False - if context is None: - context = InferenceContext() - for stmt in stmts: - if stmt is YES: - yield stmt - infered = True - continue - - kw = {} - infered_name = stmt._infer_name(frame, lookupname) - if infered_name is not None: - # only returns not None if .infer() accepts a lookupname kwarg - kw['lookupname'] = infered_name - - try: - for infered in stmt.infer(context, **kw): - yield infered - infered = True - except UnresolvableName: - continue - except InferenceError: - yield YES - infered = True - if not infered: - raise InferenceError(str(stmt)) - - -# special inference objects (e.g. may be returned as nodes by .infer()) ####### - -class _Yes(object): - """a yes object""" - def __repr__(self): - return 'YES' - def __getattribute__(self, name): - if name == 'next': - raise AttributeError('next method should not be called') - if name.startswith('__') and name.endswith('__'): - # to avoid inspection pb - return super(_Yes, self).__getattribute__(name) - return self - def __call__(self, *args, **kwargs): - return self - - -YES = _Yes() - - -class Instance(Proxy): - """a special node representing a class instance""" - def getattr(self, name, context=None, lookupclass=True): - try: - values = self._proxied.instance_attr(name, context) - except NotFoundError: - if name == '__class__': - return [self._proxied] - if lookupclass: - # class attributes not available through the instance - # unless they are explicitly defined - if name in ('__name__', '__bases__', '__mro__', '__subclasses__'): - return self._proxied.local_attr(name) - return self._proxied.getattr(name, context) - raise NotFoundError(name) - # since we've no context information, return matching class members as - # well - if lookupclass: - try: - return values + self._proxied.getattr(name, context) - except NotFoundError: - pass - return values - - def igetattr(self, name, context=None): - """inferred getattr""" - if not context: - context = InferenceContext() - try: - # avoid recursively inferring the same attr on the same class - new_context = context.push((self._proxied, name)) - # XXX frame should be self._proxied, or not ? - get_attr = self.getattr(name, new_context, lookupclass=False) - return _infer_stmts( - self._wrap_attr(get_attr, new_context), - new_context, - frame=self, - ) - except NotFoundError: - try: - # fallback to class'igetattr since it has some logic to handle - # descriptors - return self._wrap_attr(self._proxied.igetattr(name, context), - context) - except NotFoundError: - raise InferenceError(name) - - def _wrap_attr(self, attrs, context=None): - """wrap bound methods of attrs in a InstanceMethod proxies""" - for attr in attrs: - if isinstance(attr, UnboundMethod): - if BUILTINS + '.property' in attr.decoratornames(): - for infered in attr.infer_call_result(self, context): - yield infered - else: - yield BoundMethod(attr, self) - else: - yield attr - - def infer_call_result(self, caller, context=None): - """infer what a class instance is returning when called""" - infered = False - for node in self._proxied.igetattr('__call__', context): - if node is YES: - continue - for res in node.infer_call_result(caller, context): - infered = True - yield res - if not infered: - raise InferenceError() - - def __repr__(self): - return '' % (self._proxied.root().name, - self._proxied.name, - id(self)) - def __str__(self): - return 'Instance of %s.%s' % (self._proxied.root().name, - self._proxied.name) - - def callable(self): - try: - self._proxied.getattr('__call__') - return True - except NotFoundError: - return False - - def pytype(self): - return self._proxied.qname() - - def display_type(self): - return 'Instance of' - - -class UnboundMethod(Proxy): - """a special node representing a method not bound to an instance""" - def __repr__(self): - frame = self._proxied.parent.frame() - return '<%s %s of %s at 0x%s' % (self.__class__.__name__, - self._proxied.name, - frame.qname(), id(self)) - - def is_bound(self): - return False - - def getattr(self, name, context=None): - if name == 'im_func': - return [self._proxied] - return super(UnboundMethod, self).getattr(name, context) - - def igetattr(self, name, context=None): - if name == 'im_func': - 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() == '%s.object' % BUILTINS): - infer = caller.args[0].infer() if caller.args else [] - return ((x is YES and x or Instance(x)) for x in infer) - return self._proxied.infer_call_result(caller, context) - - -class BoundMethod(UnboundMethod): - """a special node representing a method bound to an instance""" - def __init__(self, proxy, bound): - UnboundMethod.__init__(self, proxy) - self.bound = bound - - def is_bound(self): - return True - - def infer_call_result(self, caller, context): - with context.scope(boundnode=self.bound): - for infered in self._proxied.infer_call_result(caller, context): - yield infered - - -class Generator(Instance): - """a special node representing a generator. - - Proxied class is set once for all in raw_building. - """ - def callable(self): - return False - - def pytype(self): - return '%s.generator' % BUILTINS - - def display_type(self): - return 'Generator' - - def __repr__(self): - return '' % (self._proxied.name, self.lineno, id(self)) - - def __str__(self): - return 'Generator(%s)' % (self._proxied.name) - - -# decorators ################################################################## - -def path_wrapper(func): - """return the given infer function wrapped to handle the path""" - def wrapped(node, context=None, _func=func, **kwargs): - """wrapper function handling context""" - if context is None: - context = InferenceContext() - context = context.push((node, kwargs.get('lookupname'))) - - yielded = set() - for res in _func(node, context, **kwargs): - # unproxy only true instance, not const, tuple, dict... - if res.__class__ is Instance: - ares = res._proxied - else: - ares = res - if not ares in yielded: - yield res - yielded.add(ares) - return wrapped - -def yes_if_nothing_infered(func): - def wrapper(*args, **kwargs): - infered = False - for node in func(*args, **kwargs): - infered = True - yield node - if not infered: - yield YES - return wrapper - -def raise_if_nothing_infered(func): - def wrapper(*args, **kwargs): - infered = False - for node in func(*args, **kwargs): - infered = True - yield node - if not infered: - raise InferenceError() - return wrapper - - -# Node ###################################################################### - -class NodeNG(object): - """Base Class for all Astroid node classes. - - It represents a node of the new abstract syntax tree. - """ - is_statement = False - optional_assign = False # True for For (and for Comprehension if py <3.0) - is_function = False # True for Function nodes - # attributes below are set by the builder module or by raw factories - lineno = None - fromlineno = None - tolineno = None - col_offset = None - # parent node in the tree - parent = None - # attributes containing child node(s) redefined in most concrete classes: - _astroid_fields = () - # instance specific inference function infer(node, context) - _explicit_inference = None - - def infer(self, context=None, **kwargs): - """main interface to the interface system, return a generator on infered - values. - - If the instance has some explicit inference function set, it will be - called instead of the default interface. - """ - if self._explicit_inference is not None: - # explicit_inference is not bound, give it self explicitly - try: - return self._explicit_inference(self, context, **kwargs) - except UseInferenceDefault: - pass - - if not context: - return self._infer(context, **kwargs) - - key = (self, kwargs.get('lookupname'), context.callcontext, context.boundnode) - if key in context.infered: - return iter(context.infered[key]) - - return context.cache_generator(key, self._infer(context, **kwargs)) - - def _repr_name(self): - """return self.name or self.attrname or '' for nice representation""" - return getattr(self, 'name', getattr(self, 'attrname', '')) - - def __str__(self): - return '%s(%s)' % (self.__class__.__name__, self._repr_name()) - - def __repr__(self): - return '<%s(%s) l.%s [%s] at 0x%x>' % (self.__class__.__name__, - self._repr_name(), - self.fromlineno, - self.root().name, - id(self)) - - - def accept(self, visitor): - func = getattr(visitor, "visit_" + self.__class__.__name__.lower()) - return func(self) - - def get_children(self): - for field in self._astroid_fields: - attr = getattr(self, field) - if attr is None: - continue - if isinstance(attr, (list, tuple)): - for elt in attr: - yield elt - else: - yield attr - - def last_child(self): - """an optimized version of list(get_children())[-1]""" - for field in self._astroid_fields[::-1]: - attr = getattr(self, field) - if not attr: # None or empty listy / tuple - continue - if attr.__class__ in (list, tuple): - return attr[-1] - else: - return attr - return None - - def parent_of(self, node): - """return true if i'm a parent of the given node""" - parent = node.parent - while parent is not None: - if self is parent: - return True - parent = parent.parent - return False - - def statement(self): - """return the first parent node marked as statement node""" - if self.is_statement: - return self - return self.parent.statement() - - def frame(self): - """return the first parent frame node (i.e. Module, Function or Class) - """ - return self.parent.frame() - - def scope(self): - """return the first node defining a new scope (i.e. Module, Function, - Class, Lambda but also GenExpr) - """ - return self.parent.scope() - - def root(self): - """return the root node of the tree, (i.e. a Module)""" - if self.parent: - return self.parent.root() - return self - - def child_sequence(self, child): - """search for the right sequence where the child lies in""" - for field in self._astroid_fields: - node_or_sequence = getattr(self, field) - if node_or_sequence is child: - return [node_or_sequence] - # /!\ compiler.ast Nodes have an __iter__ walking over child nodes - if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: - return node_or_sequence - else: - msg = 'Could not find %s in %s\'s children' - raise AstroidError(msg % (repr(child), repr(self))) - - def locate_child(self, child): - """return a 2-uple (child attribute name, sequence or node)""" - for field in self._astroid_fields: - node_or_sequence = getattr(self, field) - # /!\ compiler.ast Nodes have an __iter__ walking over child nodes - if child is node_or_sequence: - return field, child - if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence: - return field, node_or_sequence - msg = 'Could not find %s in %s\'s children' - raise AstroidError(msg % (repr(child), repr(self))) - # FIXME : should we merge child_sequence and locate_child ? locate_child - # is only used in are_exclusive, child_sequence one time in pylint. - - def next_sibling(self): - """return the next sibling statement""" - return self.parent.next_sibling() - - def previous_sibling(self): - """return the previous sibling statement""" - return self.parent.previous_sibling() - - def nearest(self, nodes): - """return the node which is the nearest before this one in the - given list of nodes - """ - myroot = self.root() - mylineno = self.fromlineno - nearest = None, 0 - for node in nodes: - assert node.root() is myroot, \ - 'nodes %s and %s are not from the same module' % (self, node) - lineno = node.fromlineno - if node.fromlineno > mylineno: - break - if lineno > nearest[1]: - nearest = node, lineno - # FIXME: raise an exception if nearest is None ? - return nearest[0] - - # these are lazy because they're relatively expensive to compute for every - # single node, and they rarely get looked at - - @cachedproperty - def fromlineno(self): - if self.lineno is None: - return self._fixed_source_line() - else: - return self.lineno - - @cachedproperty - def tolineno(self): - if not self._astroid_fields: - # can't have children - lastchild = None - else: - lastchild = self.last_child() - if lastchild is None: - return self.fromlineno - else: - return lastchild.tolineno - - # TODO / FIXME: - assert self.fromlineno is not None, self - assert self.tolineno is not None, self - - def _fixed_source_line(self): - """return the line number where the given node appears - - we need this method since not all nodes have the lineno attribute - correctly set... - """ - line = self.lineno - _node = self - try: - while line is None: - _node = next(_node.get_children()) - line = _node.lineno - except StopIteration: - _node = self.parent - while _node and line is None: - line = _node.lineno - _node = _node.parent - return line - - def block_range(self, lineno): - """handle block line numbers range for non block opening statements - """ - return lineno, self.tolineno - - def set_local(self, name, stmt): - """delegate to a scoped parent handling a locals dictionary""" - self.parent.set_local(name, stmt) - - def nodes_of_class(self, klass, skip_klass=None): - """return an iterator on nodes which are instance of the given class(es) - - klass may be a class object or a tuple of class objects - """ - if isinstance(self, klass): - yield self - for child_node in self.get_children(): - if skip_klass is not None and isinstance(child_node, skip_klass): - continue - for matching in child_node.nodes_of_class(klass, skip_klass): - yield matching - - def _infer_name(self, frame, name): - # overridden for From, Import, Global, TryExcept and Arguments - return None - - def _infer(self, context=None): - """we don't know how to resolve a statement by default""" - # this method is overridden by most concrete classes - raise InferenceError(self.__class__.__name__) - - def infered(self): - '''return list of infered values for a more simple inference usage''' - return list(self.infer()) - - def instanciate_class(self): - """instanciate a node if it is a Class node, else return self""" - return self - - def has_base(self, node): - return False - - def callable(self): - return False - - def eq(self, value): - return False - - def as_string(self): - from astroid.as_string import to_code - return to_code(self) - - def repr_tree(self, ids=False): - from astroid.as_string import dump - return dump(self) - - -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] diff --git a/brain/py2gi.py b/brain/py2gi.py deleted file mode 100644 index 20356c1..0000000 --- a/brain/py2gi.py +++ /dev/null @@ -1,169 +0,0 @@ -"""Astroid hooks for the Python 2 GObject introspection bindings. - -Helps with understanding everything imported from 'gi.repository' -""" - -import inspect -import itertools -import sys -import re - -from astroid import MANAGER, AstroidBuildingException -from astroid.builder import AstroidBuilder - - -_inspected_modules = {} - -_identifier_re = r'^[A-Za-z_]\w*$' - -def _gi_build_stub(parent): - """ - Inspect the passed module recursively and build stubs for functions, - classes, etc. - """ - classes = {} - functions = {} - constants = {} - methods = {} - for name in dir(parent): - if name.startswith("__"): - continue - - # Check if this is a valid name in python - if not re.match(_identifier_re, name): - continue - - try: - obj = getattr(parent, name) - except: - continue - - if inspect.isclass(obj): - classes[name] = obj - elif (inspect.isfunction(obj) or - inspect.isbuiltin(obj)): - functions[name] = obj - elif (inspect.ismethod(obj) or - inspect.ismethoddescriptor(obj)): - methods[name] = obj - elif type(obj) in [int, str]: - constants[name] = obj - elif (str(obj).startswith(" (3, 0) -PY33 = sys.version_info >= (3, 3) - -# general function - -def infer_func_form(node, base_type, context=None, enum=False): - """Specific inference function for namedtuple or Python 3 enum. """ - def infer_first(node): - try: - value = next(node.infer(context=context)) - if value is YES: - raise UseInferenceDefault() - else: - return value - except StopIteration: - raise InferenceError() - - # node is a CallFunc node, class name as first argument and generated class - # attributes as second argument - if len(node.args) != 2: - # something weird here, go back to class implementation - raise UseInferenceDefault() - # namedtuple or enums list of attributes can be a list of strings or a - # whitespace-separate string - try: - name = infer_first(node.args[0]).value - names = infer_first(node.args[1]) - try: - attributes = names.value.replace(',', ' ').split() - except AttributeError: - if not enum: - attributes = [infer_first(const).value for const in names.elts] - else: - # Enums supports either iterator of (name, value) pairs - # or mappings. - # TODO: support only list, tuples and mappings. - if hasattr(names, 'items') and isinstance(names.items, list): - attributes = [infer_first(const[0]).value - for const in names.items - if isinstance(const[0], nodes.Const)] - elif hasattr(names, 'elts'): - # Enums can support either ["a", "b", "c"] - # or [("a", 1), ("b", 2), ...], but they can't - # be mixed. - if all(isinstance(const, nodes.Tuple) - for const in names.elts): - attributes = [infer_first(const.elts[0]).value - for const in names.elts - if isinstance(const, nodes.Tuple)] - else: - attributes = [infer_first(const).value - for const in names.elts] - else: - raise AttributeError - if not attributes: - raise AttributeError - except (AttributeError, exceptions.InferenceError) as exc: - raise UseInferenceDefault() - # we want to return a Class node instance with proper attributes set - class_node = nodes.Class(name, 'docstring') - class_node.parent = node.parent - # set base class=tuple - class_node.bases.append(base_type) - # XXX add __init__(*attributes) method - for attr in attributes: - fake_node = nodes.EmptyNode() - fake_node.parent = class_node - class_node.instance_attrs[attr] = [fake_node] - return class_node, name, attributes - - -# module specific transformation functions ##################################### - -def transform(module): - try: - tr = MODULE_TRANSFORMS[module.name] - except KeyError: - pass - else: - tr(module) -MANAGER.register_transform(nodes.Module, transform) - -# module specific transformation functions ##################################### - -def hashlib_transform(module): - template = ''' - -class %(name)s(object): - def __init__(self, value=''): pass - def digest(self): - return u'' - def copy(self): - return self - def update(self, value): pass - def hexdigest(self): - return u'' - @property - def name(self): - return %(name)r -''' - - algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') - classes = "".join(template % {'name': hashfunc} - for hashfunc in algorithms) - - fake = AstroidBuilder(MANAGER).string_build(classes) - - for hashfunc in algorithms: - module.locals[hashfunc] = fake.locals[hashfunc] - -def collections_transform(module): - fake = AstroidBuilder(MANAGER).string_build(''' - -class defaultdict(dict): - default_factory = None - def __missing__(self, key): pass - -class deque(object): - maxlen = 0 - def __init__(self, iterable=None, maxlen=None): pass - def append(self, x): pass - def appendleft(self, x): pass - def clear(self): pass - def count(self, x): return 0 - def extend(self, iterable): pass - def extendleft(self, iterable): pass - def pop(self): pass - def popleft(self): pass - def remove(self, value): pass - def reverse(self): pass - def rotate(self, n): pass - def __iter__(self): return self - -''') - - for klass in ('deque', 'defaultdict'): - module.locals[klass] = fake.locals[klass] - -def pkg_resources_transform(module): - fake = AstroidBuilder(MANAGER).string_build(''' - -def resource_exists(package_or_requirement, resource_name): - pass - -def resource_isdir(package_or_requirement, resource_name): - pass - -def resource_filename(package_or_requirement, resource_name): - pass - -def resource_stream(package_or_requirement, resource_name): - pass - -def resource_string(package_or_requirement, resource_name): - pass - -def resource_listdir(package_or_requirement, resource_name): - pass - -def extraction_error(): - pass - -def get_cache_path(archive_name, names=()): - pass - -def postprocess(tempname, filename): - pass - -def set_extraction_path(path): - pass - -def cleanup_resources(force=False): - pass - -''') - - for func_name, func in fake.locals.items(): - module.locals[func_name] = func - - -def subprocess_transform(module): - if PY3K: - communicate = (bytes('string', 'ascii'), bytes('string', 'ascii')) - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0, restore_signals=True, - start_new_session=False, pass_fds=()): - pass - """ - else: - communicate = ('string', 'string') - init = """ - def __init__(self, args, bufsize=0, executable=None, - stdin=None, stdout=None, stderr=None, - preexec_fn=None, close_fds=False, shell=False, - cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): - pass - """ - if PY33: - wait_signature = 'def wait(self, timeout=None)' - else: - wait_signature = 'def wait(self)' - fake = AstroidBuilder(MANAGER).string_build(''' - -class Popen(object): - returncode = pid = 0 - stdin = stdout = stderr = file() - - %(init)s - - def communicate(self, input=None): - return %(communicate)r - %(wait_signature)s: - return self.returncode - def poll(self): - return self.returncode - def send_signal(self, signal): - pass - def terminate(self): - pass - def kill(self): - pass - ''' % {'init': init, - 'communicate': communicate, - 'wait_signature': wait_signature}) - - for func_name, func in fake.locals.items(): - module.locals[func_name] = func - - - -MODULE_TRANSFORMS['hashlib'] = hashlib_transform -MODULE_TRANSFORMS['collections'] = collections_transform -MODULE_TRANSFORMS['pkg_resources'] = pkg_resources_transform -MODULE_TRANSFORMS['subprocess'] = subprocess_transform - -# namedtuple support ########################################################### - -def looks_like_namedtuple(node): - func = node.func - if type(func) is nodes.Getattr: - return func.attrname == 'namedtuple' - if type(func) is nodes.Name: - return func.name == 'namedtuple' - return False - -def infer_named_tuple(node, context=None): - """Specific inference function for namedtuple CallFunc node""" - class_node, name, attributes = infer_func_form(node, nodes.Tuple._proxied, - context=context) - fake = AstroidBuilder(MANAGER).string_build(''' -class %(name)s(tuple): - _fields = %(fields)r - def _asdict(self): - return self.__dict__ - @classmethod - def _make(cls, iterable, new=tuple.__new__, len=len): - return new(cls, iterable) - def _replace(_self, **kwds): - result = _self._make(map(kwds.pop, %(fields)r, _self)) - if kwds: - raise ValueError('Got unexpected field names: %%r' %% list(kwds)) - return result - ''' % {'name': name, 'fields': attributes}) - class_node.locals['_asdict'] = fake.body[0].locals['_asdict'] - class_node.locals['_make'] = fake.body[0].locals['_make'] - class_node.locals['_replace'] = fake.body[0].locals['_replace'] - class_node.locals['_fields'] = fake.body[0].locals['_fields'] - # we use UseInferenceDefault, we can't be a generator so return an iterator - return iter([class_node]) - -def infer_enum(node, context=None): - """ Specific inference function for enum CallFunc node. """ - enum_meta = nodes.Class("EnumMeta", 'docstring') - class_node = infer_func_form(node, enum_meta, - context=context, enum=True)[0] - return iter([class_node.instanciate_class()]) - -def infer_enum_class(node, context=None): - """ Specific inference for enums. """ - names = set(('Enum', 'IntEnum', 'enum.Enum', 'enum.IntEnum')) - for basename in node.basenames: - # TODO: doesn't handle subclasses yet. This implementation - # is a hack to support enums. - if basename not in names: - continue - if node.root().name == 'enum': - # Skip if the class is directly from enum module. - break - for local, values in node.locals.items(): - if any(not isinstance(value, nodes.AssName) - for value in values): - continue - - stmt = values[0].statement() - if isinstance(stmt.targets[0], nodes.Tuple): - targets = stmt.targets[0].itered() - else: - targets = stmt.targets - - new_targets = [] - for target in targets: - # Replace all the assignments with our mocked class. - classdef = dedent(''' - class %(name)s(object): - @property - def value(self): - # Not the best return. - return None - @property - def name(self): - return %(name)r - ''' % {'name': target.name}) - fake = AstroidBuilder(MANAGER).string_build(classdef)[target.name] - fake.parent = target.parent - for method in node.mymethods(): - fake.locals[method.name] = [method] - new_targets.append(fake.instanciate_class()) - node.locals[local] = new_targets - break - return node - -MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_named_tuple), - looks_like_namedtuple) -MANAGER.register_transform(nodes.CallFunc, inference_tip(infer_enum), - AsStringRegexpPredicate('Enum', 'func')) -MANAGER.register_transform(nodes.Class, infer_enum_class) diff --git a/builder.py b/builder.py deleted file mode 100644 index 4e0e225..0000000 --- a/builder.py +++ /dev/null @@ -1,237 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""The AstroidBuilder makes astroid from living object and / or from _ast - -The builder is not thread safe and can't be used to parse different sources -at the same time. -""" -from __future__ import with_statement - -__docformat__ = "restructuredtext en" - -import sys -from os.path import splitext, basename, exists, abspath - -from astroid.exceptions import AstroidBuildingException, InferenceError -from astroid.raw_building import InspectBuilder -from astroid.rebuilder import TreeRebuilder -from astroid.manager import AstroidManager -from astroid.bases import YES, Instance -from astroid.modutils import modpath_from_file - -from _ast import PyCF_ONLY_AST -def parse(string): - return compile(string, "", 'exec', PyCF_ONLY_AST) - -if sys.version_info >= (3, 0): - from tokenize import detect_encoding - - def open_source_file(filename): - with open(filename, 'rb') as byte_stream: - encoding = detect_encoding(byte_stream.readline)[0] - stream = open(filename, 'rU', encoding=encoding) - try: - data = stream.read() - except UnicodeError: # wrong encodingg - # detect_encoding returns utf-8 if no encoding specified - msg = 'Wrong (%s) or no encoding specified' % encoding - raise AstroidBuildingException(msg) - return stream, encoding, data - -else: - import re - - _ENCODING_RGX = re.compile(r"\s*#+.*coding[:=]\s*([-\w.]+)") - - 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 ############################################################## - -MANAGER = AstroidManager() - -class AstroidBuilder(InspectBuilder): - """provide astroid building methods""" - - def __init__(self, manager=None): - InspectBuilder.__init__(self) - self._manager = manager or MANAGER - - def module_build(self, module, modname=None): - """build an astroid from a living module instance - """ - node = None - path = getattr(module, '__file__', None) - if path is not None: - path_, ext = splitext(module.__file__) - if ext in ('.py', '.pyc', '.pyo') and exists(path_ + '.py'): - node = self.file_build(path_ + '.py', modname) - if node is None: - # this is a built-in module - # get a partial representation by introspection - node = self.inspect_build(module, modname=modname, path=path) - # we have to handle transformation by ourselves since the rebuilder - # isn't called for builtin nodes - # - # XXX it's then only called for Module nodes, not for underlying - # nodes - node = self._manager.transform(node) - return node - - def file_build(self, path, modname=None): - """build astroid from a source code file (i.e. from an ast) - - path is expected to be a python source file - """ - try: - _, encoding, data = open_source_file(path) - except IOError as exc: - msg = 'Unable to load file %r (%s)' % (path, exc) - raise AstroidBuildingException(msg) - except SyntaxError as exc: # py3k encoding specification error - raise AstroidBuildingException(exc) - except LookupError as exc: # unknown encoding - raise AstroidBuildingException(exc) - # get module name if necessary - if modname is None: - try: - modname = '.'.join(modpath_from_file(path)) - except ImportError: - modname = splitext(basename(path))[0] - # build astroid representation - module = self._data_build(data, modname, path) - return self._post_build(module, encoding) - - def string_build(self, data, modname='', path=None): - """build astroid from source code string and return rebuilded astroid""" - module = self._data_build(data, modname, path) - module.file_bytes = data.encode('utf-8') - return self._post_build(module, 'utf-8') - - def _post_build(self, module, encoding): - """handles encoding and delayed nodes - after a module has been built - """ - module.file_encoding = encoding - self._manager.cache_module(module) - # post tree building steps after we stored the module in the cache: - for from_node in module._from_nodes: - if from_node.modname == '__future__': - for symbol, _ in from_node.names: - module.future_imports.add(symbol) - 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: - node_file = '' - if modname.endswith('.__init__'): - modname = modname[:-9] - package = True - else: - package = path and path.find('__init__.py') > -1 or False - rebuilder = TreeRebuilder(self._manager) - module = rebuilder.visit_module(node, modname, package) - module.file = module.path = node_file - module._from_nodes = rebuilder._from_nodes - module._delayed_assattr = 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 - """ - - _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.do_import_module() - except InferenceError: - 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 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 - elif infered.is_function: - iattrs = infered.instance_attrs - 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 - diff --git a/exceptions.py b/exceptions.py deleted file mode 100644 index 3889e2e..0000000 --- a/exceptions.py +++ /dev/null @@ -1,51 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""this module contains exceptions used in the astroid library - -""" - -__doctype__ = "restructuredtext en" - -class AstroidError(Exception): - """base exception class for all astroid related exceptions""" - -class AstroidBuildingException(AstroidError): - """exception class when we are unable to build an astroid representation""" - -class ResolveError(AstroidError): - """base class of astroid resolution/inference error""" - -class NotFoundError(ResolveError): - """raised when we are unable to resolve a name""" - -class InferenceError(ResolveError): - """raised when we are unable to infer a node""" - -class UseInferenceDefault(Exception): - """exception to be raised in custom inference function to indicate that it - should go back to the default behaviour - """ - -class UnresolvableName(InferenceError): - """raised when we are unable to resolve a name""" - -class NoDefault(AstroidError): - """raised by function's `default_value` method when an argument has - no default value - """ - diff --git a/inference.py b/inference.py deleted file mode 100644 index 6cd4b78..0000000 --- a/inference.py +++ /dev/null @@ -1,395 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""this module contains a set of functions to handle inference on astroid trees -""" - -__doctype__ = "restructuredtext en" - -from itertools import chain - -from astroid import nodes - -from astroid.manager import AstroidManager -from astroid.exceptions import (AstroidError, InferenceError, NoDefault, - NotFoundError, UnresolvableName) -from astroid.bases import (YES, Instance, InferenceContext, - _infer_stmts, path_wrapper, - raise_if_nothing_infered) -from astroid.protocols import ( - _arguments_infer_argname, - BIN_OP_METHOD, UNARY_OP_METHOD) - -MANAGER = AstroidManager() - - -class CallContext(object): - """when inferring a function call, this class is used to remember values - given as argument - """ - def __init__(self, args, starargs, dstarargs): - self.args = [] - self.nargs = {} - for arg in args: - if isinstance(arg, nodes.Keyword): - self.nargs[arg.arg] = arg.value - else: - self.args.append(arg) - self.starargs = starargs - self.dstarargs = dstarargs - - def infer_argument(self, funcnode, name, context): - """infer a function argument value according to the call context""" - # 1. search in named keywords - try: - return self.nargs[name].infer(context) - except KeyError: - # Function.args.args can be None in astroid (means that we don't have - # information on argnames) - argindex = funcnode.args.find_argname(name)[0] - if argindex is not None: - # 2. first argument of instance/class method - if argindex == 0 and funcnode.type in ('method', 'classmethod'): - if context.boundnode is not None: - boundnode = context.boundnode - else: - # XXX can do better ? - boundnode = funcnode.parent.frame() - if funcnode.type == 'method': - if not isinstance(boundnode, Instance): - boundnode = Instance(boundnode) - return iter((boundnode,)) - if funcnode.type == 'classmethod': - return iter((boundnode,)) - # if we have a method, extract one position - # from the index, so we'll take in account - # the extra parameter represented by `self` or `cls` - if funcnode.type in ('method', 'classmethod'): - argindex -= 1 - # 2. search arg index - try: - return self.args[argindex].infer(context) - except IndexError: - pass - # 3. search in *args (.starargs) - if self.starargs is not None: - its = [] - for infered in self.starargs.infer(context): - if infered is YES: - its.append((YES,)) - continue - try: - its.append(infered.getitem(argindex, context).infer(context)) - except (InferenceError, AttributeError): - its.append((YES,)) - except (IndexError, TypeError): - continue - if its: - return chain(*its) - # 4. XXX search in **kwargs (.dstarargs) - if self.dstarargs is not None: - its = [] - for infered in self.dstarargs.infer(context): - if infered is YES: - its.append((YES,)) - continue - try: - its.append(infered.getitem(name, context).infer(context)) - except (InferenceError, AttributeError): - its.append((YES,)) - except (IndexError, TypeError): - continue - if its: - return chain(*its) - # 5. */** argument, (Tuple or Dict) - if name == funcnode.args.vararg: - return iter((nodes.const_factory(()))) - if name == funcnode.args.kwarg: - return iter((nodes.const_factory({}))) - # 6. return default value if any - try: - return funcnode.args.default_value(name).infer(context) - except NoDefault: - raise InferenceError(name) - - -# .infer method ############################################################### - - -def infer_end(self, context=None): - """inference's end for node such as Module, Class, Function, Const... - """ - yield self -nodes.Module._infer = infer_end -nodes.Class._infer = infer_end -nodes.Function._infer = infer_end -nodes.Lambda._infer = infer_end -nodes.Const._infer = infer_end -nodes.List._infer = infer_end -nodes.Tuple._infer = infer_end -nodes.Dict._infer = infer_end -nodes.Set._infer = infer_end - -def _higher_function_scope(node): - """ Search for the first function which encloses the given - scope. This can be used for looking up in that function's - scope, in case looking up in a lower scope for a particular - name fails. - - :param node: A scope node. - :returns: - ``None``, if no parent function scope was found, - otherwise an instance of :class:`astroid.scoped_nodes.Function`, - which encloses the given node. - """ - current = node - while current.parent and not isinstance(current.parent, nodes.Function): - current = current.parent - if current and current.parent: - return current.parent - -def infer_name(self, context=None): - """infer a Name: use name lookup rules""" - frame, stmts = self.lookup(self.name) - if not stmts: - # Try to see if the name is enclosed in a nested function - # and use the higher (first function) scope for searching. - # TODO: should this be promoted to other nodes as well? - parent_function = _higher_function_scope(self.scope()) - if parent_function: - _, stmts = parent_function.lookup(self.name) - - if not stmts: - raise UnresolvableName(self.name) - return _infer_stmts(stmts, context, frame, self.name) -nodes.Name._infer = path_wrapper(infer_name) -nodes.AssName.infer_lhs = infer_name # won't work with a path wrapper - - -def infer_callfunc(self, context=None): - """infer a CallFunc node by trying to guess what the function returns""" - if context is None: - context = InferenceContext() - for callee in self.func.infer(context): - with context.scope( - callcontext=CallContext(self.args, self.starargs, self.kwargs), - boundnode=None, - ): - if callee is YES: - yield callee - continue - try: - if hasattr(callee, 'infer_call_result'): - for infered in callee.infer_call_result(self, context): - yield infered - except InferenceError: - ## XXX log error ? - continue -nodes.CallFunc._infer = path_wrapper(raise_if_nothing_infered(infer_callfunc)) - - -def infer_import(self, context=None, asname=True, lookupname=None): - """infer an Import node: return the imported module/object""" - if lookupname is None: - raise InferenceError() - if asname: - yield self.do_import_module(self.real_name(lookupname)) - else: - yield self.do_import_module(lookupname) -nodes.Import._infer = path_wrapper(infer_import) - -def infer_name_module(self, name): - context = InferenceContext() - return self.infer(context, asname=False, lookupname=name) -nodes.Import.infer_name_module = infer_name_module - - -def infer_from(self, context=None, asname=True, lookupname=None): - """infer a From nodes: return the imported module/object""" - if lookupname is None: - raise InferenceError() - if asname: - lookupname = self.real_name(lookupname) - module = self.do_import_module() - try: - return _infer_stmts(module.getattr(lookupname, ignore_locals=module is self.root()), context, lookupname=lookupname) - except NotFoundError: - raise InferenceError(lookupname) -nodes.From._infer = path_wrapper(infer_from) - - -def infer_getattr(self, context=None): - """infer a Getattr node by using getattr on the associated object""" - if not context: - context = InferenceContext() - for owner in self.expr.infer(context): - if owner is YES: - yield owner - continue - try: - with context.scope(boundnode=owner): - for obj in owner.igetattr(self.attrname, context): - yield obj - except (NotFoundError, InferenceError): - pass - except AttributeError: - # XXX method / function - pass -nodes.Getattr._infer = path_wrapper(raise_if_nothing_infered(infer_getattr)) -nodes.AssAttr.infer_lhs = raise_if_nothing_infered(infer_getattr) # # won't work with a path wrapper - - -def infer_global(self, context=None, lookupname=None): - if lookupname is None: - raise InferenceError() - try: - return _infer_stmts(self.root().getattr(lookupname), context) - except NotFoundError: - raise InferenceError() -nodes.Global._infer = path_wrapper(infer_global) - - -def infer_subscript(self, context=None): - """infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]""" - value = next(self.value.infer(context)) - if value is YES: - yield YES - return - - index = next(self.slice.infer(context)) - if index is YES: - yield YES - return - - if isinstance(index, nodes.Const): - try: - assigned = value.getitem(index.value, context) - except AttributeError: - raise InferenceError() - except (IndexError, TypeError): - yield YES - return - for infered in assigned.infer(context): - yield infered - else: - raise InferenceError() -nodes.Subscript._infer = path_wrapper(infer_subscript) -nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript) - -def infer_unaryop(self, context=None): - for operand in self.operand.infer(context): - try: - yield operand.infer_unary_op(self.op) - except TypeError: - continue - except AttributeError: - meth = UNARY_OP_METHOD[self.op] - if meth is None: - yield YES - else: - try: - # XXX just suppose if the type implement meth, returned type - # will be the same - operand.getattr(meth) - yield operand - except GeneratorExit: - raise - except: - yield YES -nodes.UnaryOp._infer = path_wrapper(infer_unaryop) - -def _infer_binop(operator, operand1, operand2, context, failures=None): - if operand1 is YES: - yield operand1 - return - try: - for valnode in operand1.infer_binary_op(operator, operand2, context): - yield valnode - except AttributeError: - try: - # XXX just suppose if the type implement meth, returned type - # will be the same - operand1.getattr(BIN_OP_METHOD[operator]) - yield operand1 - except: - if failures is None: - yield YES - else: - failures.append(operand1) - -def infer_binop(self, context=None): - failures = [] - for lhs in self.left.infer(context): - for val in _infer_binop(self.op, lhs, self.right, context, failures): - yield val - for lhs in failures: - for rhs in self.right.infer(context): - for val in _infer_binop(self.op, rhs, lhs, context): - yield val -nodes.BinOp._infer = path_wrapper(infer_binop) - - -def infer_arguments(self, context=None, lookupname=None): - if lookupname is None: - raise InferenceError() - return _arguments_infer_argname(self, lookupname, context) -nodes.Arguments._infer = infer_arguments - - -def infer_ass(self, context=None): - """infer a AssName/AssAttr: need to inspect the RHS part of the - assign node - """ - stmt = self.statement() - if isinstance(stmt, nodes.AugAssign): - return stmt.infer(context) - stmts = list(self.assigned_stmts(context=context)) - return _infer_stmts(stmts, context) -nodes.AssName._infer = path_wrapper(infer_ass) -nodes.AssAttr._infer = path_wrapper(infer_ass) - -def infer_augassign(self, context=None): - failures = [] - for lhs in self.target.infer_lhs(context): - for val in _infer_binop(self.op, lhs, self.value, context, failures): - yield val - for lhs in failures: - for rhs in self.value.infer(context): - for val in _infer_binop(self.op, rhs, lhs, context): - yield val -nodes.AugAssign._infer = path_wrapper(infer_augassign) - - -# no infer method on DelName and DelAttr (expected InferenceError) - - -def infer_empty_node(self, context=None): - if not self.has_underlying_object(): - yield YES - else: - try: - for infered in MANAGER.infer_ast_from_something(self.object, - context=context): - yield infered - except AstroidError: - yield YES -nodes.EmptyNode._infer = path_wrapper(infer_empty_node) - - -def infer_index(self, context=None): - return self.value.infer(context) -nodes.Index._infer = infer_index diff --git a/inspector.py b/inspector.py deleted file mode 100644 index 1fc3192..0000000 --- a/inspector.py +++ /dev/null @@ -1,273 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""visitor doing some postprocessing on the astroid tree. -Try to resolve definitions (namespace) dictionary, relationship... - -This module has been imported from pyreverse -""" - -__docformat__ = "restructuredtext en" - -from os.path import dirname - -import astroid -from astroid.exceptions import InferenceError -from astroid.utils import LocalsVisitor -from astroid.modutils import get_module_part, is_relative, is_standard_module - -class IdGeneratorMixIn(object): - """ - Mixin adding the ability to generate integer uid - """ - def __init__(self, start_value=0): - self.id_count = start_value - - def init_counter(self, start_value=0): - """init the id counter - """ - self.id_count = start_value - - def generate_id(self): - """generate a new identifier - """ - self.id_count += 1 - return self.id_count - - -class Linker(IdGeneratorMixIn, LocalsVisitor): - """ - walk on the project tree and resolve relationships. - - According to options the following attributes may be added to visited nodes: - - * uid, - a unique identifier for the node (on astroid.Project, astroid.Module, - astroid.Class and astroid.locals_type). Only if the linker has been instantiated - with tag=True parameter (False by default). - - * Function - a mapping from locals names to their bounded value, which may be a - constant like a string or an integer, or an astroid node (on astroid.Module, - astroid.Class and astroid.Function). - - * instance_attrs_type - as locals_type but for klass member attributes (only on astroid.Class) - - * implements, - list of implemented interface _objects_ (only on astroid.Class nodes) - """ - - def __init__(self, project, inherited_interfaces=0, tag=False): - IdGeneratorMixIn.__init__(self) - LocalsVisitor.__init__(self) - # take inherited interface in consideration or not - self.inherited_interfaces = inherited_interfaces - # tag nodes or not - self.tag = tag - # visited project - self.project = project - - - def visit_project(self, node): - """visit an astroid.Project node - - * optionally tag the node with a unique id - """ - if self.tag: - node.uid = self.generate_id() - for module in node.modules: - self.visit(module) - - def visit_package(self, node): - """visit an astroid.Package node - - * optionally tag the node with a unique id - """ - if self.tag: - node.uid = self.generate_id() - for subelmt in node.values(): - self.visit(subelmt) - - def visit_module(self, node): - """visit an astroid.Module node - - * set the locals_type mapping - * set the depends mapping - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - node.depends = [] - if self.tag: - node.uid = self.generate_id() - - def visit_class(self, node): - """visit an astroid.Class node - - * set the locals_type and instance_attrs_type mappings - * set the implements list and build it - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - if self.tag: - node.uid = self.generate_id() - # resolve ancestors - for baseobj in node.ancestors(recurs=False): - specializations = getattr(baseobj, 'specializations', []) - specializations.append(node) - baseobj.specializations = specializations - # resolve instance attributes - node.instance_attrs_type = {} - for assattrs in node.instance_attrs.values(): - for assattr in assattrs: - self.handle_assattr_type(assattr, node) - # resolve implemented interface - try: - node.implements = list(node.interfaces(self.inherited_interfaces)) - except InferenceError: - node.implements = () - - def visit_function(self, node): - """visit an astroid.Function node - - * set the locals_type mapping - * optionally tag the node with a unique id - """ - if hasattr(node, 'locals_type'): - return - node.locals_type = {} - if self.tag: - node.uid = self.generate_id() - - link_project = visit_project - link_module = visit_module - link_class = visit_class - link_function = visit_function - - def visit_assname(self, node): - """visit an astroid.AssName node - - handle locals_type - """ - # avoid double parsing done by different Linkers.visit - # running over the same project: - if hasattr(node, '_handled'): - return - node._handled = True - if node.name in node.frame(): - frame = node.frame() - else: - # the name has been defined as 'global' in the frame and belongs - # there. Btw the frame is not yet visited as the name is in the - # root locals; the frame hence has no locals_type attribute - frame = node.root() - try: - values = node.infered() - try: - already_infered = frame.locals_type[node.name] - for valnode in values: - if not valnode in already_infered: - already_infered.append(valnode) - except KeyError: - frame.locals_type[node.name] = values - except astroid.InferenceError: - pass - - def handle_assattr_type(self, node, parent): - """handle an astroid.AssAttr node - - handle instance_attrs_type - """ - try: - values = list(node.infer()) - try: - already_infered = parent.instance_attrs_type[node.attrname] - for valnode in values: - if not valnode in already_infered: - already_infered.append(valnode) - except KeyError: - parent.instance_attrs_type[node.attrname] = values - except astroid.InferenceError: - pass - - def visit_import(self, node): - """visit an astroid.Import node - - resolve module dependencies - """ - context_file = node.root().file - for name in node.names: - relative = is_relative(name[0], context_file) - self._imported_module(node, name[0], relative) - - - def visit_from(self, node): - """visit an astroid.From node - - resolve module dependencies - """ - basename = node.modname - context_file = node.root().file - if context_file is not None: - relative = is_relative(basename, context_file) - else: - relative = False - for name in node.names: - if name[0] == '*': - continue - # analyze dependencies - fullname = '%s.%s' % (basename, name[0]) - if fullname.find('.') > -1: - try: - # XXX: don't use get_module_part, missing package precedence - fullname = get_module_part(fullname, context_file) - except ImportError: - continue - if fullname != basename: - self._imported_module(node, fullname, relative) - - - def compute_module(self, context_name, mod_path): - """return true if the module should be added to dependencies""" - package_dir = dirname(self.project.path) - if context_name == mod_path: - return 0 - elif is_standard_module(mod_path, (package_dir,)): - return 1 - return 0 - - # protected methods ######################################################## - - def _imported_module(self, node, mod_path, relative): - """notify an imported module, used to analyze dependencies - """ - module = node.root() - context_name = module.name - if relative: - mod_path = '%s.%s' % ('.'.join(context_name.split('.')[:-1]), - mod_path) - if self.compute_module(context_name, mod_path): - # handle dependencies - if not hasattr(module, 'depends'): - module.depends = [] - mod_paths = module.depends - if not mod_path in mod_paths: - mod_paths.append(mod_path) diff --git a/manager.py b/manager.py deleted file mode 100644 index e1660e4..0000000 --- a/manager.py +++ /dev/null @@ -1,348 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""astroid manager: avoid multiple astroid build of a same module when -possible by providing a class responsible to get astroid representation -from various source and using a cache of built modules) -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -import os -from os.path import dirname, join, isdir, exists -from warnings import warn - -from logilab.common.configuration import OptionsProviderMixIn - -from astroid.exceptions import AstroidBuildingException -from astroid.modutils import NoSourceFile, is_python_source, \ - file_from_modpath, load_module_from_name, modpath_from_file, \ - get_module_files, get_source_file, zipimport - - -def astroid_wrapper(func, modname): - """wrapper to give to AstroidManager.project_from_files""" - print('parsing %s...' % modname) - try: - return func(modname) - except AstroidBuildingException as exc: - print(exc) - except Exception as 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) - except: - return '???' - - - -class AstroidManager(OptionsProviderMixIn): - """the astroid manager, responsible to build astroid from files - or modules. - - Use the Borg pattern. - """ - - name = 'astroid loader' - options = (("ignore", - {'type' : "csv", 'metavar' : "", - 'dest' : "black_list", "default" : ('CVS',), - 'help' : "add (may be a directory) to the black list\ -. It should be a base name, not a path. You may set this option multiple times\ -."}), - ("project", - {'default': "No Name", 'type' : 'string', 'short': 'p', - 'metavar' : '', - 'help' : 'set the project name.'}), - ) - brain = {} - def __init__(self): - self.__dict__ = AstroidManager.brain - if not self.__dict__: - OptionsProviderMixIn.__init__(self) - self.load_defaults() - # NOTE: cache entries are added by the [re]builder - self.astroid_cache = {} - self._mod_file_cache = {} - self.transforms = {} - - def ast_from_file(self, filepath, modname=None, fallback=True, source=False): - """given a module name, return the astroid object""" - try: - filepath = get_source_file(filepath, include_no_ext=True) - source = True - except NoSourceFile: - pass - if modname is None: - try: - modname = '.'.join(modpath_from_file(filepath)) - except ImportError: - modname = filepath - if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath: - return self.astroid_cache[modname] - if source: - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).file_build(filepath, modname) - elif fallback and modname: - return self.ast_from_module_name(modname) - raise AstroidBuildingException('unable to get astroid for file %s' % - filepath) - - def ast_from_module_name(self, modname, context_file=None): - """given a module name, return the astroid object""" - if modname in self.astroid_cache: - return self.astroid_cache[modname] - if modname == '__main__': - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).string_build('', modname) - old_cwd = os.getcwd() - if context_file: - os.chdir(dirname(context_file)) - try: - filepath = self.file_from_module_name(modname, context_file) - if filepath is not None and not is_python_source(filepath): - module = self.zip_import_data(filepath) - if module is not None: - return module - if filepath is None or not is_python_source(filepath): - try: - module = load_module_from_name(modname) - except Exception as ex: - msg = 'Unable to load module %s (%s)' % (modname, ex) - raise AstroidBuildingException(msg) - return self.ast_from_module(module, modname) - return self.ast_from_file(filepath, modname, fallback=False) - finally: - os.chdir(old_cwd) - - def zip_import_data(self, filepath): - if zipimport is None: - return None - from astroid.builder import AstroidBuilder - builder = AstroidBuilder(self) - for ext in ('.zip', '.egg'): - try: - eggpath, resource = filepath.rsplit(ext + '/', 1) - except ValueError: - continue - try: - importer = zipimport.zipimporter(eggpath + ext) - zmodname = resource.replace('/', '.') - if importer.is_package(resource): - zmodname = zmodname + '.__init__' - module = builder.string_build(importer.get_source(resource), - zmodname, filepath) - return module - except: - continue - return None - - def file_from_module_name(self, modname, contextfile): - try: - value = self._mod_file_cache[(modname, contextfile)] - except KeyError: - try: - value = file_from_modpath(modname.split('.'), - context_file=contextfile) - except ImportError as ex: - msg = 'Unable to load module %s (%s)' % (modname, ex) - value = AstroidBuildingException(msg) - self._mod_file_cache[(modname, contextfile)] = value - if isinstance(value, AstroidBuildingException): - raise value - return value - - def ast_from_module(self, module, modname=None): - """given an imported module, return the astroid object""" - modname = modname or module.__name__ - if modname in self.astroid_cache: - return self.astroid_cache[modname] - try: - # some builtin modules don't have __file__ attribute - filepath = module.__file__ - if is_python_source(filepath): - return self.ast_from_file(filepath, modname) - except AttributeError: - pass - from astroid.builder import AstroidBuilder - return AstroidBuilder(self).module_build(module, modname) - - def ast_from_class(self, klass, modname=None): - """get astroid for the given class""" - if modname is None: - try: - modname = klass.__module__ - except AttributeError: - raise AstroidBuildingException( - 'Unable to get module for class %s' % safe_repr(klass)) - modastroid = self.ast_from_module_name(modname) - return modastroid.getattr(klass.__name__)[0] # XXX - - - def infer_ast_from_something(self, obj, context=None): - """infer astroid for the given class""" - if hasattr(obj, '__class__') and not isinstance(obj, type): - klass = obj.__class__ - else: - klass = obj - try: - modname = klass.__module__ - except AttributeError: - raise AstroidBuildingException( - 'Unable to get module for %s' % safe_repr(klass)) - except Exception as ex: - raise AstroidBuildingException( - 'Unexpected error while retrieving module for %s: %s' - % (safe_repr(klass), ex)) - try: - name = klass.__name__ - except AttributeError: - raise AstroidBuildingException( - 'Unable to get name for %s' % safe_repr(klass)) - except Exception as ex: - raise AstroidBuildingException( - 'Unexpected error while retrieving name for %s: %s' - % (safe_repr(klass), ex)) - # take care, on living object __module__ is regularly wrong :( - modastroid = self.ast_from_module_name(modname) - if klass is obj: - for infered in modastroid.igetattr(name, context): - yield infered - else: - for infered in modastroid.igetattr(name, context): - yield infered.instanciate_class() - - def project_from_files(self, files, func_wrapper=astroid_wrapper, - project_name=None, black_list=None): - """return a Project from a list of files or modules""" - # build the project representation - project_name = project_name or self.config.project - black_list = black_list or self.config.black_list - project = Project(project_name) - for something in files: - if not exists(something): - fpath = file_from_modpath(something.split('.')) - elif isdir(something): - fpath = join(something, '__init__.py') - else: - fpath = something - astroid = func_wrapper(self.ast_from_file, fpath) - if astroid is None: - continue - # XXX why is first file defining the project.path ? - project.path = project.path or astroid.file - project.add_module(astroid) - base_name = astroid.name - # recurse in package except if __init__ was explicitly given - if astroid.package and something.find('__init__') == -1: - # recurse on others packages / modules if this is a package - for fpath in get_module_files(dirname(astroid.file), - black_list): - astroid = func_wrapper(self.ast_from_file, fpath) - if astroid is None or astroid.name == base_name: - continue - project.add_module(astroid) - return project - - def register_transform(self, node_class, transform, predicate=None): - """Register `transform(node)` function to be applied on the given - Astroid's `node_class` if `predicate` is None or return a true value - when called with the node as argument. - - The transform function may return a value which is then used to - substitute the original node in the tree. - """ - self.transforms.setdefault(node_class, []).append((transform, predicate)) - - def unregister_transform(self, node_class, transform, predicate=None): - """Unregister the given transform.""" - self.transforms[node_class].remove((transform, predicate)) - - def transform(self, node): - """Call matching transforms for the given node if any and return the - transformed node. - """ - cls = node.__class__ - if cls not in self.transforms: - # no transform registered for this class of node - return node - - transforms = self.transforms[cls] - orig_node = node # copy the reference - for transform_func, predicate in transforms: - if predicate is None or predicate(node): - ret = transform_func(node) - # if the transformation function returns something, it's - # expected to be a replacement for the node - if ret is not None: - if node is not orig_node: - # node has already be modified by some previous - # transformation, warn about it - warn('node %s substituted multiple times' % node) - node = ret - return node - - def cache_module(self, module): - """Cache a module if no module with the same name is known yet.""" - self.astroid_cache.setdefault(module.name, module) - - def clear_cache(self): - self.astroid_cache.clear() - # force bootstrap again, else we may ends up with cache inconsistency - # between the manager and CONST_PROXY, making - # unittest_lookup.LookupTC.test_builtin_lookup fail depending on the - # test order - from astroid.raw_building import astroid_bootstrapping - astroid_bootstrapping() - - -class Project(object): - """a project handle a set of modules / packages""" - def __init__(self, name=''): - self.name = name - self.path = None - self.modules = [] - self.locals = {} - self.__getitem__ = self.locals.__getitem__ - self.__iter__ = self.locals.__iter__ - self.values = self.locals.values - self.keys = self.locals.keys - self.items = self.locals.items - - def add_module(self, node): - self.locals[node.name] = node - self.modules.append(node) - - def get_module(self, name): - return self.locals[name] - - def get_children(self): - return self.modules - - def __repr__(self): - return '' % (self.name, id(self), - len(self.modules)) - - diff --git a/mixins.py b/mixins.py deleted file mode 100644 index dbf1673..0000000 --- a/mixins.py +++ /dev/null @@ -1,124 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""This module contains some mixins for the different nodes. -""" - -from logilab.common.decorators import cachedproperty - -from astroid.exceptions import (AstroidBuildingException, InferenceError, - NotFoundError) - - -class BlockRangeMixIn(object): - """override block range """ - - @cachedproperty - def blockstart_tolineno(self): - return self.lineno - - def _elsed_block_range(self, lineno, orelse, last=None): - """handle block line numbers range for try/finally, for, if and while - statements - """ - if lineno == self.fromlineno: - return lineno, lineno - if orelse: - if lineno >= orelse[0].fromlineno: - return lineno, orelse[-1].tolineno - return lineno, orelse[0].fromlineno - 1 - return lineno, last or self.tolineno - - -class FilterStmtsMixin(object): - """Mixin for statement filtering and assignment type""" - - def _get_filtered_stmts(self, _, node, _stmts, mystmt): - """method used in _filter_stmts to get statemtents and trigger break""" - if self.statement() is mystmt: - # original node's statement is the assignment, only keep - # current node (gen exp, list comp) - return [node], True - return _stmts, False - - def ass_type(self): - return self - - -class AssignTypeMixin(object): - - def ass_type(self): - return self - - def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt): - """method used in filter_stmts""" - if self is mystmt: - return _stmts, True - if self.statement() is mystmt: - # original node's statement is the assignment, only keep - # current node (gen exp, list comp) - return [node], True - return _stmts, False - - -class ParentAssignTypeMixin(AssignTypeMixin): - - def ass_type(self): - return self.parent.ass_type() - - -class FromImportMixIn(FilterStmtsMixin): - """MixIn for From and Import Nodes""" - - def _infer_name(self, frame, name): - return name - - def do_import_module(self, modname=None): - """return the ast for a module whose name is imported by - """ - # handle special case where we are on a package node importing a module - # using the same name as the package, which may end in an infinite loop - # on relative imports - # XXX: no more needed ? - mymodule = self.root() - level = getattr(self, 'level', None) # Import as no level - if modname is None: - modname = self.modname - # XXX we should investigate deeper if we really want to check - # importing itself: modname and mymodule.name be relative or absolute - if mymodule.relative_to_absolute_name(modname, level) == mymodule.name: - # FIXME: we used to raise InferenceError here, but why ? - return mymodule - try: - return mymodule.import_module(modname, level=level) - except AstroidBuildingException: - raise InferenceError(modname) - except SyntaxError as ex: - raise InferenceError(str(ex)) - - def real_name(self, asname): - """get name from 'as' name""" - for name, _asname in self.names: - if name == '*': - return asname - if not _asname: - name = name.split('.', 1)[0] - _asname = name - if asname == _asname: - return name - raise NotFoundError(asname) - diff --git a/modutils.py b/modutils.py deleted file mode 100644 index efc999b..0000000 --- a/modutils.py +++ /dev/null @@ -1,638 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""Python modules manipulation utility functions. - -:type PY_SOURCE_EXTS: tuple(str) -:var PY_SOURCE_EXTS: list of possible python source file extension - -:type STD_LIB_DIR: str -:var STD_LIB_DIR: directory where standard modules are located - -:type BUILTIN_MODULES: dict -:var BUILTIN_MODULES: dictionary with builtin module names has key -""" -from __future__ import with_statement - -__docformat__ = "restructuredtext en" - -import sys -import os -from os.path import splitext, join, abspath, isdir, dirname, exists -from imp import find_module, load_module, C_BUILTIN, PY_COMPILED, PKG_DIRECTORY -from distutils.sysconfig import get_python_lib -from distutils.errors import DistutilsPlatformError - -try: - import zipimport -except ImportError: - zipimport = None - -ZIPFILE = object() - -from logilab.common import _handle_blacklist - -# Notes about STD_LIB_DIR -# Consider arch-specific installation for STD_LIB_DIR definition -# :mod:`distutils.sysconfig` contains to much hardcoded values to rely on -# -# :see: `Problems with /usr/lib64 builds `_ -# :see: `FHS `_ -if sys.platform.startswith('win'): - PY_SOURCE_EXTS = ('py', 'pyw') - PY_COMPILED_EXTS = ('dll', 'pyd') -else: - PY_SOURCE_EXTS = ('py',) - PY_COMPILED_EXTS = ('so',) - -try: - STD_LIB_DIR = get_python_lib(standard_lib=1) -# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to -# non-valid path, see https://bugs.pypy.org/issue1164 -except DistutilsPlatformError: - STD_LIB_DIR = '//' - -EXT_LIB_DIR = get_python_lib() - -BUILTIN_MODULES = dict(zip(sys.builtin_module_names, - [1]*len(sys.builtin_module_names))) - - -class NoSourceFile(Exception): - """exception raised when we are not able to get a python - source file for a precompiled file - """ - - -def load_module_from_name(dotted_name, path=None, use_sys=1): - """Load a Python module from its name. - - :type dotted_name: str - :param dotted_name: python name of a module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - return load_module_from_modpath(dotted_name.split('.'), path, use_sys) - - -def load_module_from_modpath(parts, path=None, use_sys=1): - """Load a python module from its splitted name. - - :type parts: list(str) or tuple(str) - :param parts: - python name of a module or package splitted on '.' - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be used or not - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - if use_sys: - try: - return sys.modules['.'.join(parts)] - except KeyError: - pass - modpath = [] - prevmodule = None - for part in parts: - modpath.append(part) - curname = '.'.join(modpath) - module = None - if len(modpath) != len(parts): - # even with use_sys=False, should try to get outer packages from sys.modules - module = sys.modules.get(curname) - elif use_sys: - # because it may have been indirectly loaded through a parent - module = sys.modules.get(curname) - if module is None: - mp_file, mp_filename, mp_desc = find_module(part, path) - module = load_module(curname, mp_file, mp_filename, mp_desc) - if prevmodule: - setattr(prevmodule, part, module) - _file = getattr(module, '__file__', '') - if not _file and len(modpath) != len(parts): - raise ImportError('no module in %s' % '.'.join(parts[len(modpath):])) - path = [dirname(_file)] - prevmodule = module - return module - - -def load_module_from_file(filepath, path=None, use_sys=1, extrapath=None): - """Load a Python module from it's path. - - :type filepath: str - :param filepath: path to the python module or package - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type use_sys: bool - :param use_sys: - boolean indicating whether the sys.modules dictionary should be - used or not - - - :raise ImportError: if the module or package is not found - - :rtype: module - :return: the loaded module - """ - modpath = modpath_from_file(filepath, extrapath) - return load_module_from_modpath(modpath, path, use_sys) - - -def _check_init(path, mod_path): - """check there are some __init__.py all along the way""" - for part in mod_path: - path = join(path, part) - if not _has_init(path): - return False - return True - - -def modpath_from_file(filename, extrapath=None): - """given a file path return the corresponding splitted module's name - (i.e name of a module or package splitted on '.') - - :type filename: str - :param filename: file's path for which we want the module's name - - :type extrapath: dict - :param extrapath: - optional extra search path, with path as key and package name for the path - as value. This is usually useful to handle package splitted in multiple - directories using __path__ trick. - - - :raise ImportError: - if the corresponding module's name has not been found - - :rtype: list(str) - :return: the corresponding splitted module's name - """ - base = splitext(abspath(filename))[0] - if extrapath is not None: - for path_ in extrapath: - path = _abspath(path_) - if path and base[:len(path)] == path: - submodpath = [pkg for pkg in base[len(path):].split(os.sep) - if pkg] - if _check_init(path, submodpath[:-1]): - return extrapath[path_].split('.') + submodpath - for path in sys.path: - path = _abspath(path) - if path and base.startswith(path): - modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg] - if _check_init(path, modpath[:-1]): - return modpath - raise ImportError('Unable to find module for %s in %s' % ( - filename, ', \n'.join(sys.path))) - - - -def file_from_modpath(modpath, path=None, context_file=None): - """given a mod path (i.e. splitted module / package name), return the - corresponding file, giving priority to source file over precompiled - file if it exists - - :type modpath: list or tuple - :param modpath: - splitted module's name (i.e name of a module or package splitted - on '.') - (this means explicit relative imports that start with dots have - empty strings in this list!) - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the path to the module's file or None if it's an integrated - builtin module such as 'sys' - """ - if context_file is not None: - context = dirname(context_file) - else: - context = context_file - if modpath[0] == 'xml': - # handle _xmlplus - try: - return _file_from_modpath(['_xmlplus'] + modpath[1:], path, context) - except ImportError: - return _file_from_modpath(modpath, path, context) - elif modpath == ['os', 'path']: - # FIXME: currently ignoring search_path... - return os.path.__file__ - return _file_from_modpath(modpath, path, context) - - - -def get_module_part(dotted_name, context_file=None): - """given a dotted name return the module part of the name : - - >>> get_module_part('logilab.common.modutils.get_module_part') - 'logilab.common.modutils' - - :type dotted_name: str - :param dotted_name: full name of the identifier we are interested in - - :type context_file: str or None - :param context_file: - context file to consider, necessary if the identifier has been - introduced using a relative import unresolvable in the actual - context (i.e. modutils) - - - :raise ImportError: if there is no such module in the directory - - :rtype: str or None - :return: - the module part of the name or None if we have not been able at - all to import the given name - - XXX: deprecated, since it doesn't handle package precedence over module - (see #10066) - """ - # os.path trick - if dotted_name.startswith('os.path'): - return 'os.path' - parts = dotted_name.split('.') - if context_file is not None: - # first check for builtin module which won't be considered latter - # in that case (path != None) - if parts[0] in BUILTIN_MODULES: - if len(parts) > 2: - raise ImportError(dotted_name) - return parts[0] - # don't use += or insert, we want a new list to be created ! - path = None - starti = 0 - if parts[0] == '': - assert context_file is not None, \ - 'explicit relative import, but no context_file?' - path = [] # prevent resolving the import non-relatively - starti = 1 - while parts[starti] == '': # for all further dots: change context - starti += 1 - context_file = dirname(context_file) - for i in range(starti, len(parts)): - try: - file_from_modpath(parts[starti:i+1], path=path, - context_file=context_file) - except ImportError: - if not i >= max(1, len(parts) - 2): - raise - return '.'.join(parts[:i]) - return dotted_name - - -def get_module_files(src_directory, blacklist): - """given a package directory return a list of all available python - module's files in the package and its subpackages - - :type src_directory: str - :param src_directory: - path of the directory corresponding to the package - - :type blacklist: list or tuple - :param blacklist: - optional list of files or directory to ignore, default to the value of - `logilab.common.STD_BLACKLIST` - - :rtype: list - :return: - the list of all available python module's files in the package and - its subpackages - """ - files = [] - for directory, dirnames, filenames in os.walk(src_directory): - _handle_blacklist(blacklist, dirnames, filenames) - # check for __init__.py - if not '__init__.py' in filenames: - dirnames[:] = () - continue - for filename in filenames: - if _is_python_file(filename): - src = join(directory, filename) - files.append(src) - return files - - -def get_source_file(filename, include_no_ext=False): - """given a python module's file name return the matching source file - name (the filename will be returned identically if it's a already an - absolute path to a python source file...) - - :type filename: str - :param filename: python module's file name - - - :raise NoSourceFile: if no source file exists on the file system - - :rtype: str - :return: the absolute path of the source file if it exists - """ - base, orig_ext = splitext(abspath(filename)) - for ext in PY_SOURCE_EXTS: - source_path = '%s.%s' % (base, ext) - if exists(source_path): - return source_path - if include_no_ext and not orig_ext and exists(base): - return base - raise NoSourceFile(filename) - - -def is_python_source(filename): - """ - rtype: bool - return: True if the filename is a python source file - """ - return splitext(filename)[1][1:] in PY_SOURCE_EXTS - - -def is_standard_module(modname, std_path=(STD_LIB_DIR,)): - """try to guess if a module is a standard python module (by default, - see `std_path` parameter's description) - - :type modname: str - :param modname: name of the module we are interested in - - :type std_path: list(str) or tuple(str) - :param std_path: list of path considered has standard - - - :rtype: bool - :return: - true if the module: - - is located on the path listed in one of the directory in `std_path` - - is a built-in module - """ - modname = modname.split('.')[0] - try: - filename = file_from_modpath([modname]) - except ImportError: - # import failed, i'm probably not so wrong by supposing it's - # not standard... - return False - # modules which are not living in a file are considered standard - # (sys and __builtin__ for instance) - if filename is None: - return True - filename = abspath(filename) - if filename.startswith(EXT_LIB_DIR): - return False - for path in std_path: - if filename.startswith(_abspath(path)): - return True - return False - - - -def is_relative(modname, from_file): - """return true if the given module name is relative to the given - file name - - :type modname: str - :param modname: name of the module we are interested in - - :type from_file: str - :param from_file: - path of the module from which modname has been imported - - :rtype: bool - :return: - true if the module has been imported relatively to `from_file` - """ - if not isdir(from_file): - from_file = dirname(from_file) - if from_file in sys.path: - return False - try: - find_module(modname.split('.')[0], [from_file]) - return True - except ImportError: - return False - - -# internal only functions ##################################################### - -def _file_from_modpath(modpath, path=None, context=None): - """given a mod path (i.e. splitted module / package name), return the - corresponding file - - this function is used internally, see `file_from_modpath`'s - documentation for more information - """ - assert len(modpath) > 0 - if context is not None: - try: - mtype, mp_filename = _module_file(modpath, [context]) - except ImportError: - mtype, mp_filename = _module_file(modpath, path) - else: - mtype, mp_filename = _module_file(modpath, path) - if mtype == PY_COMPILED: - try: - return get_source_file(mp_filename) - except NoSourceFile: - return mp_filename - elif mtype == C_BUILTIN: - # integrated builtin module - return None - elif mtype == PKG_DIRECTORY: - mp_filename = _has_init(mp_filename) - return mp_filename - -def _search_zip(modpath, pic): - for filepath, importer in pic.items(): - if importer is not None: - if importer.find_module(modpath[0]): - if not importer.find_module(os.path.sep.join(modpath)): - raise ImportError('No module named %s in %s/%s' % ( - '.'.join(modpath[1:]), filepath, modpath)) - return ZIPFILE, abspath(filepath) + os.path.sep + os.path.sep.join(modpath), filepath - raise ImportError('No module named %s' % '.'.join(modpath)) - - -def _abspath(path, _abspathcache={}): #pylint: disable=dangerous-default-value - """abspath with caching""" - # _module_file calls abspath on every path in sys.path every time it's - # called; on a larger codebase this easily adds up to half a second just - # assembling path components. This cache alleviates that. - try: - return _abspathcache[path] - except KeyError: - if not path: # don't cache result for '' - return abspath(path) - _abspathcache[path] = abspath(path) - return _abspathcache[path] - -try: - import pkg_resources -except ImportError: - pkg_resources = None - -def _module_file(modpath, path=None): - """get a module type / file path - - :type modpath: list or tuple - :param modpath: - splitted module's name (i.e name of a module or package splitted - on '.'), with leading empty strings for explicit relative import - - :type path: list or None - :param path: - optional list of path where the module or package should be - searched (use sys.path if nothing or None is given) - - - :rtype: tuple(int, str) - :return: the module type flag and the file path for a module - """ - # egg support compat - try: - pic = sys.path_importer_cache - _path = (path is None and sys.path or path) - for __path in _path: - if not __path in pic: - try: - pic[__path] = zipimport.zipimporter(__path) - except zipimport.ZipImportError: - pic[__path] = None - checkeggs = True - except AttributeError: - checkeggs = False - # pkg_resources support (aka setuptools namespace packages) - if (pkg_resources is not None - and modpath[0] in pkg_resources._namespace_packages - and modpath[0] in sys.modules - and len(modpath) > 1): - # setuptools has added into sys.modules a module object with proper - # __path__, get back information from there - module = sys.modules[modpath.pop(0)] - path = module.__path__ - imported = [] - while modpath: - modname = modpath[0] - # take care to changes in find_module implementation wrt builtin modules - # - # Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23) - # >>> imp.find_module('posix') - # (None, 'posix', ('', '', 6)) - # - # Python 3.3.1 (default, Apr 26 2013, 12:08:46) - # >>> imp.find_module('posix') - # (None, None, ('', '', 6)) - try: - _, mp_filename, mp_desc = find_module(modname, path) - except ImportError: - if checkeggs: - return _search_zip(modpath, pic)[:2] - raise - else: - if checkeggs and mp_filename: - fullabspath = [_abspath(x) for x in _path] - try: - pathindex = fullabspath.index(dirname(abspath(mp_filename))) - emtype, emp_filename, zippath = _search_zip(modpath, pic) - if pathindex > _path.index(zippath): - # an egg takes priority - return emtype, emp_filename - except ValueError: - # XXX not in _path - pass - except ImportError: - pass - checkeggs = False - imported.append(modpath.pop(0)) - mtype = mp_desc[2] - if modpath: - if mtype != PKG_DIRECTORY: - raise ImportError('No module %s in %s' % ('.'.join(modpath), - '.'.join(imported))) - # XXX guess if package is using pkgutil.extend_path by looking for - # those keywords in the first four Kbytes - try: - with open(join(mp_filename, '__init__.py'), 'rb') as stream: - data = stream.read(4096) - except IOError: - path = [mp_filename] - else: - if b'pkgutil' in data and b'extend_path' in data: - # extend_path is called, search sys.path for module/packages - # of this name see pkgutil.extend_path documentation - path = [join(p, *imported) for p in sys.path - if isdir(join(p, *imported))] - else: - path = [mp_filename] - return mtype, mp_filename - -def _is_python_file(filename): - """return true if the given filename should be considered as a python file - - .pyc and .pyo are ignored - """ - for ext in ('.py', '.so', '.pyd', '.pyw'): - if filename.endswith(ext): - return True - return False - - -def _has_init(directory): - """if the given directory has a valid __init__ file, return its path, - else return None - """ - mod_or_pack = join(directory, '__init__') - for ext in PY_SOURCE_EXTS + ('pyc', 'pyo'): - if exists(mod_or_pack + '.' + ext): - return mod_or_pack + '.' + ext - return None diff --git a/node_classes.py b/node_classes.py deleted file mode 100644 index 71e512f..0000000 --- a/node_classes.py +++ /dev/null @@ -1,965 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""Module for some node classes. More nodes in scoped_nodes.py -""" - -import sys - -import six -from logilab.common.decorators import cachedproperty - -from astroid.exceptions import NoDefault -from astroid.bases import (NodeNG, Statement, Instance, InferenceContext, - _infer_stmts, YES, BUILTINS) -from astroid.mixins import (BlockRangeMixIn, AssignTypeMixin, - ParentAssignTypeMixin, FromImportMixIn) - -PY3K = sys.version_info >= (3, 0) - - -def unpack_infer(stmt, context=None): - """recursively generate nodes inferred by the given statement. - If the inferred value is a list or a tuple, recurse on the elements - """ - if isinstance(stmt, (List, Tuple)): - for elt in stmt.elts: - for infered_elt in unpack_infer(elt, context): - yield infered_elt - return - # if infered is a final node, return it and stop - infered = next(stmt.infer(context)) - if infered is stmt: - yield infered - return - # else, infer recursivly, except YES object that should be returned as is - for infered in stmt.infer(context): - if infered is YES: - yield infered - else: - for inf_inf in unpack_infer(infered, context): - yield inf_inf - - -def are_exclusive(stmt1, stmt2, exceptions=None): - """return true if the two given statements are mutually exclusive - - `exceptions` may be a list of exception names. If specified, discard If - branches and check one of the statement is in an exception handler catching - one of the given exceptions. - - algorithm : - 1) index stmt1's parents - 2) climb among stmt2's parents until we find a common parent - 3) if the common parent is a If or TryExcept statement, look if nodes are - in exclusive branches - """ - # index stmt1's parents - stmt1_parents = {} - children = {} - node = stmt1.parent - previous = stmt1 - while node: - stmt1_parents[node] = 1 - children[node] = previous - previous = node - node = node.parent - # climb among stmt2's parents until we find a common parent - node = stmt2.parent - previous = stmt2 - while node: - if node in stmt1_parents: - # if the common parent is a If or TryExcept statement, look if - # nodes are in exclusive branches - if isinstance(node, If) and exceptions is None: - if (node.locate_child(previous)[1] - is not node.locate_child(children[node])[1]): - return True - elif isinstance(node, TryExcept): - c2attr, c2node = node.locate_child(previous) - c1attr, c1node = node.locate_child(children[node]) - if c1node is not c2node: - if ((c2attr == 'body' and c1attr == 'handlers' and children[node].catch(exceptions)) or - (c2attr == 'handlers' and c1attr == 'body' and previous.catch(exceptions)) or - (c2attr == 'handlers' and c1attr == 'orelse') or - (c2attr == 'orelse' and c1attr == 'handlers')): - return True - elif c2attr == 'handlers' and c1attr == 'handlers': - return previous is not children[node] - return False - previous = node - node = node.parent - return False - - -class LookupMixIn(object): - """Mixin looking up a name in the right scope - """ - - def lookup(self, name): - """lookup a variable name - - return the scope node and the list of assignments associated to the - given name according to the scope where it has been found (locals, - globals or builtin) - - The lookup is starting from self's scope. If self is not a frame itself - and the name is found in the inner frame locals, statements will be - filtered to remove ignorable statements according to self's location - """ - return self.scope().scope_lookup(self, name) - - 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) - return _infer_stmts(stmts, None, frame) - - def _filter_stmts(self, stmts, frame, offset): - """filter statements to remove ignorable statements. - - If self is not a frame itself and the name is found in the inner - frame locals, statements will be filtered to remove ignorable - statements according to self's location - """ - # if offset == -1, my actual frame is not the inner frame but its parent - # - # class A(B): pass - # - # we need this to resolve B correctly - if offset == -1: - myframe = self.frame().parent.frame() - else: - myframe = self.frame() - # If the frame of this node is the same as the statement - # of this node, then the node is part of a class or - # a function definition and the frame of this node should be the - # the upper frame, not the frame of the definition. - # For more information why this is important, - # see Pylint issue #295. - # For example, for 'b', the statement is the same - # as the frame / scope: - # - # def test(b=1): - # ... - - if self.statement() is myframe and myframe.parent: - myframe = myframe.parent.frame() - if not myframe is frame or self is frame: - return stmts - mystmt = self.statement() - # line filtering if we are in the same frame - # - # take care node may be missing lineno information (this is the case for - # nodes inserted for living objects) - if myframe is frame and mystmt.fromlineno is not None: - assert mystmt.fromlineno is not None, mystmt - mylineno = mystmt.fromlineno + offset - else: - # disabling lineno filtering - mylineno = 0 - _stmts = [] - _stmt_parents = [] - for node in stmts: - stmt = node.statement() - # line filtering is on and we have reached our location, break - if mylineno > 0 and stmt.fromlineno > mylineno: - break - assert hasattr(node, 'ass_type'), (node, node.scope(), - node.scope().locals) - ass_type = node.ass_type() - - if node.has_base(self): - break - - _stmts, done = ass_type._get_filtered_stmts(self, node, _stmts, mystmt) - if done: - break - - optional_assign = ass_type.optional_assign - if optional_assign and ass_type.parent_of(self): - # we are inside a loop, loop var assigment is hidding previous - # assigment - _stmts = [node] - _stmt_parents = [stmt.parent] - continue - - # XXX comment various branches below!!! - try: - pindex = _stmt_parents.index(stmt.parent) - except ValueError: - pass - else: - # we got a parent index, this means the currently visited node - # is at the same block level as a previously visited node - if _stmts[pindex].ass_type().parent_of(ass_type): - # both statements are not at the same block level - continue - # if currently visited node is following previously considered - # assignement and both are not exclusive, we can drop the - # previous one. For instance in the following code :: - # - # if a: - # x = 1 - # else: - # x = 2 - # print x - # - # we can't remove neither x = 1 nor x = 2 when looking for 'x' - # of 'print x'; while in the following :: - # - # x = 1 - # x = 2 - # print x - # - # we can remove x = 1 when we see x = 2 - # - # moreover, on loop assignment types, assignment won't - # necessarily be done if the loop has no iteration, so we don't - # want to clear previous assigments if any (hence the test on - # optional_assign) - if not (optional_assign or are_exclusive(_stmts[pindex], node)): - del _stmt_parents[pindex] - del _stmts[pindex] - if isinstance(node, AssName): - if not optional_assign and stmt.parent is mystmt.parent: - _stmts = [] - _stmt_parents = [] - elif isinstance(node, DelName): - _stmts = [] - _stmt_parents = [] - continue - if not are_exclusive(self, node): - _stmts.append(node) - _stmt_parents.append(stmt.parent) - return _stmts - -# Name classes - -class AssName(LookupMixIn, ParentAssignTypeMixin, NodeNG): - """class representing an AssName node""" - - -class DelName(LookupMixIn, ParentAssignTypeMixin, NodeNG): - """class representing a DelName node""" - - -class Name(LookupMixIn, NodeNG): - """class representing a Name node""" - - - - -##################### node classes ######################################## - -class Arguments(NodeNG, AssignTypeMixin): - """class representing an Arguments node""" - if PY3K: - # Python 3.4+ uses a different approach regarding annotations, - # each argument is a new class, _ast.arg, which exposes an - # 'annotation' attribute. In astroid though, arguments are exposed - # as is in the Arguments node and the only way to expose annotations - # is by using something similar with Python 3.3: - # - we expose 'varargannotation' and 'kwargannotation' of annotations - # of varargs and kwargs. - # - we expose 'annotation', a list with annotations for - # for each normal argument. If an argument doesn't have an - # annotation, its value will be None. - - _astroid_fields = ('args', 'defaults', 'kwonlyargs', - 'kw_defaults', 'annotations', - 'varargannotation', 'kwargannotation') - annotations = None - varargannotation = None - kwargannotation = None - else: - _astroid_fields = ('args', 'defaults', 'kwonlyargs', 'kw_defaults') - args = None - defaults = None - kwonlyargs = None - kw_defaults = None - - def __init__(self, vararg=None, kwarg=None): - self.vararg = vararg - self.kwarg = kwarg - - def _infer_name(self, frame, name): - if self.parent is frame: - return name - return None - - @cachedproperty - def fromlineno(self): - lineno = super(Arguments, self).fromlineno - return max(lineno, self.parent.fromlineno) - - def format_args(self): - """return arguments formatted as string""" - result = [] - if self.args: - result.append(_format_args(self.args, self.defaults)) - if self.vararg: - result.append('*%s' % self.vararg) - if self.kwarg: - result.append('**%s' % self.kwarg) - if self.kwonlyargs: - if not self.vararg: - result.append('*') - result.append(_format_args(self.kwonlyargs, self.kw_defaults)) - return ', '.join(result) - - def default_value(self, argname): - """return the default value for an argument - - :raise `NoDefault`: if there is no default value defined - """ - i = _find_arg(argname, self.args)[0] - if i is not None: - idx = i - (len(self.args) - len(self.defaults)) - if idx >= 0: - return self.defaults[idx] - i = _find_arg(argname, self.kwonlyargs)[0] - if i is not None and self.kw_defaults[i] is not None: - return self.kw_defaults[i] - raise NoDefault() - - def is_argument(self, name): - """return True if the name is defined in arguments""" - if name == self.vararg: - return True - if name == self.kwarg: - return True - return self.find_argname(name, True)[1] is not None - - def find_argname(self, argname, rec=False): - """return index and Name node with given name""" - if self.args: # self.args may be None in some cases (builtin function) - return _find_arg(argname, self.args, rec) - return None, None - - def get_children(self): - """override get_children to skip over None elements in kw_defaults""" - for child in super(Arguments, self).get_children(): - if child is not None: - yield child - - -def _find_arg(argname, args, rec=False): - for i, arg in enumerate(args): - if isinstance(arg, Tuple): - if rec: - found = _find_arg(argname, arg.elts) - if found[0] is not None: - return found - elif arg.name == argname: - return i, arg - return None, None - - -def _format_args(args, defaults=None): - values = [] - if args is None: - return '' - if defaults is not None: - default_offset = len(args) - len(defaults) - for i, arg in enumerate(args): - if isinstance(arg, Tuple): - values.append('(%s)' % _format_args(arg.elts)) - else: - values.append(arg.name) - if defaults is not None and i >= default_offset: - if defaults[i-default_offset] is not None: - values[-1] += '=' + defaults[i-default_offset].as_string() - return ', '.join(values) - - -class AssAttr(NodeNG, ParentAssignTypeMixin): - """class representing an AssAttr node""" - _astroid_fields = ('expr',) - expr = None - -class Assert(Statement): - """class representing an Assert node""" - _astroid_fields = ('test', 'fail',) - test = None - fail = None - -class Assign(Statement, AssignTypeMixin): - """class representing an Assign node""" - _astroid_fields = ('targets', 'value',) - targets = None - value = None - -class AugAssign(Statement, AssignTypeMixin): - """class representing an AugAssign node""" - _astroid_fields = ('target', 'value',) - target = None - value = None - -class Backquote(NodeNG): - """class representing a Backquote node""" - _astroid_fields = ('value',) - value = None - -class BinOp(NodeNG): - """class representing a BinOp node""" - _astroid_fields = ('left', 'right',) - left = None - right = None - -class BoolOp(NodeNG): - """class representing a BoolOp node""" - _astroid_fields = ('values',) - values = None - -class Break(Statement): - """class representing a Break node""" - - -class CallFunc(NodeNG): - """class representing a CallFunc node""" - _astroid_fields = ('func', 'args', 'starargs', 'kwargs') - func = None - args = None - starargs = None - kwargs = None - - def __init__(self): - self.starargs = None - self.kwargs = None - -class Compare(NodeNG): - """class representing a Compare node""" - _astroid_fields = ('left', 'ops',) - left = None - ops = None - - def get_children(self): - """override get_children for tuple fields""" - yield self.left - for _, comparator in self.ops: - yield comparator # we don't want the 'op' - - def last_child(self): - """override last_child""" - # XXX maybe if self.ops: - return self.ops[-1][1] - #return self.left - -class Comprehension(NodeNG): - """class representing a Comprehension node""" - _astroid_fields = ('target', 'iter', 'ifs') - target = None - iter = None - ifs = None - - optional_assign = True - def ass_type(self): - return self - - def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt): - """method used in filter_stmts""" - if self is mystmt: - if isinstance(lookup_node, (Const, Name)): - return [lookup_node], True - - elif self.statement() is mystmt: - # original node's statement is the assignment, only keeps - # current node (gen exp, list comp) - - return [node], True - - return stmts, False - - -class Const(NodeNG, Instance): - """represent a constant node like num, str, bool, None, bytes""" - - def __init__(self, value=None): - self.value = value - - def getitem(self, index, context=None): - if isinstance(self.value, six.string_types): - return Const(self.value[index]) - raise TypeError('%r (value=%s)' % (self, self.value)) - - def has_dynamic_getattr(self): - return False - - def itered(self): - if isinstance(self.value, six.string_types): - return self.value - raise TypeError() - - def pytype(self): - return self._proxied.qname() - - -class Continue(Statement): - """class representing a Continue node""" - - -class Decorators(NodeNG): - """class representing a Decorators node""" - _astroid_fields = ('nodes',) - nodes = None - - def __init__(self, nodes=None): - self.nodes = nodes - - def scope(self): - # skip the function node to go directly to the upper level scope - return self.parent.parent.scope() - -class DelAttr(NodeNG, ParentAssignTypeMixin): - """class representing a DelAttr node""" - _astroid_fields = ('expr',) - expr = None - - -class Delete(Statement, AssignTypeMixin): - """class representing a Delete node""" - _astroid_fields = ('targets',) - targets = None - - -class Dict(NodeNG, Instance): - """class representing a Dict node""" - _astroid_fields = ('items',) - - 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.items()] - - def pytype(self): - return '%s.dict' % BUILTINS - - def get_children(self): - """get children of a Dict node""" - # overrides get_children - for key, value in self.items: - yield key - yield value - - def last_child(self): - """override last_child""" - if self.items: - return self.items[-1][1] - return None - - def itered(self): - return self.items[::2] - - def getitem(self, lookup_key, context=None): - for key, value in self.items: - for inferedkey in key.infer(context): - if inferedkey is YES: - continue - if isinstance(inferedkey, Const) \ - and inferedkey.value == lookup_key: - return value - # This should raise KeyError, but all call sites only catch - # IndexError. Let's leave it like that for now. - raise IndexError(lookup_key) - - -class Discard(Statement): - """class representing a Discard node""" - _astroid_fields = ('value',) - value = None - - -class Ellipsis(NodeNG): - """class representing an Ellipsis node""" - - -class EmptyNode(NodeNG): - """class representing an EmptyNode node""" - - -class ExceptHandler(Statement, AssignTypeMixin): - """class representing an ExceptHandler node""" - _astroid_fields = ('type', 'name', 'body',) - type = None - name = None - body = None - - @cachedproperty - def blockstart_tolineno(self): - if self.name: - return self.name.tolineno - elif self.type: - return self.type.tolineno - else: - return self.lineno - - def catch(self, exceptions): - if self.type is None or exceptions is None: - return True - for node in self.type.nodes_of_class(Name): - if node.name in exceptions: - return True - - -class Exec(Statement): - """class representing an Exec node""" - _astroid_fields = ('expr', 'globals', 'locals',) - expr = None - globals = None - locals = None - - -class ExtSlice(NodeNG): - """class representing an ExtSlice node""" - _astroid_fields = ('dims',) - dims = None - -class For(BlockRangeMixIn, AssignTypeMixin, Statement): - """class representing a For node""" - _astroid_fields = ('target', 'iter', 'body', 'orelse',) - target = None - iter = None - body = None - orelse = None - - optional_assign = True - @cachedproperty - def blockstart_tolineno(self): - return self.iter.tolineno - - -class From(FromImportMixIn, Statement): - """class representing a From node""" - - def __init__(self, fromname, names, level=0): - self.modname = fromname - self.names = names - self.level = level - -class Getattr(NodeNG): - """class representing a Getattr node""" - _astroid_fields = ('expr',) - expr = None - - -class Global(Statement): - """class representing a Global node""" - - def __init__(self, names): - self.names = names - - def _infer_name(self, frame, name): - return name - - -class If(BlockRangeMixIn, Statement): - """class representing an If node""" - _astroid_fields = ('test', 'body', 'orelse') - test = None - body = None - orelse = None - - @cachedproperty - def blockstart_tolineno(self): - return self.test.tolineno - - def block_range(self, lineno): - """handle block line numbers range for if statements""" - if lineno == self.body[0].fromlineno: - return lineno, lineno - if lineno <= self.body[-1].tolineno: - return lineno, self.body[-1].tolineno - return self._elsed_block_range(lineno, self.orelse, - self.body[0].fromlineno - 1) - - -class IfExp(NodeNG): - """class representing an IfExp node""" - _astroid_fields = ('test', 'body', 'orelse') - test = None - body = None - orelse = None - - -class Import(FromImportMixIn, Statement): - """class representing an Import node""" - - -class Index(NodeNG): - """class representing an Index node""" - _astroid_fields = ('value',) - value = None - - -class Keyword(NodeNG): - """class representing a Keyword node""" - _astroid_fields = ('value',) - value = None - - -class List(NodeNG, Instance, ParentAssignTypeMixin): - """class representing a List node""" - _astroid_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 '%s.list' % BUILTINS - - def getitem(self, index, context=None): - return self.elts[index] - - def itered(self): - return self.elts - - -class Nonlocal(Statement): - """class representing a Nonlocal node""" - - def __init__(self, names): - self.names = names - - def _infer_name(self, frame, name): - return name - - -class Pass(Statement): - """class representing a Pass node""" - - -class Print(Statement): - """class representing a Print node""" - _astroid_fields = ('dest', 'values',) - dest = None - values = None - - -class Raise(Statement): - """class representing a Raise node""" - exc = None - if sys.version_info < (3, 0): - _astroid_fields = ('exc', 'inst', 'tback') - inst = None - tback = None - else: - _astroid_fields = ('exc', 'cause') - exc = None - cause = None - - def raises_not_implemented(self): - if not self.exc: - return - for name in self.exc.nodes_of_class(Name): - if name.name == 'NotImplementedError': - return True - - -class Return(Statement): - """class representing a Return node""" - _astroid_fields = ('value',) - value = None - - -class Set(NodeNG, Instance, ParentAssignTypeMixin): - """class representing a Set node""" - _astroid_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 '%s.set' % BUILTINS - - def itered(self): - return self.elts - - -class Slice(NodeNG): - """class representing a Slice node""" - _astroid_fields = ('lower', 'upper', 'step') - lower = None - upper = None - step = None - -class Starred(NodeNG, ParentAssignTypeMixin): - """class representing a Starred node""" - _astroid_fields = ('value',) - value = None - - -class Subscript(NodeNG): - """class representing a Subscript node""" - _astroid_fields = ('value', 'slice') - value = None - slice = None - - -class TryExcept(BlockRangeMixIn, Statement): - """class representing a TryExcept node""" - _astroid_fields = ('body', 'handlers', 'orelse',) - body = None - handlers = None - orelse = None - - def _infer_name(self, frame, name): - return name - - def block_range(self, lineno): - """handle block line numbers range for try/except statements""" - last = None - for exhandler in self.handlers: - if exhandler.type and lineno == exhandler.type.fromlineno: - return lineno, lineno - if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno: - return lineno, exhandler.body[-1].tolineno - if last is None: - last = exhandler.body[0].fromlineno - 1 - return self._elsed_block_range(lineno, self.orelse, last) - - -class TryFinally(BlockRangeMixIn, Statement): - """class representing a TryFinally node""" - _astroid_fields = ('body', 'finalbody',) - body = None - finalbody = None - - def block_range(self, lineno): - """handle block line numbers range for try/finally statements""" - child = self.body[0] - # py2.5 try: except: finally: - if (isinstance(child, TryExcept) and child.fromlineno == self.fromlineno - and lineno > self.fromlineno and lineno <= child.tolineno): - return child.block_range(lineno) - return self._elsed_block_range(lineno, self.finalbody) - - -class Tuple(NodeNG, Instance, ParentAssignTypeMixin): - """class representing a Tuple node""" - _astroid_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 '%s.tuple' % BUILTINS - - def getitem(self, index, context=None): - return self.elts[index] - - def itered(self): - return self.elts - - -class UnaryOp(NodeNG): - """class representing an UnaryOp node""" - _astroid_fields = ('operand',) - operand = None - - -class While(BlockRangeMixIn, Statement): - """class representing a While node""" - _astroid_fields = ('test', 'body', 'orelse',) - test = None - body = None - orelse = None - - @cachedproperty - def blockstart_tolineno(self): - return self.test.tolineno - - def block_range(self, lineno): - """handle block line numbers range for for and while statements""" - return self. _elsed_block_range(lineno, self.orelse) - - -class With(BlockRangeMixIn, AssignTypeMixin, Statement): - """class representing a With node""" - _astroid_fields = ('items', 'body') - items = None - body = None - - @cachedproperty - def blockstart_tolineno(self): - return self.items[-1][0].tolineno - - def get_children(self): - for expr, var in self.items: - yield expr - if var: - yield var - for elt in self.body: - yield elt - -class Yield(NodeNG): - """class representing a Yield node""" - _astroid_fields = ('value',) - value = None - -class YieldFrom(Yield): - """ Class representing a YieldFrom node. """ - -# constants ############################################################## - -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 astroid node for a python value""" - # XXX we should probably be stricter here and only consider stuff in - # CONST_CLS or do better treatment: in case where value is not in CONST_CLS, - # we should rather recall the builder on this value than returning an empty - # node (another option being that const_factory shouldn't be called with something - # not in CONST_CLS) - assert not isinstance(value, NodeNG) - try: - return CONST_CLS[value.__class__](value) - except (KeyError, AttributeError): - node = EmptyNode() - node.object = value - return node diff --git a/nodes.py b/nodes.py deleted file mode 100644 index 263ab47..0000000 --- a/nodes.py +++ /dev/null @@ -1,73 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -""" -on all nodes : - .is_statement, returning true if the node should be considered as a - statement node - .root(), returning the root node of the tree (i.e. a Module) - .previous_sibling(), returning previous sibling statement node - .next_sibling(), returning next sibling statement node - .statement(), returning the first parent node marked as statement node - .frame(), returning the first node defining a new local scope (i.e. - Module, Function or Class) - .set_local(name, node), define an identifier on the first parent frame, - with the node defining it. This is used by the astroid builder and should not - be used from out there. - -on From and Import : - .real_name(name), - - -""" - -__docformat__ = "restructuredtext en" - -from astroid.node_classes import Arguments, AssAttr, Assert, Assign, \ - AssName, AugAssign, Backquote, BinOp, BoolOp, Break, CallFunc, Compare, \ - Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, \ - Dict, Discard, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, \ - From, Getattr, Global, If, IfExp, Import, Index, Keyword, \ - List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, \ - TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, YieldFrom, \ - const_factory -from astroid.scoped_nodes import Module, GenExpr, Lambda, DictComp, \ - ListComp, SetComp, Function, Class - -ALL_NODE_CLASSES = ( - Arguments, AssAttr, Assert, Assign, AssName, AugAssign, - Backquote, BinOp, BoolOp, Break, - CallFunc, Class, Compare, Comprehension, Const, Continue, - Decorators, DelAttr, DelName, Delete, - Dict, DictComp, Discard, - Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, - For, From, Function, - Getattr, GenExpr, Global, - If, IfExp, Import, Index, - Keyword, - Lambda, List, ListComp, - Name, Nonlocal, - Module, - Pass, Print, - Raise, Return, - Set, SetComp, Slice, Starred, Subscript, - TryExcept, TryFinally, Tuple, - UnaryOp, - While, With, - Yield, YieldFrom - ) - diff --git a/protocols.py b/protocols.py deleted file mode 100644 index 4dd515f..0000000 --- a/protocols.py +++ /dev/null @@ -1,361 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""this module contains a set of functions to handle python protocols for nodes -where it makes sense. -""" - -__doctype__ = "restructuredtext en" - -from astroid.exceptions import InferenceError, NoDefault, NotFoundError -from astroid.node_classes import unpack_infer -from astroid.bases import InferenceContext, \ - raise_if_nothing_infered, yes_if_nothing_infered, Instance, YES -from astroid.nodes import const_factory -from astroid import nodes - -BIN_OP_METHOD = {'+': '__add__', - '-': '__sub__', - '/': '__div__', - '//': '__floordiv__', - '*': '__mul__', - '**': '__power__', - '%': '__mod__', - '&': '__and__', - '|': '__or__', - '^': '__xor__', - '<<': '__lshift__', - '>>': '__rshift__', - } - -UNARY_OP_METHOD = {'+': '__pos__', - '-': '__neg__', - '~': '__invert__', - 'not': None, # XXX not '__nonzero__' - } - -# unary operations ############################################################ - -def tl_infer_unary_op(self, operator): - if operator == 'not': - return const_factory(not bool(self.elts)) - raise TypeError() # XXX log unsupported operation -nodes.Tuple.infer_unary_op = tl_infer_unary_op -nodes.List.infer_unary_op = tl_infer_unary_op - - -def dict_infer_unary_op(self, operator): - if operator == 'not': - return const_factory(not bool(self.items)) - raise TypeError() # XXX log unsupported operation -nodes.Dict.infer_unary_op = dict_infer_unary_op - - -def const_infer_unary_op(self, operator): - if operator == 'not': - return const_factory(not self.value) - # XXX log potentially raised TypeError - elif operator == '+': - return const_factory(+self.value) - else: # operator == '-': - return const_factory(-self.value) -nodes.Const.infer_unary_op = const_infer_unary_op - - -# binary operations ########################################################### - -BIN_OP_IMPL = {'+': lambda a, b: a + b, - '-': lambda a, b: a - b, - '/': lambda a, b: a / b, - '//': lambda a, b: a // b, - '*': lambda a, b: a * b, - '**': lambda a, b: a ** b, - '%': lambda a, b: a % b, - '&': lambda a, b: a & b, - '|': lambda a, b: a | b, - '^': lambda a, b: a ^ b, - '<<': lambda a, b: a << b, - '>>': lambda a, b: a >> b, - } -for key, impl in list(BIN_OP_IMPL.items()): - BIN_OP_IMPL[key+'='] = impl - -def const_infer_binary_op(self, operator, other, context): - for other in other.infer(context): - if isinstance(other, nodes.Const): - try: - impl = BIN_OP_IMPL[operator] - - try: - yield const_factory(impl(self.value, other.value)) - except Exception: - # ArithmeticError is not enough: float >> float is a TypeError - # TODO : let pylint know about the problem - pass - except TypeError: - # XXX log TypeError - continue - elif other is YES: - yield other - else: - try: - for val in other.infer_binary_op(operator, self, context): - yield val - except AttributeError: - yield YES -nodes.Const.infer_binary_op = yes_if_nothing_infered(const_infer_binary_op) - - -def tl_infer_binary_op(self, operator, other, context): - for other in other.infer(context): - if isinstance(other, self.__class__) and operator == '+': - node = self.__class__() - elts = [n for elt in self.elts for n in elt.infer(context) - if not n is YES] - elts += [n for elt in other.elts for n in elt.infer(context) - if not n is YES] - node.elts = elts - yield node - elif isinstance(other, nodes.Const) and operator == '*': - if not isinstance(other.value, int): - yield YES - continue - node = self.__class__() - elts = [n for elt in self.elts for n in elt.infer(context) - if not n is YES] * other.value - node.elts = elts - yield node - elif isinstance(other, Instance) and not isinstance(other, nodes.Const): - yield YES - # XXX else log TypeError -nodes.Tuple.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op) -nodes.List.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op) - - -def dict_infer_binary_op(self, operator, other, context): - for other in other.infer(context): - if isinstance(other, Instance) and isinstance(other._proxied, nodes.Class): - yield YES - # XXX else log TypeError -nodes.Dict.infer_binary_op = yes_if_nothing_infered(dict_infer_binary_op) - -def instance_infer_binary_op(self, operator, other, context): - try: - methods = self.getattr(BIN_OP_METHOD[operator]) - except (NotFoundError, KeyError): - # Unknown operator - yield YES - else: - for method in methods: - if not isinstance(method, nodes.Function): - continue - for result in method.infer_call_result(self, context): - if result is not YES: - yield result - # We are interested only in the first infered method, - # don't go looking in the rest of the methods of the ancestors. - break - -Instance.infer_binary_op = yes_if_nothing_infered(instance_infer_binary_op) - - -# assignment ################################################################## - -"""the assigned_stmts method is responsible to return the assigned statement -(e.g. not inferred) according to the assignment type. - -The `asspath` argument is used to record the lhs path of the original node. -For instance if we want assigned statements for 'c' in 'a, (b,c)', asspath -will be [1, 1] once arrived to the Assign node. - -The `context` argument is the current inference context which should be given -to any intermediary inference necessary. -""" - -def _resolve_looppart(parts, asspath, context): - """recursive function to resolve multiple assignments on loops""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if part is YES: - continue - # XXX handle __iter__ and log potentially detected errors - if not hasattr(part, 'itered'): - continue - try: - itered = part.itered() - except TypeError: - continue # XXX log error - for stmt in itered: - try: - assigned = stmt.getitem(index, context) - except (AttributeError, IndexError): - continue - except TypeError: # stmt is unsubscriptable Const - continue - if not asspath: - # we achieved to resolved the assignment path, - # don't infer the last part - yield assigned - elif assigned is YES: - break - else: - # we are not yet on the last part of the path - # search on each possibly inferred value - try: - for infered in _resolve_looppart(assigned.infer(context), - asspath, context): - yield infered - except InferenceError: - break - - -def for_assigned_stmts(self, node, context=None, asspath=None): - if asspath is None: - for lst in self.iter.infer(context): - if isinstance(lst, (nodes.Tuple, nodes.List)): - for item in lst.elts: - yield item - else: - for infered in _resolve_looppart(self.iter.infer(context), - asspath, context): - yield infered - -nodes.For.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts) -nodes.Comprehension.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts) - - -def mulass_assigned_stmts(self, node, context=None, asspath=None): - if asspath is None: - asspath = [] - asspath.insert(0, self.elts.index(node)) - return self.parent.assigned_stmts(self, context, asspath) -nodes.Tuple.assigned_stmts = mulass_assigned_stmts -nodes.List.assigned_stmts = mulass_assigned_stmts - - -def assend_assigned_stmts(self, context=None): - return self.parent.assigned_stmts(self, context=context) -nodes.AssName.assigned_stmts = assend_assigned_stmts -nodes.AssAttr.assigned_stmts = assend_assigned_stmts - - -def _arguments_infer_argname(self, name, context): - # arguments information may be missing, in which case we can't do anything - # more - if not (self.args or self.vararg or self.kwarg): - yield YES - return - # first argument of instance/class method - if self.args and getattr(self.args[0], 'name', None) == name: - functype = self.parent.type - if functype == 'method': - yield Instance(self.parent.parent.frame()) - return - if functype == 'classmethod': - yield self.parent.parent.frame() - return - if name == self.vararg: - vararg = const_factory(()) - vararg.parent = self - yield vararg - return - if name == self.kwarg: - kwarg = const_factory({}) - kwarg.parent = self - yield kwarg - return - # if there is a default value, yield it. And then yield YES to reflect - # we can't guess given argument value - try: - if context is None: - context = InferenceContext() - for infered in self.default_value(name).infer(context): - yield infered - yield YES - except NoDefault: - yield YES - - -def arguments_assigned_stmts(self, node, context, asspath=None): - if context.callcontext: - # reset call context/name - callcontext = context.callcontext - return callcontext.infer_argument(self.parent, node.name, context) - return _arguments_infer_argname(self, node.name, context) -nodes.Arguments.assigned_stmts = arguments_assigned_stmts - - -def assign_assigned_stmts(self, node, context=None, asspath=None): - if not asspath: - yield self.value - return - for infered in _resolve_asspart(self.value.infer(context), asspath, context): - yield infered -nodes.Assign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) -nodes.AugAssign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts) - - -def _resolve_asspart(parts, asspath, context): - """recursive function to resolve multiple assignments""" - asspath = asspath[:] - index = asspath.pop(0) - for part in parts: - if hasattr(part, 'getitem'): - try: - assigned = part.getitem(index, context) - # XXX raise a specific exception to avoid potential hiding of - # unexpected exception ? - except (TypeError, IndexError): - return - if not asspath: - # we achieved to resolved the assignment path, don't infer the - # last part - yield assigned - elif assigned is YES: - return - else: - # we are not yet on the last part of the path search on each - # possibly inferred value - try: - for infered in _resolve_asspart(assigned.infer(context), - asspath, context): - yield infered - except InferenceError: - return - - -def excepthandler_assigned_stmts(self, node, context=None, asspath=None): - for assigned in unpack_infer(self.type): - if isinstance(assigned, nodes.Class): - assigned = Instance(assigned) - yield assigned -nodes.ExceptHandler.assigned_stmts = raise_if_nothing_infered(excepthandler_assigned_stmts) - - -def with_assigned_stmts(self, node, context=None, asspath=None): - if asspath is None: - for _, vars in self.items: - if vars is None: - continue - for lst in vars.infer(context): - if isinstance(lst, (nodes.Tuple, nodes.List)): - for item in lst.nodes: - yield item -nodes.With.assigned_stmts = raise_if_nothing_infered(with_assigned_stmts) - - diff --git a/raw_building.py b/raw_building.py deleted file mode 100644 index 08f6af6..0000000 --- a/raw_building.py +++ /dev/null @@ -1,364 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""this module contains a set of functions to create astroid trees from scratch -(build_* functions) or from living object (object_build_* functions) -""" - -__docformat__ = "restructuredtext en" - -import sys -from os.path import abspath -from inspect import (getargspec, isdatadescriptor, isfunction, ismethod, - ismethoddescriptor, isclass, isbuiltin, ismodule) -import six - -from astroid.node_classes import CONST_CLS -from astroid.nodes import (Module, Class, Const, const_factory, From, - Function, EmptyNode, Name, Arguments) -from astroid.bases import BUILTINS, Generator -from astroid.manager import AstroidManager -MANAGER = AstroidManager() - -_CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types - -def _io_discrepancy(member): - # _io module names itself `io`: http://bugs.python.org/issue18602 - member_self = getattr(member, '__self__', None) - return (member_self and - ismodule(member_self) and - member_self.__name__ == '_io' and - member.__module__ == 'io') - -def _attach_local_node(parent, node, name): - node.name = name # needed by add_local_node - parent.add_local_node(node) - -_marker = object() - -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 = EmptyNode() - enode.object = object - _attach_local_node(node, enode, name) - -def _has_underlying_object(self): - return hasattr(self, 'object') and self.object is not _marker - -EmptyNode.has_underlying_object = _has_underlying_object - -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, 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 = From(modname, [(membername, None)]) - _attach_local_node(node, from_node, membername) - - -def build_module(name, doc=None): - """create and initialize a astroid Module node""" - 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 astroid Class node""" - node = Class(name, doc) - for base in basenames: - basenode = Name() - basenode.name = base - node.bases.append(basenode) - basenode.parent = node - return node - -def build_function(name, args=None, defaults=None, flag=0, doc=None): - """create and initialize a astroid Function node""" - args, defaults = args or [], defaults or [] - # first argument is now a list of decorators - func = Function(name, doc) - func.args = argsnode = Arguments() - argsnode.args = [] - for arg in args: - argsnode.args.append(Name()) - argsnode.args[-1].name = arg - argsnode.args[-1].parent = argsnode - argsnode.defaults = [] - for default in defaults: - argsnode.defaults.append(const_factory(default)) - argsnode.defaults[-1].parent = argsnode - argsnode.kwarg = None - argsnode.vararg = None - argsnode.parent = func - if args: - register_arguments(func) - return func - - -def build_from_import(fromname, names): - """create and initialize an astroid From import statement""" - return From(fromname, [(name, None) for name in names]) - -def register_arguments(func, args=None): - """add given arguments to local - - args is a list that may contains nested lists - (i.e. def func(a, (b, c, d)): ...) - """ - if args is None: - args = func.args.args - if func.args.vararg: - func.set_local(func.args.vararg, func.args) - if func.args.kwarg: - func.set_local(func.args.kwarg, func.args) - for arg in args: - if isinstance(arg, Name): - func.set_local(arg.name, arg) - else: - register_arguments(func, arg.elts) - -def object_build_class(node, member, localname): - """create astroid for a living class object""" - basenames = [base.__name__ for base in member.__bases__] - return _base_class_object_build(node, member, basenames, - localname=localname) - -def object_build_function(node, member, localname): - """create astroid for a living function object""" - args, varargs, varkw, defaults = getargspec(member) - if varargs is not None: - args.append(varargs) - if varkw is not None: - args.append(varkw) - func = build_function(getattr(member, '__name__', None) or localname, args, - defaults, member.func_code.co_flags, member.__doc__) - node.add_local_node(func, localname) - -def object_build_datadescriptor(node, member, name): - """create astroid for a living data descriptor object""" - return _base_class_object_build(node, member, [], name) - -def object_build_methoddescriptor(node, member, localname): - """create astroid for a living method descriptor object""" - # FIXME get arguments ? - func = build_function(getattr(member, '__name__', None) or localname, - doc=member.__doc__) - # set node's arguments to None to notice that we have no information, not - # and empty argument list - func.args.args = None - node.add_local_node(func, localname) - -def _base_class_object_build(node, member, basenames, name=None, localname=None): - """create astroid for a living class object, with a given set of base names - (e.g. ancestors) - """ - klass = build_class(name or getattr(member, '__name__', None) or localname, - basenames, member.__doc__) - klass._newstyle = isinstance(member, type) - node.add_local_node(klass, localname) - try: - # limit the instantiation trick since it's too dangerous - # (such as infinite test execution...) - # this at least resolves common case such as Exception.args, - # OSError.errno - if issubclass(member, Exception): - instdict = member().__dict__ - else: - raise TypeError - except: - pass - else: - for name, obj in instdict.items(): - valnode = EmptyNode() - valnode.object = obj - valnode.parent = klass - valnode.lineno = 1 - klass.instance_attrs[name] = [valnode] - return klass - - - - -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. - """ - - # astroid from living objects ############################################### - - def __init__(self): - self._done = {} - self._module = None - - def inspect_build(self, module, modname=None, path=None): - """build astroid 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__ - try: - node = build_module(modname, module.__doc__) - except AttributeError: - # in jython, java modules have no __doc__ (see #109562) - node = build_module(modname) - node.file = node.path = path and abspath(path) or path - node.name = modname - MANAGER.cache_module(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 = six.get_method_function(member) - if isfunction(member): - # verify this is not an imported function - filename = getattr(six.get_function_code(member), - 'co_filename', None) - if filename is None: - assert isinstance(member, object) - object_build_methoddescriptor(node, member, name) - elif filename != getattr(self._module, '__file__', None): - attach_dummy_node(node, name, member) - else: - object_build_function(node, member, name) - elif isbuiltin(member): - if (not _io_discrepancy(member) and - 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 type(member) in _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 astroid 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 = BUILTINS - 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 - - -### astroid bootstrapping ###################################################### -Astroid_BUILDER = InspectBuilder() - -_CONST_PROXY = {} -def astroid_bootstrapping(): - """astroid 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 - from logilab.common.compat import builtins - astroid_builtin = Astroid_BUILDER.inspect_build(builtins) - for cls, node_cls in CONST_CLS.items(): - if cls is type(None): - proxy = build_class('NoneType') - proxy.parent = astroid_builtin - else: - proxy = astroid_builtin.getattr(cls.__name__)[0] - if cls in (dict, list, set, tuple): - node_cls._proxied = proxy - else: - _CONST_PROXY[cls] = proxy - -astroid_bootstrapping() - -# 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) - -from types import GeneratorType -Generator._proxied = Class(GeneratorType.__name__, GeneratorType.__doc__) -Astroid_BUILDER.object_build(Generator._proxied, GeneratorType) - diff --git a/rebuilder.py b/rebuilder.py deleted file mode 100644 index e3899fd..0000000 --- a/rebuilder.py +++ /dev/null @@ -1,905 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""this module contains utilities for rebuilding a _ast tree in -order to get a single Astroid representation -""" - -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 astroid 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', - 'comprehension': 'Comprehension', - 'Call': 'CallFunc', - 'ClassDef': 'Class', - "ListCompFor": 'Comprehension', - "GenExprFor": 'Comprehension', - 'excepthandler': 'ExceptHandler', - 'Expr': 'Discard', - 'FunctionDef': 'Function', - 'GeneratorExp': 'GenExpr', - 'ImportFrom': 'From', - 'keyword': 'Keyword', - 'Repr': 'Backquote', - } -PY3K = sys.version_info >= (3, 0) -PY34 = sys.version_info >= (3, 4) - -def _init_set_doc(node, newnode): - newnode.doc = None - try: - if isinstance(node.body[0], Discard) and isinstance(node.body[0].value, Str): - newnode.doc = node.body[0].value.s - node.body = node.body[1:] - - except IndexError: - pass # ast built from scratch - -def _lineno_parent(oldnode, newnode, parent): - newnode.parent = parent - newnode.lineno = oldnode.lineno - newnode.col_offset = oldnode.col_offset - -def _set_infos(oldnode, newnode, parent): - newnode.parent = parent - if hasattr(oldnode, 'lineno'): - newnode.lineno = oldnode.lineno - if hasattr(oldnode, 'col_offset'): - newnode.col_offset = oldnode.col_offset - -def _create_yield_node(node, parent, rebuilder, factory): - newnode = factory() - _lineno_parent(node, newnode, parent) - if node.value is not None: - newnode.value = rebuilder.visit(node.value, newnode) - return newnode - - -class TreeRebuilder(object): - """Rebuilds the _ast tree to become an Astroid tree""" - - def __init__(self, manager): - self._manager = manager - self.asscontext = None - self._global_names = [] - self._from_nodes = [] - self._delayed_assattr = [] - self._visit_meths = {} - self._transform = manager.transform - - 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 - newnode.parent = None - _init_set_doc(node, newnode) - newnode.body = [self.visit(child, newnode) for child in node.body] - return self._transform(newnode) - - def visit(self, node, parent): - cls = node.__class__ - if cls in self._visit_meths: - visit_method = self._visit_meths[cls] - 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 self._transform(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() - newnode.parent = 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.kwonlyargs = [] - newnode.kw_defaults = [] - vararg, kwarg = node.vararg, node.kwarg - # change added in 82732 (7c5c678e4164), vararg and kwarg - # are instances of `_ast.arg`, not strings - if vararg: - if PY34: - if vararg.annotation: - newnode.varargannotation = self.visit(vararg.annotation, - newnode) - vararg = vararg.arg - elif PY3K and node.varargannotation: - newnode.varargannotation = self.visit(node.varargannotation, - newnode) - if kwarg: - if PY34: - if kwarg.annotation: - newnode.kwargannotation = self.visit(kwarg.annotation, - newnode) - kwarg = kwarg.arg - elif PY3K: - if node.kwargannotation: - newnode.kwargannotation = self.visit(node.kwargannotation, - newnode) - newnode.vararg = vararg - newnode.kwarg = kwarg - # save argument names in locals: - if vararg: - newnode.parent.set_local(vararg, newnode) - if kwarg: - newnode.parent.set_local(kwarg, newnode) - 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) - 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) - if node.msg is not None: - newnode.fail = self.visit(node.msg, newnode) - 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) - # set some function or metaclass infos XXX explain ? - klass = newnode.parent.frame() - 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, new.Function): - if func_name in ('classmethod', 'staticmethod'): - meth.type = func_name - elif func_name == 'classproperty': # see lgc.decorators - meth.type = 'classmethod' - meth.extra_decorators.append(newnode.value) - except (AttributeError, KeyError): - continue - 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) - 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) - 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__] - 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__] - 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) - for child in node.keywords: - newnode.args.append(self.visit(child, newnode)) - return newnode - - def visit_class(self, node, parent): - """visit a Class node to become astroid""" - 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.parent.frame().set_local(newnode.name, newnode) - return newnode - - def visit_const(self, node, parent): - """visit a Const node by returning a fresh instance of it""" - 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 = 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)] - return newnode - - def visit_comprehension(self, node, parent): - """visit a Comprehension node by returning a fresh instance of it""" - newnode = new.Comprehension() - newnode.parent = 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] - 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 astroid.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] - 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 - 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)] - 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] - 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) - return newnode - - def visit_ellipsis(self, node, parent): - """visit an Ellipsis node by returning a fresh instance of it""" - 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 = new.EmptyNode() - _set_infos(node, newnode, parent) - 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) - 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] - 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) - 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) - return newnode - - def visit_extslice(self, node, parent): - """visit an ExtSlice node by returning a fresh instance of it""" - newnode = new.ExtSlice() - newnode.parent = parent - newnode.dims = [self.visit(dim, newnode) for dim in node.dims] - 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] - 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 or None) - _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 astroid""" - self._global_names.append({}) - 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) - if PY3K and node.returns: - newnode.returns = self.visit(node.returns, newnode) - self._global_names.pop() - frame = newnode.parent.frame() - 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, new.Name): - if decorator_expr.name in ('classmethod', 'staticmethod'): - newnode._type = decorator_expr.name - elif decorator_expr.name == 'classproperty': - newnode._type = 'classmethod' - 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] - 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 - return newnode - - def visit_global(self, node, parent): - """visit an Global node to become astroid""" - 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 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] - 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) - 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() - newnode.parent = parent - newnode.value = self.visit(node.value, newnode) - return newnode - - def visit_keyword(self, node, parent): - """visit a Keyword node by returning a fresh instance of it""" - newnode = new.Keyword() - newnode.parent = parent - newnode.arg = node.arg - newnode.value = self.visit(node.value, newnode) - 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) - 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] - 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] - 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) - 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 = new.Pass() - _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) - _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 - if node.dest is not None: - newnode.dest = self.visit(node.dest, newnode) - newnode.values = [self.visit(child, newnode) for child in node.values] - 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) - 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) - return newnode - - def visit_set(self, node, parent): - """visit a Set 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] - 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] - return newnode - - def visit_slice(self, node, parent): - """visit a Slice node by returning a fresh instance of it""" - newnode = new.Slice() - newnode.parent = 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) - 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 - 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] - 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] - 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] - 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__] - 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] - return newnode - - def visit_with(self, node, parent): - newnode = new.With() - _lineno_parent(node, newnode, parent) - expr = self.visit(node.context_expr, newnode) - self.asscontext = "Ass" - if node.optional_vars is not None: - vars = self.visit(node.optional_vars, newnode) - else: - vars = None - self.asscontext = None - newnode.items = [(expr, vars)] - newnode.body = [self.visit(child, newnode) for child in node.body] - return newnode - - def visit_yield(self, node, parent): - """visit a Yield node by returning a fresh instance of it""" - return _create_yield_node(node, parent, self, new.Yield) - -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 node is coming from py>=3.0, but we use AssName in py2.x - # XXX or we should instead introduce a Arg node in astroid ? - return self.visit_assname(node, parent, node.arg) - - def visit_nameconstant(self, node, parent): - # in Python 3.4 we have NameConstant for True / False / None - newnode = new.Const(node.value) - _set_infos(node, newnode, parent) - return newnode - - def visit_arguments(self, node, parent): - newnode = super(TreeRebuilder3k, self).visit_arguments(node, parent) - self.asscontext = "Ass" - newnode.kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs] - self.asscontext = None - newnode.kw_defaults = [self.visit(child, newnode) if child else None for child in node.kw_defaults] - newnode.annotations = [ - self.visit(arg.annotation, newnode) if arg.annotation else None - for arg in node.args] - 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) - 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] - 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) - 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) - return newnode - - def visit_try(self, node, parent): - # python 3.3 introduce a new Try node replacing TryFinally/TryExcept nodes - if node.finalbody: - newnode = new.TryFinally() - _lineno_parent(node, newnode, parent) - newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody] - if node.handlers: - excnode = new.TryExcept() - _lineno_parent(node, excnode, newnode) - excnode.body = [self.visit(child, excnode) for child in node.body] - excnode.handlers = [self.visit(child, excnode) for child in node.handlers] - excnode.orelse = [self.visit(child, excnode) for child in node.orelse] - newnode.body = [excnode] - else: - newnode.body = [self.visit(child, newnode) for child in node.body] - elif node.handlers: - 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] - return newnode - - def visit_with(self, node, parent): - if 'items' not in node._fields: - # python < 3.3 - return super(TreeRebuilder3k, self).visit_with(node, parent) - - newnode = new.With() - _lineno_parent(node, newnode, parent) - def visit_child(child): - expr = self.visit(child.context_expr, newnode) - self.asscontext = 'Ass' - if child.optional_vars: - var = self.visit(child.optional_vars, newnode) - else: - var = None - self.asscontext = None - return expr, var - newnode.items = [visit_child(child) - for child in node.items] - newnode.body = [self.visit(child, newnode) for child in node.body] - return newnode - - def visit_yieldfrom(self, node, parent): - return _create_yield_node(node, parent, self, new.YieldFrom) - - def visit_class(self, node, parent): - newnode = super(TreeRebuilder3k, self).visit_class(node, parent) - newnode._newstyle = True - for keyword in node.keywords: - if keyword.arg == 'metaclass': - newnode._metaclass = self.visit(keyword, newnode).value - break - return newnode - -if sys.version_info >= (3, 0): - TreeRebuilder = TreeRebuilder3k - - diff --git a/scoped_nodes.py b/scoped_nodes.py deleted file mode 100644 index b792bd8..0000000 --- a/scoped_nodes.py +++ /dev/null @@ -1,1304 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""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 with_statement - -__doctype__ = "restructuredtext en" - -import sys -from itertools import chain -try: - from io import BytesIO -except ImportError: - from cStringIO import StringIO as BytesIO - -import six -from logilab.common.compat import builtins -from logilab.common.decorators import cached, cachedproperty - -from astroid.exceptions import NotFoundError, \ - AstroidBuildingException, InferenceError -from astroid.node_classes import Const, DelName, DelAttr, \ - Dict, From, List, Pass, Raise, Return, Tuple, Yield, YieldFrom, \ - LookupMixIn, const_factory as cf, unpack_infer, Name, CallFunc -from astroid.bases import NodeNG, InferenceContext, Instance,\ - YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, \ - BUILTINS -from astroid.mixins import FilterStmtsMixin -from astroid.bases import Statement -from astroid.manager import AstroidManager - -ITER_METHODS = ('__iter__', '__getitem__') -PY3K = sys.version_info >= (3, 0) - - -def remove_nodes(func, cls): - def wrapper(*args, **kwargs): - nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)] - if not nodes: - raise NotFoundError() - return nodes - return wrapper - - -def function_to_method(n, klass): - if isinstance(n, Function): - if n.type == 'classmethod': - return BoundMethod(n, klass) - if n.type != 'staticmethod': - return UnboundMethod(n) - return n - -def std_special_attributes(self, name, add_locals=True): - if add_locals: - locals = self.locals - else: - locals = {} - if name == '__name__': - return [cf(self.name)] + locals.get(name, []) - if name == '__doc__': - return [cf(self.doc)] + locals.get(name, []) - if name == '__dict__': - return [Dict()] + locals.get(name, []) - raise NotFoundError(name) - -MANAGER = AstroidManager() -def builtin_lookup(name): - """lookup a name into the builtin module - return the list of matching statements and the astroid for the builtin - module - """ - builtin_astroid = MANAGER.ast_from_module(builtins) - if name == '__dict__': - return builtin_astroid, () - try: - stmts = builtin_astroid.locals[name] - except KeyError: - stmts = () - return builtin_astroid, stmts - - -# TODO move this Mixin to mixins.py; problem: 'Function' in _scope_lookup -class LocalsDictNodeNG(LookupMixIn, NodeNG): - """ this class provides locals handling common to Module, Function - and Class nodes, including a dict like interface for direct access - to locals information - """ - - # attributes below are set by the builder module or by raw factories - - # dictionary of locals with name as key and node defining the local as - # value - - def qname(self): - """return the 'qualified' name of the node, eg module.name, - module.class.name ... - """ - if self.parent is None: - return self.name - return '%s.%s' % (self.parent.frame().qname(), self.name) - - def frame(self): - """return the first parent frame node (i.e. Module, Function or Class) - """ - return self - - def scope(self): - """return the first node defining a new scope (i.e. Module, - Function, Class, Lambda but also GenExpr, DictComp and SetComp) - """ - return self - - - def _scope_lookup(self, node, name, offset=0): - """XXX method for interfacing the scope lookup""" - try: - stmts = node._filter_stmts(self.locals[name], self, offset) - except KeyError: - stmts = () - if stmts: - return self, stmts - if self.parent: # i.e. not Module - # nested scope: if parent scope is a function, that's fine - # else jump to the module - pscope = self.parent.scope() - if not pscope.is_function: - pscope = pscope.root() - return pscope.scope_lookup(node, name) - return builtin_lookup(name) # Module - - - - def set_local(self, name, stmt): - """define in locals ( is the node defining the name) - if the node is a Module node (i.e. has globals), add the name to - globals - - if the name is already defined, ignore it - """ - #assert not stmt in self.locals.get(name, ()), (self, stmt) - self.locals.setdefault(name, []).append(stmt) - - __setitem__ = set_local - - def _append_node(self, child): - """append a child, linking it in the tree""" - self.body.append(child) - child.parent = self - - def add_local_node(self, child_node, name=None): - """append a child which should alter locals to the given node""" - if name != '__class__': - # add __class__ node as a child will cause infinite recursion later! - self._append_node(child_node) - self.set_local(name or child_node.name, child_node) - - - def __getitem__(self, item): - """method from the `dict` interface returning the first node - associated with the given name in the locals dictionary - - :type item: str - :param item: the name of the locally defined object - :raises KeyError: if the name is not defined - """ - return self.locals[item][0] - - def __iter__(self): - """method from the `dict` interface returning an iterator on - `self.keys()` - """ - return iter(self.keys()) - - def keys(self): - """method from the `dict` interface returning a tuple containing - locally defined names - """ - return list(self.locals.keys()) - - def values(self): - """method from the `dict` interface returning a tuple containing - locally defined nodes which are instance of `Function` or `Class` - """ - return [self[key] for key in self.keys()] - - def items(self): - """method from the `dict` interface returning a list of tuple - containing each locally defined name with its associated node, - which is an instance of `Function` or `Class` - """ - return list(zip(self.keys(), self.values())) - - - def __contains__(self, name): - return name in self.locals - has_key = __contains__ - -# Module ##################################################################### - -class Module(LocalsDictNodeNG): - _astroid_fields = ('body',) - - fromlineno = 0 - lineno = 0 - - # attributes below are set by the builder module or by raw factories - - # the file from which as been extracted the astroid representation. It may - # be None if the representation has been built from a built-in module - file = None - # Alternatively, if built from a string/bytes, this can be set - file_bytes = None - # encoding of python source file, so we can get unicode out of it (python2 - # only) - file_encoding = None - # the module name - name = None - # boolean for astroid built from source (i.e. ast) - pure_python = None - # boolean for package module - package = None - # dictionary of globals with name as key and node defining the global - # as value - globals = None - - # Future imports - future_imports = None - - # names of python special attributes (handled by getattr impl.) - special_attributes = set(('__name__', '__doc__', '__file__', '__path__', - '__dict__')) - # names of module attributes available through the global scope - scope_attrs = set(('__name__', '__doc__', '__file__', '__path__')) - - def __init__(self, name, doc, pure_python=True): - self.name = name - self.doc = doc - self.pure_python = pure_python - self.locals = self.globals = {} - self.body = [] - self.future_imports = set() - - @property - def file_stream(self): - if self.file_bytes is not None: - return BytesIO(self.file_bytes) - if self.file is not None: - return open(self.file, 'rb') - return None - - def block_range(self, lineno): - """return block line numbers. - - start from the beginning whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def scope_lookup(self, node, name, offset=0): - if name in self.scope_attrs and not name in self.locals: - try: - return self, self.getattr(name) - except NotFoundError: - return self, () - return self._scope_lookup(node, name, offset) - - def pytype(self): - return '%s.module' % BUILTINS - - def display_type(self): - return 'Module' - - def getattr(self, name, context=None, ignore_locals=False): - if name in self.special_attributes: - if name == '__file__': - return [cf(self.file)] + self.locals.get(name, []) - if name == '__path__' and self.package: - return [List()] + self.locals.get(name, []) - return std_special_attributes(self, name) - if not ignore_locals and name in self.locals: - return self.locals[name] - if self.package: - try: - return [self.import_module(name, relative_only=True)] - except AstroidBuildingException: - raise NotFoundError(name) - except SyntaxError: - 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) - - def igetattr(self, name, context=None): - """inferred getattr""" - # set lookup name since this is necessary to infer on import nodes for - # instance - if not context: - context = InferenceContext() - try: - return _infer_stmts(self.getattr(name, context), context, frame=self, lookupname=name) - except NotFoundError: - raise InferenceError(name) - - def fully_defined(self): - """return True if this module has been built from a .py file - and so contains a complete representation including the code - """ - return self.file is not None and self.file.endswith('.py') - - def statement(self): - """return the first parent node marked as statement node - consider a module as a statement... - """ - return self - - def previous_sibling(self): - """module has no sibling""" - return - - def next_sibling(self): - """module has no sibling""" - return - - if sys.version_info < (2, 8): - @cachedproperty - 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 = True - - def absolute_import_activated(self): - return self._absolute_import_activated - - def import_module(self, modname, relative_only=False, level=None): - """import the given module considering self as context""" - if relative_only and level is None: - level = 0 - absmodname = self.relative_to_absolute_name(modname, level) - try: - return MANAGER.ast_from_module_name(absmodname) - except AstroidBuildingException: - # we only want to import a sub module or package of this module, - # skip here - if relative_only: - raise - return MANAGER.ast_from_module_name(modname) - - def relative_to_absolute_name(self, modname, level): - """return the absolute module name for a relative import. - - The relative import can be implicit or explicit. - """ - # XXX this returns non sens when called on an absolute import - # like 'pylint.checkers.astroid.utils' - # XXX doesn't return absolute name if self.name isn't absolute name - if self.absolute_import_activated() and level is None: - return modname - if level: - if self.package: - level = level - 1 - package_name = self.name.rsplit('.', level)[0] - elif self.package: - package_name = self.name - else: - package_name = self.name.rsplit('.', 1)[0] - if package_name: - if not modname: - return package_name - return '%s.%s' % (package_name, modname) - return modname - - - def wildcard_import_names(self): - """return the list of imported names when this module is 'wildcard - imported' - - It doesn't include the '__builtins__' name which is added by the - current CPython implementation of wildcard imports. - """ - # take advantage of a living module if it exists - try: - living = sys.modules[self.name] - except KeyError: - pass - else: - try: - return living.__all__ - except AttributeError: - return [name for name in living.__dict__.keys() - if not name.startswith('_')] - # else lookup the astroid - # - # We separate the different steps of lookup in try/excepts - # to avoid catching too many Exceptions - default = [name for name in self.keys() if not name.startswith('_')] - try: - all = self['__all__'] - except KeyError: - return default - try: - explicit = next(all.assigned_stmts()) - except InferenceError: - return default - except AttributeError: - # not an assignment node - # XXX infer? - return default - - # Try our best to detect the exported name. - infered = [] - try: - explicit = next(explicit.infer()) - except InferenceError: - return default - if not isinstance(explicit, (Tuple, List)): - return default - - str_const = lambda node: (isinstance(node, Const) and - isinstance(node.value, six.string_types)) - for node in explicit.elts: - if str_const(node): - infered.append(node.value) - else: - try: - infered_node = next(node.infer()) - except InferenceError: - continue - if str_const(infered_node): - infered.append(infered_node.value) - return infered - - - -class ComprehensionScope(LocalsDictNodeNG): - def frame(self): - return self.parent.frame() - - scope_lookup = LocalsDictNodeNG._scope_lookup - - -class GenExpr(ComprehensionScope): - _astroid_fields = ('elt', 'generators') - - def __init__(self): - self.locals = {} - self.elt = None - self.generators = [] - - -class DictComp(ComprehensionScope): - _astroid_fields = ('key', 'value', 'generators') - - def __init__(self): - self.locals = {} - self.key = None - self.value = None - self.generators = [] - - -class SetComp(ComprehensionScope): - _astroid_fields = ('elt', 'generators') - - def __init__(self): - self.locals = {} - self.elt = None - self.generators = [] - - -class _ListComp(NodeNG): - """class representing a ListComp node""" - _astroid_fields = ('elt', 'generators') - elt = None - generators = None - -if sys.version_info >= (3, 0): - class ListComp(_ListComp, ComprehensionScope): - """class representing a ListComp node""" - def __init__(self): - self.locals = {} -else: - class ListComp(_ListComp): - """class representing a ListComp node""" - -# Function ################################################################### - -def _infer_decorator_callchain(node): - """ Detect decorator call chaining and see if the - end result is a static or a classmethod. - """ - current = node - while True: - if isinstance(current, CallFunc): - try: - current = next(current.func.infer()) - except InferenceError: - return - elif isinstance(current, Function): - if not current.parent: - return - try: - # TODO: We don't handle multiple inference results right now, - # because there's no flow to reason when the return - # is what we are looking for, a static or a class method. - result = next(current.infer_call_result(current.parent)) - if current is result: - # This will lead to an infinite loop, where a decorator - # returns itself. - return - except (StopIteration, InferenceError): - return - if isinstance(result, (Function, CallFunc)): - current = result - else: - if isinstance(result, Instance): - result = result._proxied - if isinstance(result, Class): - if (result.name == 'classmethod' and - result.root().name == BUILTINS): - return 'classmethod' - elif (result.name == 'staticmethod' and - result.root().name == BUILTINS): - return 'staticmethod' - else: - return - else: - # We aren't interested in anything else returned, - # so go back to the function type inference. - return - else: - return - -def _function_type(self): - """ - Function type, possible values are: - method, function, staticmethod, classmethod. - """ - # Can't infer that this node is decorated - # with a subclass of `classmethod` where `type` is first set, - # so do it here. - if self.decorators: - for node in self.decorators.nodes: - if isinstance(node, CallFunc): - _type = _infer_decorator_callchain(node) - if _type is None: - continue - else: - return _type - if not isinstance(node, Name): - continue - try: - for infered in node.infer(): - if not isinstance(infered, Class): - continue - for ancestor in infered.ancestors(): - if isinstance(ancestor, Class): - if (ancestor.name == 'classmethod' and - ancestor.root().name == BUILTINS): - return 'classmethod' - elif (ancestor.name == 'staticmethod' and - ancestor.root().name == BUILTINS): - return 'staticmethod' - except InferenceError: - pass - return self._type - - -class Lambda(LocalsDictNodeNG, FilterStmtsMixin): - _astroid_fields = ('args', 'body',) - name = '' - - # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod' - type = 'function' - - def __init__(self): - self.locals = {} - self.args = [] - self.body = [] - - def pytype(self): - if 'method' in self.type: - return '%s.instancemethod' % BUILTINS - return '%s.function' % BUILTINS - - def display_type(self): - if 'method' in self.type: - return 'Method' - return 'Function' - - def callable(self): - return True - - def argnames(self): - """return a list of argument names""" - if self.args.args: # maybe None with builtin functions - names = _rec_get_names(self.args.args) - else: - names = [] - if self.args.vararg: - names.append(self.args.vararg) - if self.args.kwarg: - names.append(self.args.kwarg) - return names - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - return self.body.infer(context) - - def scope_lookup(self, node, name, offset=0): - if node in self.args.defaults or node in self.args.kw_defaults: - frame = self.parent.frame() - # line offset to avoid that def func(f=func) resolve the default - # value to the defined function - offset = -1 - else: - # check this is not used in function decorators - frame = self - return frame._scope_lookup(node, name, offset) - - -class Function(Statement, Lambda): - if PY3K: - _astroid_fields = ('decorators', 'args', 'body', 'returns') - returns = None - else: - _astroid_fields = ('decorators', 'args', 'body') - - special_attributes = set(('__name__', '__doc__', '__dict__')) - is_function = True - # attributes below are set by the builder module or by raw factories - blockstart_tolineno = None - decorators = None - _type = "function" - type = cachedproperty(_function_type) - - def __init__(self, name, doc): - self.locals = {} - self.args = [] - self.body = [] - self.name = name - self.doc = doc - self.extra_decorators = [] - self.instance_attrs = {} - - @cachedproperty - def fromlineno(self): - # lineno is the line number of the first decorator, we want the def - # statement lineno - lineno = self.lineno - if self.decorators is not None: - lineno += sum(node.tolineno - node.lineno + 1 - for node in self.decorators.nodes) - - return lineno - - @cachedproperty - def blockstart_tolineno(self): - return self.args.tolineno - - def block_range(self, lineno): - """return block line numbers. - - start from the "def" position whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def getattr(self, name, context=None): - """this method doesn't look in the instance_attrs dictionary since it's - done by an Instance proxy at inference time. - """ - 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): - """return true if the function node should be considered as a method""" - # check we are defined in a Class, because this is usually expected - # (e.g. pylint...) when is_method() return True - return self.type != 'function' and isinstance(self.parent.frame(), Class) - - def decoratornames(self): - """return a list of decorator qualified names""" - result = set() - decoratornodes = [] - if self.decorators is not None: - decoratornodes += self.decorators.nodes - decoratornodes += self.extra_decorators - for decnode in decoratornodes: - for infnode in decnode.infer(): - result.add(infnode.qname()) - return result - decoratornames = cached(decoratornames) - - def is_bound(self): - """return true if the function is bound to an Instance or a class""" - return self.type == 'classmethod' - - def is_abstract(self, pass_is_abstract=True): - """Returns True if the method is abstract. - - A method is considered abstract if - - the only statement is 'raise NotImplementedError', or - - the only statement is 'pass' and pass_is_abstract is True, or - - the method is annotated with abc.astractproperty/abc.abstractmethod - """ - if self.decorators: - for node in self.decorators.nodes: - try: - infered = next(node.infer()) - except InferenceError: - continue - if infered and infered.qname() in ('abc.abstractproperty', - 'abc.abstractmethod'): - return True - - for child_node in self.body: - if isinstance(child_node, Raise): - if child_node.raises_not_implemented(): - return True - if pass_is_abstract and isinstance(child_node, Pass): - return True - return False - # empty function is the same as function with a single "pass" statement - if pass_is_abstract: - return True - - def is_generator(self): - """return true if this is a generator function""" - # XXX should be flagged, not computed - return next(self.nodes_of_class((Yield, YieldFrom), - skip_klass=(Function, Lambda)), False) - - def infer_call_result(self, caller, context=None): - """infer what a function is returning when called""" - if self.is_generator(): - yield Generator() - return - returns = self.nodes_of_class(Return, skip_klass=Function) - for returnnode in returns: - if returnnode.value is None: - yield Const(None) - else: - try: - for infered in returnnode.value.infer(context): - yield infered - except InferenceError: - yield YES - - -def _rec_get_names(args, names=None): - """return a list of all argument names""" - if names is None: - names = [] - for arg in args: - if isinstance(arg, Tuple): - _rec_get_names(arg.elts, names) - else: - names.append(arg.name) - return names - - -# Class ###################################################################### - - -def _is_metaclass(klass, seen=None): - """ Return if the given class can be - used as a metaclass. - """ - if klass.name == 'type': - return True - if seen is None: - seen = set() - for base in klass.bases: - try: - for baseobj in base.infer(): - if baseobj in seen: - continue - else: - seen.add(baseobj) - if isinstance(baseobj, Instance): - # not abstract - return False - if baseobj is YES: - continue - if baseobj is klass: - continue - if not isinstance(baseobj, Class): - continue - if baseobj._type == 'metaclass': - return True - if _is_metaclass(baseobj, seen): - return True - except InferenceError: - continue - return False - - -def _class_type(klass, ancestors=None): - """return a Class node type to differ metaclass, interface and exception - from 'regular' classes - """ - # XXX we have to store ancestors in case we have a ancestor loop - if klass._type is not None: - return klass._type - if _is_metaclass(klass): - klass._type = 'metaclass' - elif klass.name.endswith('Interface'): - klass._type = 'interface' - elif klass.name.endswith('Exception'): - klass._type = 'exception' - else: - if ancestors is None: - ancestors = set() - if klass in ancestors: - # XXX we are in loop ancestors, and have found no type - klass._type = 'class' - return 'class' - ancestors.add(klass) - for base in klass.ancestors(recurs=False): - name = _class_type(base, ancestors) - if name != 'class': - if name == 'metaclass' and not _is_metaclass(klass): - # don't propagate it if the current class - # can't be a metaclass - continue - klass._type = base.type - break - if klass._type is None: - klass._type = 'class' - return klass._type - -def _iface_hdlr(iface_node): - """a handler function used by interfaces to handle suspicious - interface nodes - """ - return True - - -class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin): - - # some of the attributes below are set by the builder module or - # by a raw factories - - # a dictionary of class instances attributes - _astroid_fields = ('decorators', 'bases', 'body') # name - - decorators = None - special_attributes = set(('__name__', '__doc__', '__dict__', '__module__', - '__bases__', '__mro__', '__subclasses__')) - blockstart_tolineno = None - - _type = None - type = property(_class_type, - doc="class'type, possible values are 'class' | " - "'metaclass' | 'interface' | 'exception'") - - def __init__(self, name, doc): - self.instance_attrs = {} - self.locals = {} - self.bases = [] - self.body = [] - self.name = name - self.doc = doc - - def _newstyle_impl(self, context=None): - if context is None: - context = InferenceContext() - if self._newstyle is not None: - return self._newstyle - for base in self.ancestors(recurs=False, context=context): - if base._newstyle_impl(context): - self._newstyle = True - break - klass = self._explicit_metaclass() - # could be any callable, we'd need to infer the result of klass(name, - # bases, dict). punt if it's not a class node. - if klass is not None and isinstance(klass, Class): - self._newstyle = klass._newstyle_impl(context) - if self._newstyle is None: - self._newstyle = False - return self._newstyle - - _newstyle = None - newstyle = property(_newstyle_impl, - doc="boolean indicating if it's a new style class" - "or not") - - @cachedproperty - def blockstart_tolineno(self): - if self.bases: - return self.bases[-1].tolineno - else: - return self.fromlineno - - def block_range(self, lineno): - """return block line numbers. - - start from the "class" position whatever the given lineno - """ - return self.fromlineno, self.tolineno - - def pytype(self): - if self.newstyle: - return '%s.type' % BUILTINS - return '%s.classobj' % BUILTINS - - def display_type(self): - return 'Class' - - def callable(self): - return True - - def _is_subtype_of(self, type_name): - if self.qname() == type_name: - return True - for anc in self.ancestors(): - if anc.qname() == type_name: - return True - - def infer_call_result(self, caller, context=None): - """infer what a class is returning when called""" - if self._is_subtype_of('%s.type' % (BUILTINS,)) and len(caller.args) == 3: - name_node = next(caller.args[0].infer()) - if (isinstance(name_node, Const) and - isinstance(name_node.value, six.string_types)): - name = name_node.value - else: - yield YES - return - result = Class(name, None) - bases = next(caller.args[1].infer()) - if isinstance(bases, (Tuple, List)): - result.bases = bases.itered() - else: - # There is currently no AST node that can represent an 'unknown' - # node (YES is not an AST node), therefore we simply return YES here - # although we know at least the name of the class. - yield YES - return - result.parent = caller.parent - yield result - else: - yield Instance(self) - - def scope_lookup(self, node, name, offset=0): - if node in self.bases: - frame = self.parent.frame() - # line offset to avoid that class A(A) resolve the ancestor to - # the defined class - offset = -1 - else: - frame = self - return frame._scope_lookup(node, name, offset) - - # list of parent class as a list of string (i.e. names as they appear - # in the class definition) XXX bw compat - def basenames(self): - return [bnode.as_string() for bnode in self.bases] - basenames = property(basenames) - - def ancestors(self, recurs=True, context=None): - """return an iterator on the node base classes in a prefixed - depth first order - - :param recurs: - boolean indicating if it should recurse or return direct - ancestors only - """ - # FIXME: should be possible to choose the resolution order - # FIXME: inference make infinite loops possible here - yielded = set([self]) - if context is None: - context = InferenceContext() - for stmt in self.bases: - try: - for baseobj in stmt.infer(context): - if not isinstance(baseobj, Class): - if isinstance(baseobj, Instance): - baseobj = baseobj._proxied - else: - # duh ? - continue - if baseobj in yielded: - continue # cf xxx above - yielded.add(baseobj) - yield baseobj - if recurs: - for grandpa in baseobj.ancestors(recurs=True, - context=context): - if grandpa in yielded: - continue # cf xxx above - yielded.add(grandpa) - yield grandpa - except InferenceError: - # XXX log error ? - continue - - def local_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their locals - """ - for astroid in self.ancestors(context=context): - if name in astroid: - yield astroid - - def instance_attr_ancestors(self, name, context=None): - """return an iterator on astroid representation of parent classes - which have defined in their instance attribute dictionary - """ - for astroid in self.ancestors(context=context): - if name in astroid.instance_attrs: - yield astroid - - def has_base(self, node): - return node in self.bases - - def local_attr(self, name, context=None): - """return the list of assign node associated to name in this class - locals or in its parents - - :raises `NotFoundError`: - if no attribute with this name has been find in this class or - its parent classes - """ - try: - return self.locals[name] - except KeyError: - # get if from the first parent implementing it if any - for class_node in self.local_attr_ancestors(name, context): - return class_node.locals[name] - raise NotFoundError(name) - local_attr = remove_nodes(local_attr, DelAttr) - - def instance_attr(self, name, context=None): - """return the astroid nodes associated to name in this class instance - attributes dictionary and in its parents - - :raises `NotFoundError`: - if no attribute with this name has been find in this class or - its parent classes - """ - # Return a copy, so we don't modify self.instance_attrs, - # which could lead to infinite loop. - values = list(self.instance_attrs.get(name, [])) - # get all values from parents - for class_node in self.instance_attr_ancestors(name, context): - values += class_node.instance_attrs[name] - if not values: - raise NotFoundError(name) - return values - instance_attr = remove_nodes(instance_attr, DelAttr) - - def instanciate_class(self): - """return Instance of Class node, else return self""" - return Instance(self) - - def getattr(self, name, context=None): - """this method doesn't look in the instance_attrs dictionary since it's - done by an Instance proxy at inference time. - - It may return a YES object if the attribute has not been actually - found but a __getattr__ or __getattribute__ method is defined - """ - values = self.locals.get(name, []) - if name in self.special_attributes: - if name == '__module__': - return [cf(self.root().qname())] + values - # FIXME: do we really need the actual list of ancestors? - # returning [Tuple()] + values don't break any test - # this is ticket http://www.logilab.org/ticket/52785 - # XXX need proper meta class handling + MRO implementation - if name == '__bases__' or (name == '__mro__' and self.newstyle): - node = Tuple() - node.items = self.ancestors(recurs=True, context=context) - return [node] + values - return std_special_attributes(self, name) - # don't modify the list in self.locals! - values = list(values) - for classnode in self.ancestors(recurs=True, context=context): - values += classnode.locals.get(name, []) - if not values: - raise NotFoundError(name) - return values - - def igetattr(self, name, context=None): - """inferred getattr, need special treatment in class to handle - descriptors - """ - # set lookup name since this is necessary to infer on import nodes for - # instance - if not context: - context = InferenceContext() - try: - for infered in _infer_stmts(self.getattr(name, context), context, - frame=self, lookupname=name): - # yield YES object instead of descriptors when necessary - if not isinstance(infered, Const) and isinstance(infered, Instance): - try: - infered._proxied.getattr('__get__', context) - except NotFoundError: - yield infered - else: - yield YES - else: - yield function_to_method(infered, self) - except NotFoundError: - if not name.startswith('__') and self.has_dynamic_getattr(context): - # class handle some dynamic attributes, return a YES object - yield YES - else: - raise InferenceError(name) - - def has_dynamic_getattr(self, context=None): - """return True if the class has a custom __getattr__ or - __getattribute__ method - """ - # need to explicitly handle optparse.Values (setattr is not detected) - if self.name == 'Values' and self.root().name == 'optparse': - return True - try: - self.getattr('__getattr__', context) - return True - except NotFoundError: - #if self.newstyle: XXX cause an infinite recursion error - try: - getattribute = self.getattr('__getattribute__', context)[0] - if getattribute.root().name != BUILTINS: - # class has a custom __getattribute__ defined - return True - except NotFoundError: - pass - return False - - def methods(self): - """return an iterator on all methods defined in the class and - its ancestors - """ - done = {} - for astroid in chain(iter((self,)), self.ancestors()): - for meth in astroid.mymethods(): - if meth.name in done: - continue - done[meth.name] = None - yield meth - - def mymethods(self): - """return an iterator on all methods defined in the class""" - for member in self.values(): - if isinstance(member, Function): - yield member - - def interfaces(self, herited=True, handler_func=_iface_hdlr): - """return an iterator on interfaces implemented by the given - class node - """ - # FIXME: what if __implements__ = (MyIFace, MyParent.__implements__)... - try: - implements = Instance(self).getattr('__implements__')[0] - except NotFoundError: - return - if not herited and not implements.frame() is self: - return - found = set() - missing = False - for iface in unpack_infer(implements): - if iface is YES: - missing = True - continue - if not iface in found and handler_func(iface): - found.add(iface) - yield iface - if missing: - raise InferenceError() - - _metaclass = None - def _explicit_metaclass(self): - """ Return the explicit defined metaclass - for the current class. - - An explicit defined metaclass is defined - either by passing the ``metaclass`` keyword argument - in the class definition line (Python 3) or (Python 2) by - having a ``__metaclass__`` class attribute, or if there are - no explicit bases but there is a global ``__metaclass__`` variable. - """ - if self._metaclass: - # Expects this from Py3k TreeRebuilder - try: - return next(node for node in self._metaclass.infer() - if node is not YES) - except (InferenceError, StopIteration): - return None - if sys.version_info >= (3, ): - return None - - if '__metaclass__' in self.locals: - assignment = self.locals['__metaclass__'][-1] - elif self.bases: - return None - elif '__metaclass__' in self.root().locals: - assignments = [ass for ass in self.root().locals['__metaclass__'] - if ass.lineno < self.lineno] - if not assignments: - return None - assignment = assignments[-1] - else: - return None - - try: - infered = next(assignment.infer()) - except InferenceError: - return - if infered is YES: # don't expose this - return None - return infered - - def metaclass(self): - """ Return the metaclass of this class. - - If this class does not define explicitly a metaclass, - then the first defined metaclass in ancestors will be used - instead. - """ - klass = self._explicit_metaclass() - if klass is None: - for parent in self.ancestors(): - klass = parent.metaclass() - if klass is not None: - break - return klass - - def _islots(self): - """ Return an iterator with the inferred slots. """ - if '__slots__' not in self.locals: - return - for slots in self.igetattr('__slots__'): - # check if __slots__ is a valid type - for meth in ITER_METHODS: - try: - slots.getattr(meth) - break - except NotFoundError: - continue - else: - continue - - if isinstance(slots, Const): - # a string. Ignore the following checks, - # but yield the node, only if it has a value - if slots.value: - yield slots - continue - if not hasattr(slots, 'itered'): - # we can't obtain the values, maybe a .deque? - continue - - if isinstance(slots, Dict): - values = [item[0] for item in slots.items] - else: - values = slots.itered() - if values is YES: - continue - - for elt in values: - try: - for infered in elt.infer(): - if infered is YES: - continue - if (not isinstance(infered, Const) or - not isinstance(infered.value, str)): - continue - if not infered.value: - continue - yield infered - except InferenceError: - continue - - # Cached, because inferring them all the time is expensive - @cached - def slots(self): - """ Return all the slots for this node. """ - return list(self._islots()) diff --git a/setup.py b/setup.py index 2e4f853..bd5be28 100644 --- a/setup.py +++ b/setup.py @@ -17,39 +17,17 @@ # # You should have received a copy of the GNU Lesser General Public License along # with astroid. If not, see . -"""Generic Setup script, takes package info from __pkginfo__.py file. -""" -__docformat__ = "restructuredtext en" +"""Setup script for astroid.""" +from setuptools import setup, find_packages +from astroid import __pkginfo__ +from astroid.__pkginfo__ import modname, version, license, description, \ + web, author, author_email, distname, install_requires, classifiers -import os -import sys -from os.path import isdir, exists, join +with open('README') as fobj: + long_description = fobj.read() -from setuptools import setup -sys.modules.pop('__pkginfo__', None) -# import optional features -__pkginfo__ = __import__("__pkginfo__") -# import required features -from __pkginfo__ import modname, version, license, description, \ - web, author, author_email - -distname = getattr(__pkginfo__, 'distname', modname) -data_files = getattr(__pkginfo__, 'data_files', None) -include_dirs = getattr(__pkginfo__, 'include_dirs', []) -install_requires = getattr(__pkginfo__, 'install_requires', None) -classifiers = getattr(__pkginfo__, 'classifiers', []) - -if exists('README'): - long_description = open('README').read() -else: - long_description = '' - - -def install(**kwargs): - """setup entry point""" - if '--force-manifest' in sys.argv: - sys.argv.remove('--force-manifest') +def install(): return setup(name = distname, version = version, license = license, @@ -59,25 +37,11 @@ def install(**kwargs): author = author, author_email = author_email, url = web, - data_files = data_files, include_package_data = True, install_requires = install_requires, - package_dir = {modname: '.'}, - packages = [modname], - package_data = { - '': ['brain/*.py', - 'test/regrtest_data/absimp/*.py', - 'test/regrtest_data/package/*.py', - 'test/regrtest_data/package/subpackage/*.py', - 'test/regrtest_data/absimp/sidepackage/*.py', - 'test/regrtest_data/unicode_package/*.py', - 'test/regrtest_data/unicode_package/core/*.py', - 'test/data*/*.egg', - 'test/data*/*.zip', - ], - }, - **kwargs + packages = find_packages(), ) + if __name__ == '__main__' : install() diff --git a/test/data/MyPyPa-0.1.0-py2.5.egg b/test/data/MyPyPa-0.1.0-py2.5.egg deleted file mode 100644 index f62599c..0000000 Binary files a/test/data/MyPyPa-0.1.0-py2.5.egg and /dev/null differ diff --git a/test/data/MyPyPa-0.1.0-py2.5.zip b/test/data/MyPyPa-0.1.0-py2.5.zip deleted file mode 100644 index f62599c..0000000 Binary files a/test/data/MyPyPa-0.1.0-py2.5.zip and /dev/null differ diff --git a/test/data/SSL1/Connection1.py b/test/data/SSL1/Connection1.py deleted file mode 100644 index c0f0152..0000000 --- a/test/data/SSL1/Connection1.py +++ /dev/null @@ -1,14 +0,0 @@ -"""M2Crypto.SSL.Connection - -Copyright (c) 1999-2004 Ng Pheng Siong. All rights reserved.""" - -RCS_id='$Id: Connection1.py,v 1.1 2005-06-13 20:55:22 syt Exp $' - -#Some code deleted here - -class Connection: - - """An SSL connection.""" - - def __init__(self, ctx, sock=None): - print 'init Connection' diff --git a/test/data/SSL1/__init__.py b/test/data/SSL1/__init__.py deleted file mode 100644 index a007b04..0000000 --- a/test/data/SSL1/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from Connection1 import Connection diff --git a/test/data/__init__.py b/test/data/__init__.py deleted file mode 100644 index 332e2e7..0000000 --- a/test/data/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__revision__="$Id: __init__.py,v 1.1 2005-06-13 20:55:20 syt Exp $" diff --git a/test/data/absimport.py b/test/data/absimport.py deleted file mode 100644 index f98effa..0000000 --- a/test/data/absimport.py +++ /dev/null @@ -1,3 +0,0 @@ -from __future__ import absolute_import -import email -from email import message diff --git a/test/data/all.py b/test/data/all.py deleted file mode 100644 index 23f7d2b..0000000 --- a/test/data/all.py +++ /dev/null @@ -1,9 +0,0 @@ - -name = 'a' -_bla = 2 -other = 'o' -class Aaa: pass - -def func(): print 'yo' - -__all__ = 'Aaa', '_bla', 'name' diff --git a/test/data/appl/__init__.py b/test/data/appl/__init__.py deleted file mode 100644 index d652ffd..0000000 --- a/test/data/appl/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Init -""" diff --git a/test/data/appl/myConnection.py b/test/data/appl/myConnection.py deleted file mode 100644 index 93026a6..0000000 --- a/test/data/appl/myConnection.py +++ /dev/null @@ -1,11 +0,0 @@ -import SSL1 -class MyConnection(SSL1.Connection): - - """An SSL connection.""" - - def __init__(self, dummy): - print 'MyConnection init' - -if __name__ == '__main__': - myConnection = MyConnection(' ') - raw_input('Press Enter to continue...') diff --git a/test/data/email.py b/test/data/email.py deleted file mode 100644 index dc59356..0000000 --- a/test/data/email.py +++ /dev/null @@ -1 +0,0 @@ -"""fake email module to test absolute import doesn't grab this one""" diff --git a/test/data/find_test/__init__.py b/test/data/find_test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/find_test/module.py b/test/data/find_test/module.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/find_test/module2.py b/test/data/find_test/module2.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/find_test/noendingnewline.py b/test/data/find_test/noendingnewline.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/find_test/nonregr.py b/test/data/find_test/nonregr.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data/format.py b/test/data/format.py deleted file mode 100644 index 7379706..0000000 --- a/test/data/format.py +++ /dev/null @@ -1,34 +0,0 @@ -"""A multiline string -""" - -function('aeozrijz\ -earzer', hop) -# XXX write test -x = [i for i in range(5) - if i % 4] - -fonction(1, - 2, - 3, - 4) - -def definition(a, - b, - c): - return a + b + c - -class debile(dict, - object): - pass - -if aaaa: pass -else: - aaaa,bbbb = 1,2 - aaaa,bbbb = bbbb,aaaa -# XXX write test -hop = \ - aaaa - - -__revision__.lower(); - diff --git a/test/data/lmfp/__init__.py b/test/data/lmfp/__init__.py deleted file mode 100644 index 74b26b8..0000000 --- a/test/data/lmfp/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# force a "direct" python import -from . import foo diff --git a/test/data/lmfp/foo.py b/test/data/lmfp/foo.py deleted file mode 100644 index 8f7de1e..0000000 --- a/test/data/lmfp/foo.py +++ /dev/null @@ -1,6 +0,0 @@ -import sys -if not getattr(sys, 'bar', None): - sys.just_once = [] -# there used to be two numbers here because -# of a load_module_from_path bug -sys.just_once.append(42) diff --git a/test/data/module.py b/test/data/module.py deleted file mode 100644 index fb48dc1..0000000 --- a/test/data/module.py +++ /dev/null @@ -1,89 +0,0 @@ -"""test module for astroid -""" - -__revision__ = '$Id: module.py,v 1.2 2005-11-02 11:56:54 syt Exp $' -from logilab.common.shellutils import ProgressBar as pb -from astroid import modutils -from astroid.utils import * -import os.path -MY_DICT = {} - -def global_access(key, val): - """function test""" - local = 1 - MY_DICT[key] = val - for i in val: - if i: - del MY_DICT[i] - continue - else: - break - else: - print '!!!' - - -class YO: - """hehe""" - a = 1 - - def __init__(self): - try: - self.yo = 1 - except ValueError, ex: - pass - except (NameError, TypeError): - raise XXXError() - except: - raise - - - -class YOUPI(YO): - class_attr = None - - def __init__(self): - self.member = None - - def method(self): - """method test""" - global MY_DICT - try: - MY_DICT = {} - local = None - autre = [a for (a, b) in MY_DICT if b] - if b in autre: - print 'yo', - else: - if a in autre: - print 'hehe' - global_access(local, val=autre) - finally: - return local - - def static_method(): - """static method test""" - assert MY_DICT, '???' - static_method = staticmethod(static_method) - - def class_method(cls): - """class method test""" - exec a in b - class_method = classmethod(class_method) - - -def four_args(a, b, c, d): - """four arguments (was nested_args)""" - print a, b, c, d - while 1: - if a: - break - a += +1 - else: - b += -2 - if c: - d = ((a) and (b)) or (c) - else: - c = ((a) and (b)) or (d) - map(lambda x, y: (y, x), a) -redirect = four_args - diff --git a/test/data/module1abs/__init__.py b/test/data/module1abs/__init__.py deleted file mode 100644 index 9eb5902..0000000 --- a/test/data/module1abs/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import absolute_import -from . import core -from .core import * -print sys.version diff --git a/test/data/module1abs/core.py b/test/data/module1abs/core.py deleted file mode 100644 index de10111..0000000 --- a/test/data/module1abs/core.py +++ /dev/null @@ -1 +0,0 @@ -import sys diff --git a/test/data/module2.py b/test/data/module2.py deleted file mode 100644 index d6bf49e..0000000 --- a/test/data/module2.py +++ /dev/null @@ -1,143 +0,0 @@ -from data.module import YO, YOUPI -import data - - -class Specialization(YOUPI, YO): - pass - - - -class Metaclass(type): - pass - - - -class Interface: - pass - - - -class MyIFace(Interface): - pass - - - -class AnotherIFace(Interface): - pass - - - -class MyException(Exception): - pass - - - -class MyError(MyException): - pass - - - -class AbstractClass(object): - - def to_override(self, whatever): - raise NotImplementedError() - - def return_something(self, param): - if param: - return 'toto' - return - - - -class Concrete0: - __implements__ = MyIFace - - - -class Concrete1: - __implements__ = (MyIFace, AnotherIFace) - - - -class Concrete2: - __implements__ = (MyIFace, AnotherIFace) - - - -class Concrete23(Concrete1): - pass - -del YO.member -del YO -[SYN1, SYN2] = (Concrete0, Concrete1) -assert `1` -b = (1) | (((2) & (3)) ^ (8)) -bb = ((1) | (two)) | (6) -ccc = ((one) & (two)) & (three) -dddd = ((x) ^ (o)) ^ (r) -exec 'c = 3' -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) -c = (b) // (2) -c = (b) - (2) -c = (b) % (2) -c = (b) ** (2) -c = (b) << (2) -c = (b) >> (2) -c = ~b -c = not b -d = [c] -e = d[:] -e = d[a:b:c] -raise_string(*args, **kwargs) -print >> stream, 'bonjour' -print >> stream, 'salut', - -def make_class(any, base=data.module.YO, *args, **kwargs): - """check base is correctly resolved to Concrete0""" - - - class Aaaa(base): - """dynamic class""" - - - return Aaaa -from os.path import abspath -import os as myos - - -class A: - pass - - - -class A(A): - pass - - -def generator(): - """A generator.""" - yield - -def not_a_generator(): - """A function that contains generator, but is not one.""" - - def generator(): - yield - genl = lambda : (yield) - -def with_metaclass(meta, *bases): - return meta('NewBase', bases, {}) - - -class NotMetaclass(with_metaclass(Metaclass)): - pass - - diff --git a/test/data/noendingnewline.py b/test/data/noendingnewline.py deleted file mode 100644 index e1d6e4a..0000000 --- a/test/data/noendingnewline.py +++ /dev/null @@ -1,36 +0,0 @@ -import unittest - - -class TestCase(unittest.TestCase): - - def setUp(self): - unittest.TestCase.setUp(self) - - - def tearDown(self): - unittest.TestCase.tearDown(self) - - def testIt(self): - self.a = 10 - self.xxx() - - - def xxx(self): - if False: - pass - print 'a' - - if False: - pass - pass - - if False: - pass - print 'rara' - - -if __name__ == '__main__': - print 'test2' - unittest.main() - - diff --git a/test/data/nonregr.py b/test/data/nonregr.py deleted file mode 100644 index a670e39..0000000 --- a/test/data/nonregr.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import generators - -try: - enumerate = enumerate -except NameError: - - def enumerate(iterable): - """emulates the python2.3 enumerate() function""" - i = 0 - for val in iterable: - yield i, val - i += 1 - -def toto(value): - for k, v in value: - print v.get('yo') - - -import imp -fp, mpath, desc = imp.find_module('optparse',a) -s_opt = imp.load_module('std_optparse', fp, mpath, desc) - -class OptionParser(s_opt.OptionParser): - - def parse_args(self, args=None, values=None, real_optparse=False): - if real_optparse: - pass -## return super(OptionParser, self).parse_args() - else: - import optcomp - optcomp.completion(self) - - -class Aaa(object): - """docstring""" - def __init__(self): - self.__setattr__('a','b') - pass - - def one_public(self): - """docstring""" - pass - - def another_public(self): - """docstring""" - pass - -class Ccc(Aaa): - """docstring""" - - class Ddd(Aaa): - """docstring""" - pass - - class Eee(Ddd): - """docstring""" - pass diff --git a/test/data/notall.py b/test/data/notall.py deleted file mode 100644 index b998d88..0000000 --- a/test/data/notall.py +++ /dev/null @@ -1,8 +0,0 @@ - -name = 'a' -_bla = 2 -other = 'o' -class Aaa: pass - -def func(): print 'yo' - diff --git a/test/data2/__init__.py b/test/data2/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data2/clientmodule_test.py b/test/data2/clientmodule_test.py deleted file mode 100644 index 196fff8..0000000 --- a/test/data2/clientmodule_test.py +++ /dev/null @@ -1,32 +0,0 @@ -""" docstring for file clientmodule.py """ -from data2.suppliermodule_test import Interface as IFace, DoNothing - -class Toto: pass - -class Ancestor: - """ Ancestor method """ - __implements__ = (IFace,) - - def __init__(self, value): - local_variable = 0 - self.attr = 'this method shouldn\'t have a docstring' - self.__value = value - - def get_value(self): - """ nice docstring ;-) """ - return self.__value - - def set_value(self, value): - self.__value = value - return 'this method shouldn\'t have a docstring' - -class Specialization(Ancestor): - TYPE = 'final class' - top = 'class' - - def __init__(self, value, _id): - Ancestor.__init__(self, value) - self._id = _id - self.relation = DoNothing() - self.toto = Toto() - diff --git a/test/data2/suppliermodule_test.py b/test/data2/suppliermodule_test.py deleted file mode 100644 index ddacb47..0000000 --- a/test/data2/suppliermodule_test.py +++ /dev/null @@ -1,13 +0,0 @@ -""" file suppliermodule.py """ - -class NotImplemented(Exception): - pass - -class Interface: - def get_value(self): - raise NotImplemented() - - def set_value(self, value): - raise NotImplemented() - -class DoNothing : pass diff --git a/test/data_py3/MyPyPa-0.1.0-py2.5.egg b/test/data_py3/MyPyPa-0.1.0-py2.5.egg deleted file mode 100644 index f62599c..0000000 Binary files a/test/data_py3/MyPyPa-0.1.0-py2.5.egg and /dev/null differ diff --git a/test/data_py3/MyPyPa-0.1.0-py2.5.zip b/test/data_py3/MyPyPa-0.1.0-py2.5.zip deleted file mode 100644 index f62599c..0000000 Binary files a/test/data_py3/MyPyPa-0.1.0-py2.5.zip and /dev/null differ diff --git a/test/data_py3/SSL1/Connection1.py b/test/data_py3/SSL1/Connection1.py deleted file mode 100644 index 7373271..0000000 --- a/test/data_py3/SSL1/Connection1.py +++ /dev/null @@ -1,14 +0,0 @@ -"""M2Crypto.SSL.Connection - -Copyright (c) 1999-2004 Ng Pheng Siong. All rights reserved.""" - -RCS_id='$Id: Connection1.py,v 1.1 2005-06-13 20:55:22 syt Exp $' - -#Some code deleted here - -class Connection: - - """An SSL connection.""" - - def __init__(self, ctx, sock=None): - print('init Connection') diff --git a/test/data_py3/SSL1/__init__.py b/test/data_py3/SSL1/__init__.py deleted file mode 100644 index c83eded..0000000 --- a/test/data_py3/SSL1/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .Connection1 import Connection diff --git a/test/data_py3/__init__.py b/test/data_py3/__init__.py deleted file mode 100644 index 332e2e7..0000000 --- a/test/data_py3/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__revision__="$Id: __init__.py,v 1.1 2005-06-13 20:55:20 syt Exp $" diff --git a/test/data_py3/absimport.py b/test/data_py3/absimport.py deleted file mode 100644 index 88f9d95..0000000 --- a/test/data_py3/absimport.py +++ /dev/null @@ -1,3 +0,0 @@ - -import email -from email import message diff --git a/test/data_py3/all.py b/test/data_py3/all.py deleted file mode 100644 index 587765b..0000000 --- a/test/data_py3/all.py +++ /dev/null @@ -1,9 +0,0 @@ - -name = 'a' -_bla = 2 -other = 'o' -class Aaa: pass - -def func(): print('yo') - -__all__ = 'Aaa', '_bla', 'name' diff --git a/test/data_py3/appl/__init__.py b/test/data_py3/appl/__init__.py deleted file mode 100644 index d652ffd..0000000 --- a/test/data_py3/appl/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Init -""" diff --git a/test/data_py3/appl/myConnection.py b/test/data_py3/appl/myConnection.py deleted file mode 100644 index b5b206a..0000000 --- a/test/data_py3/appl/myConnection.py +++ /dev/null @@ -1,11 +0,0 @@ -import SSL1 -class MyConnection(SSL1.Connection): - - """An SSL connection.""" - - def __init__(self, dummy): - print('MyConnection init') - -if __name__ == '__main__': - myConnection = MyConnection(' ') - input('Press Enter to continue...') diff --git a/test/data_py3/email.py b/test/data_py3/email.py deleted file mode 100644 index dc59356..0000000 --- a/test/data_py3/email.py +++ /dev/null @@ -1 +0,0 @@ -"""fake email module to test absolute import doesn't grab this one""" diff --git a/test/data_py3/find_test/__init__.py b/test/data_py3/find_test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data_py3/find_test/module.py b/test/data_py3/find_test/module.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data_py3/find_test/module2.py b/test/data_py3/find_test/module2.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data_py3/find_test/noendingnewline.py b/test/data_py3/find_test/noendingnewline.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data_py3/find_test/nonregr.py b/test/data_py3/find_test/nonregr.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/data_py3/format.py b/test/data_py3/format.py deleted file mode 100644 index 7379706..0000000 --- a/test/data_py3/format.py +++ /dev/null @@ -1,34 +0,0 @@ -"""A multiline string -""" - -function('aeozrijz\ -earzer', hop) -# XXX write test -x = [i for i in range(5) - if i % 4] - -fonction(1, - 2, - 3, - 4) - -def definition(a, - b, - c): - return a + b + c - -class debile(dict, - object): - pass - -if aaaa: pass -else: - aaaa,bbbb = 1,2 - aaaa,bbbb = bbbb,aaaa -# XXX write test -hop = \ - aaaa - - -__revision__.lower(); - diff --git a/test/data_py3/lmfp/__init__.py b/test/data_py3/lmfp/__init__.py deleted file mode 100644 index 74b26b8..0000000 --- a/test/data_py3/lmfp/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# force a "direct" python import -from . import foo diff --git a/test/data_py3/lmfp/foo.py b/test/data_py3/lmfp/foo.py deleted file mode 100644 index 8f7de1e..0000000 --- a/test/data_py3/lmfp/foo.py +++ /dev/null @@ -1,6 +0,0 @@ -import sys -if not getattr(sys, 'bar', None): - sys.just_once = [] -# there used to be two numbers here because -# of a load_module_from_path bug -sys.just_once.append(42) diff --git a/test/data_py3/module.py b/test/data_py3/module.py deleted file mode 100644 index ec5b64a..0000000 --- a/test/data_py3/module.py +++ /dev/null @@ -1,89 +0,0 @@ -"""test module for astroid -""" - -__revision__ = '$Id: module.py,v 1.2 2005-11-02 11:56:54 syt Exp $' -from logilab.common.shellutils import ProgressBar as pb -from astroid import modutils -from astroid.utils import * -import os.path -MY_DICT = {} - -def global_access(key, val): - """function test""" - local = 1 - MY_DICT[key] = val - for i in val: - if i: - del MY_DICT[i] - continue - else: - break - else: - print('!!!') - - -class YO: - """hehe""" - a = 1 - - def __init__(self): - try: - self.yo = 1 - except ValueError as ex: - pass - except (NameError, TypeError): - raise XXXError() - except: - raise - - - -class YOUPI(YO): - class_attr = None - - def __init__(self): - self.member = None - - def method(self): - """method test""" - global MY_DICT - try: - MY_DICT = {} - local = None - autre = [a for (a, b) in MY_DICT if b] - if b in autre: - print('yo', end=' ') - else: - if a in autre: - print('hehe') - global_access(local, val=autre) - finally: - return local - - def static_method(): - """static method test""" - assert MY_DICT, '???' - static_method = staticmethod(static_method) - - def class_method(cls): - """class method test""" - exec(a, b) - class_method = classmethod(class_method) - - -def four_args(a, b, c, d): - """four arguments (was nested_args)""" - print(a, b, c, d) - while 1: - if a: - break - a += +1 - else: - b += -2 - if c: - d = ((a) and (b)) or (c) - else: - c = ((a) and (b)) or (d) - list(map(lambda x, y: (y, x), a)) -redirect = four_args - diff --git a/test/data_py3/module1abs/__init__.py b/test/data_py3/module1abs/__init__.py deleted file mode 100644 index f9d5b68..0000000 --- a/test/data_py3/module1abs/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ - -from . import core -from .core import * -print(sys.version) diff --git a/test/data_py3/module1abs/core.py b/test/data_py3/module1abs/core.py deleted file mode 100644 index de10111..0000000 --- a/test/data_py3/module1abs/core.py +++ /dev/null @@ -1 +0,0 @@ -import sys diff --git a/test/data_py3/module2.py b/test/data_py3/module2.py deleted file mode 100644 index 94a8bc7..0000000 --- a/test/data_py3/module2.py +++ /dev/null @@ -1,143 +0,0 @@ -from data_py3.module import YO, YOUPI -import data_py3 - - -class Specialization(YOUPI, YO): - pass - - - -class Metaclass(type): - pass - - - -class Interface: - pass - - - -class MyIFace(Interface): - pass - - - -class AnotherIFace(Interface): - pass - - - -class MyException(Exception): - pass - - - -class MyError(MyException): - pass - - - -class AbstractClass(object): - - def to_override(self, whatever): - raise NotImplementedError() - - def return_something(self, param): - if param: - return 'toto' - return - - - -class Concrete0: - __implements__ = MyIFace - - - -class Concrete1: - __implements__ = (MyIFace, AnotherIFace) - - - -class Concrete2: - __implements__ = (MyIFace, AnotherIFace) - - - -class Concrete23(Concrete1): - pass - -del YO.member -del YO -[SYN1, SYN2] = (Concrete0, Concrete1) -assert repr(1) -b = (1) | (((2) & (3)) ^ (8)) -bb = ((1) | (two)) | (6) -ccc = ((one) & (two)) & (three) -dddd = ((x) ^ (o)) ^ (r) -exec('c = 3') -exec('c = 3', {}, {}) - -def raise_string(a=2, *args, **kwargs): - raise Exception('yo') - yield 'coucou' - yield -a = (b) + (2) -c = (b) * (2) -c = (b) / (2) -c = (b) // (2) -c = (b) - (2) -c = (b) % (2) -c = (b) ** (2) -c = (b) << (2) -c = (b) >> (2) -c = ~b -c = not b -d = [c] -e = d[:] -e = d[a:b:c] -raise_string(*args, **kwargs) -print('bonjour', file=stream) -print('salut', end=' ', file=stream) - -def make_class(any, base=data_py3.module.YO, *args, **kwargs): - """check base is correctly resolved to Concrete0""" - - - class Aaaa(base): - """dynamic class""" - - - return Aaaa -from os.path import abspath -import os as myos - - -class A: - pass - - - -class A(A): - pass - - -def generator(): - """A generator.""" - yield - -def not_a_generator(): - """A function that contains generator, but is not one.""" - - def generator(): - yield - genl = lambda : (yield) - -def with_metaclass(meta, *bases): - return meta('NewBase', bases, {}) - - -class NotMetaclass(with_metaclass(Metaclass)): - pass - - diff --git a/test/data_py3/noendingnewline.py b/test/data_py3/noendingnewline.py deleted file mode 100644 index e17b92c..0000000 --- a/test/data_py3/noendingnewline.py +++ /dev/null @@ -1,36 +0,0 @@ -import unittest - - -class TestCase(unittest.TestCase): - - def setUp(self): - unittest.TestCase.setUp(self) - - - def tearDown(self): - unittest.TestCase.tearDown(self) - - def testIt(self): - self.a = 10 - self.xxx() - - - def xxx(self): - if False: - pass - print('a') - - if False: - pass - pass - - if False: - pass - print('rara') - - -if __name__ == '__main__': - print('test2') - unittest.main() - - diff --git a/test/data_py3/nonregr.py b/test/data_py3/nonregr.py deleted file mode 100644 index 78765c8..0000000 --- a/test/data_py3/nonregr.py +++ /dev/null @@ -1,57 +0,0 @@ - - -try: - enumerate = enumerate -except NameError: - - def enumerate(iterable): - """emulates the python2.3 enumerate() function""" - i = 0 - for val in iterable: - yield i, val - i += 1 - -def toto(value): - for k, v in value: - print(v.get('yo')) - - -import imp -fp, mpath, desc = imp.find_module('optparse',a) -s_opt = imp.load_module('std_optparse', fp, mpath, desc) - -class OptionParser(s_opt.OptionParser): - - def parse_args(self, args=None, values=None, real_optparse=False): - if real_optparse: - pass -## return super(OptionParser, self).parse_args() - else: - import optcomp - optcomp.completion(self) - - -class Aaa(object): - """docstring""" - def __init__(self): - self.__setattr__('a','b') - pass - - def one_public(self): - """docstring""" - pass - - def another_public(self): - """docstring""" - pass - -class Ccc(Aaa): - """docstring""" - - class Ddd(Aaa): - """docstring""" - pass - - class Eee(Ddd): - """docstring""" - pass diff --git a/test/data_py3/notall.py b/test/data_py3/notall.py deleted file mode 100644 index 9d35aa3..0000000 --- a/test/data_py3/notall.py +++ /dev/null @@ -1,8 +0,0 @@ - -name = 'a' -_bla = 2 -other = 'o' -class Aaa: pass - -def func(): print('yo') - diff --git a/test/regrtest_data/absimp/__init__.py b/test/regrtest_data/absimp/__init__.py deleted file mode 100644 index b98444d..0000000 --- a/test/regrtest_data/absimp/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""a package with absolute import activated -""" - -from __future__ import absolute_import - diff --git a/test/regrtest_data/absimp/sidepackage/__init__.py b/test/regrtest_data/absimp/sidepackage/__init__.py deleted file mode 100644 index 239499a..0000000 --- a/test/regrtest_data/absimp/sidepackage/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""a side package with nothing in it -""" - diff --git a/test/regrtest_data/absimp/string.py b/test/regrtest_data/absimp/string.py deleted file mode 100644 index e68e749..0000000 --- a/test/regrtest_data/absimp/string.py +++ /dev/null @@ -1,3 +0,0 @@ -from __future__ import absolute_import, print_function -import string -print(string) diff --git a/test/regrtest_data/descriptor_crash.py b/test/regrtest_data/descriptor_crash.py deleted file mode 100644 index 11fbb4a..0000000 --- a/test/regrtest_data/descriptor_crash.py +++ /dev/null @@ -1,11 +0,0 @@ - -import urllib - -class Page(object): - _urlOpen = staticmethod(urllib.urlopen) - - def getPage(self, url): - handle = self._urlOpen(url) - data = handle.read() - handle.close() - return data diff --git a/test/regrtest_data/package/__init__.py b/test/regrtest_data/package/__init__.py deleted file mode 100644 index 575d18b..0000000 --- a/test/regrtest_data/package/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -"""package's __init__ file""" - - -from . import subpackage diff --git a/test/regrtest_data/package/absimport.py b/test/regrtest_data/package/absimport.py deleted file mode 100644 index 33ed117..0000000 --- a/test/regrtest_data/package/absimport.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import absolute_import, print_function -import import_package_subpackage_module # fail -print(import_package_subpackage_module) - -from . import hello as hola - diff --git a/test/regrtest_data/package/hello.py b/test/regrtest_data/package/hello.py deleted file mode 100644 index b154c84..0000000 --- a/test/regrtest_data/package/hello.py +++ /dev/null @@ -1,2 +0,0 @@ -"""hello module""" - diff --git a/test/regrtest_data/package/import_package_subpackage_module.py b/test/regrtest_data/package/import_package_subpackage_module.py deleted file mode 100644 index ad442c1..0000000 --- a/test/regrtest_data/package/import_package_subpackage_module.py +++ /dev/null @@ -1,49 +0,0 @@ -# pylint: disable-msg=I0011,C0301,W0611 -"""I found some of my scripts trigger off an AttributeError in pylint -0.8.1 (with common 0.12.0 and astroid 0.13.1). - -Traceback (most recent call last): - File "/usr/bin/pylint", line 4, in ? - lint.Run(sys.argv[1:]) - File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 729, in __init__ - linter.check(args) - File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 412, in check - self.check_file(filepath, modname, checkers) - File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 426, in check_file - astroid = self._check_file(filepath, modname, checkers) - File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 450, in _check_file - self.check_astroid_module(astroid, checkers) - File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 494, in check_astroid_module - self.astroid_events(astroid, [checker for checker in checkers - File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astroid_events - self.astroid_events(child, checkers, _reversed_checkers) - File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astroid_events - self.astroid_events(child, checkers, _reversed_checkers) - File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 508, in astroid_events - checker.visit(astroid) - File "/usr/lib/python2.4/site-packages/logilab/astroid/utils.py", line 84, in visit - method(node) - File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 295, in visit_import - self._check_module_attrs(node, module, name_parts[1:]) - File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 357, in _check_module_attrs - self.add_message('E0611', args=(name, module.name), -AttributeError: Import instance has no attribute 'name' - - -You can reproduce it by: -(1) create package structure like the following: - -package/ - __init__.py - subpackage/ - __init__.py - module.py - -(2) in package/__init__.py write: - -import subpackage - -(3) run pylint with a script importing package.subpackage.module. -""" -__revision__ = '$Id: import_package_subpackage_module.py,v 1.1 2005-11-10 15:59:32 syt Exp $' -import package.subpackage.module diff --git a/test/regrtest_data/package/subpackage/__init__.py b/test/regrtest_data/package/subpackage/__init__.py deleted file mode 100644 index dc4782e..0000000 --- a/test/regrtest_data/package/subpackage/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""package.subpackage""" diff --git a/test/regrtest_data/package/subpackage/module.py b/test/regrtest_data/package/subpackage/module.py deleted file mode 100644 index 4b7244b..0000000 --- a/test/regrtest_data/package/subpackage/module.py +++ /dev/null @@ -1 +0,0 @@ -"""package.subpackage.module""" diff --git a/test/regrtest_data/recursion.py b/test/regrtest_data/recursion.py deleted file mode 100644 index a34dad3..0000000 --- a/test/regrtest_data/recursion.py +++ /dev/null @@ -1,3 +0,0 @@ -""" For issue #25 """ -class Base(object): - pass \ No newline at end of file diff --git a/test/regrtest_data/unicode_package/__init__.py b/test/regrtest_data/unicode_package/__init__.py deleted file mode 100644 index 713e559..0000000 --- a/test/regrtest_data/unicode_package/__init__.py +++ /dev/null @@ -1 +0,0 @@ -x = "șțîâ" \ No newline at end of file diff --git a/test/regrtest_data/unicode_package/core/__init__.py b/test/regrtest_data/unicode_package/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test/unittest_brain.py b/test/unittest_brain.py deleted file mode 100644 index 8fa853c..0000000 --- a/test/unittest_brain.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright 2013 Google Inc. All Rights Reserved. -# -# This file is part of astroid. -# -# 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 . -"""Tests for basic functionality in astroid.brain.""" -import sys - -import unittest - -from astroid import MANAGER -from astroid import bases -from astroid import test_utils -import astroid - -class HashlibTC(unittest.TestCase): - def test_hashlib(self): - """Tests that brain extensions for hashlib work.""" - hashlib_module = MANAGER.ast_from_module_name('hashlib') - for class_name in ['md5', 'sha1']: - class_obj = hashlib_module[class_name] - self.assertIn('update', class_obj) - self.assertIn('digest', class_obj) - self.assertIn('hexdigest', class_obj) - self.assertEqual(len(class_obj['__init__'].args.args), 2) - self.assertEqual(len(class_obj['__init__'].args.defaults), 1) - self.assertEqual(len(class_obj['update'].args.args), 2) - self.assertEqual(len(class_obj['digest'].args.args), 1) - self.assertEqual(len(class_obj['hexdigest'].args.args), 1) - - -class NamedTupleTest(unittest.TestCase): - def test_namedtuple_base(self): - klass = test_utils.extract_node(""" - from collections import namedtuple - - class X(namedtuple("X", ["a", "b", "c"])): - pass - """) - self.assertEqual( - [anc.name for anc in klass.ancestors()], - ['X', 'tuple', 'object']) - for anc in klass.ancestors(): - self.assertFalse(anc.parent is None) - - def test_namedtuple_inference(self): - klass = test_utils.extract_node(""" - from collections import namedtuple - - name = "X" - fields = ["a", "b", "c"] - class X(namedtuple(name, fields)): - pass - """) - for base in klass.ancestors(): - if base.name == 'X': - break - self.assertSetEqual({"a", "b", "c"}, set(base.instance_attrs)) - - def test_namedtuple_inference_failure(self): - klass = test_utils.extract_node(""" - from collections import namedtuple - - def foo(fields): - return __(namedtuple("foo", fields)) - """) - self.assertIs(bases.YES, next(klass.infer())) - - - def test_namedtuple_advanced_inference(self): - if sys.version_info[0] > 2: - self.skipTest('Currently broken for Python 3.') - # urlparse return an object of class ParseResult, which has a - # namedtuple call and a mixin as base classes - result = test_utils.extract_node(""" - import urlparse - - result = __(urlparse.urlparse('gopher://')) - """) - instance = next(result.infer()) - self.assertEqual(len(instance.getattr('scheme')), 1) - self.assertEqual(len(instance.getattr('port')), 1) - with self.assertRaises(astroid.NotFoundError): - instance.getattr('foo') - - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_builder.py b/test/unittest_builder.py deleted file mode 100644 index 47ae158..0000000 --- a/test/unittest_builder.py +++ /dev/null @@ -1,781 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""tests for the astroid builder and rebuilder module""" - -import unittest -import sys -from os.path import join, abspath, dirname -from functools import partial - -from pprint import pprint - -from astroid import builder, nodes, InferenceError, NotFoundError -from astroid.nodes import Module -from astroid.bases import YES, BUILTINS -from astroid.manager import AstroidManager -from astroid import test_utils - -MANAGER = AstroidManager() - -PY3K = sys.version_info >= (3, 0) - -if PY3K: - DATA = join(dirname(abspath(__file__)), 'data_py3') -else: - DATA = join(dirname(abspath(__file__)), 'data') - -from unittest_inference import get_name_node - -if PY3K: - import data_py3 as data - from data_py3 import module as test_module -else: - import data - from data import module as test_module - - -class FromToLineNoTC(unittest.TestCase): - - astroid = builder.AstroidBuilder().file_build(join(DATA, 'format.py')) - - def test_callfunc_lineno(self): - stmts = self.astroid.body - # on line 4: - # function('aeozrijz\ - # earzer', hop) - discard = stmts[0] - self.assertIsInstance(discard, nodes.Discard) - self.assertEqual(discard.fromlineno, 4) - self.assertEqual(discard.tolineno, 5) - callfunc = discard.value - self.assertIsInstance(callfunc, nodes.CallFunc) - self.assertEqual(callfunc.fromlineno, 4) - self.assertEqual(callfunc.tolineno, 5) - name = callfunc.func - self.assertIsInstance(name, nodes.Name) - self.assertEqual(name.fromlineno, 4) - self.assertEqual(name.tolineno, 4) - strarg = callfunc.args[0] - self.assertIsInstance(strarg, nodes.Const) - if hasattr(sys, 'pypy_version_info'): - lineno = 4 - else: - lineno = 5 # no way for this one in CPython (is 4 actually) - self.assertEqual(strarg.fromlineno, lineno) - self.assertEqual(strarg.tolineno, lineno) - namearg = callfunc.args[1] - self.assertIsInstance(namearg, nodes.Name) - self.assertEqual(namearg.fromlineno, 5) - self.assertEqual(namearg.tolineno, 5) - # on line 10: - # fonction(1, - # 2, - # 3, - # 4) - discard = stmts[2] - self.assertIsInstance(discard, nodes.Discard) - self.assertEqual(discard.fromlineno, 10) - self.assertEqual(discard.tolineno, 13) - callfunc = discard.value - self.assertIsInstance(callfunc, nodes.CallFunc) - self.assertEqual(callfunc.fromlineno, 10) - self.assertEqual(callfunc.tolineno, 13) - name = callfunc.func - self.assertIsInstance(name, nodes.Name) - self.assertEqual(name.fromlineno, 10) - self.assertEqual(name.tolineno, 10) - for i, arg in enumerate(callfunc.args): - self.assertIsInstance(arg, nodes.Const) - self.assertEqual(arg.fromlineno, 10+i) - self.assertEqual(arg.tolineno, 10+i) - - def test_function_lineno(self): - stmts = self.astroid.body - # on line 15: - # def definition(a, - # b, - # c): - # return a + b + c - function = stmts[3] - self.assertIsInstance(function, nodes.Function) - self.assertEqual(function.fromlineno, 15) - self.assertEqual(function.tolineno, 18) - 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): - astroid = builder.AstroidBuilder().string_build(''' -@decorator -def function( - arg): - print (arg) -''', __name__, __file__) - function = astroid['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.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): - stmts = self.astroid.body - # on line 20: - # class debile(dict, - # object): - # pass - class_ = stmts[4] - self.assertIsInstance(class_, nodes.Class) - self.assertEqual(class_.fromlineno, 20) - self.assertEqual(class_.tolineno, 22) - self.assertEqual(class_.blockstart_tolineno, 21) - pass_ = class_.body[0] - self.assertIsInstance(pass_, nodes.Pass) - self.assertEqual(pass_.fromlineno, 22) - self.assertEqual(pass_.tolineno, 22) - - def test_if_lineno(self): - stmts = self.astroid.body - # on line 20: - # if aaaa: pass - # else: - # aaaa,bbbb = 1,2 - # aaaa,bbbb = bbbb,aaaa - if_ = stmts[5] - self.assertIsInstance(if_, nodes.If) - self.assertEqual(if_.fromlineno, 24) - self.assertEqual(if_.tolineno, 27) - self.assertEqual(if_.blockstart_tolineno, 24) - self.assertEqual(if_.orelse[0].fromlineno, 26) - self.assertEqual(if_.orelse[1].tolineno, 27) - - def test_for_while_lineno(self): - for code in (''' -for a in range(4): - print (a) - break -else: - print ("bouh") -''', ''' -while a: - print (a) - break -else: - print ("bouh") -''', - ): - astroid = builder.AstroidBuilder().string_build(code, __name__, __file__) - stmt = astroid.body[0] - self.assertEqual(stmt.fromlineno, 2) - self.assertEqual(stmt.tolineno, 6) - self.assertEqual(stmt.blockstart_tolineno, 2) - self.assertEqual(stmt.orelse[0].fromlineno, 6) # XXX - self.assertEqual(stmt.orelse[0].tolineno, 6) - - - def test_try_except_lineno(self): - astroid = builder.AstroidBuilder().string_build(''' -try: - print (a) -except: - pass -else: - print ("bouh") -''', __name__, __file__) - try_ = astroid.body[0] - self.assertEqual(try_.fromlineno, 2) - self.assertEqual(try_.tolineno, 7) - self.assertEqual(try_.blockstart_tolineno, 2) - self.assertEqual(try_.orelse[0].fromlineno, 7) # XXX - self.assertEqual(try_.orelse[0].tolineno, 7) - hdlr = try_.handlers[0] - self.assertEqual(hdlr.fromlineno, 4) - self.assertEqual(hdlr.tolineno, 5) - self.assertEqual(hdlr.blockstart_tolineno, 4) - - - def test_try_finally_lineno(self): - astroid = builder.AstroidBuilder().string_build(''' -try: - print (a) -finally: - print ("bouh") -''', __name__, __file__) - try_ = astroid.body[0] - self.assertEqual(try_.fromlineno, 2) - self.assertEqual(try_.tolineno, 5) - self.assertEqual(try_.blockstart_tolineno, 2) - self.assertEqual(try_.finalbody[0].fromlineno, 5) # XXX - self.assertEqual(try_.finalbody[0].tolineno, 5) - - - def test_try_finally_25_lineno(self): - astroid = builder.AstroidBuilder().string_build(''' -try: - print (a) -except: - pass -finally: - print ("bouh") -''', __name__, __file__) - try_ = astroid.body[0] - self.assertEqual(try_.fromlineno, 2) - self.assertEqual(try_.tolineno, 7) - self.assertEqual(try_.blockstart_tolineno, 2) - self.assertEqual(try_.finalbody[0].fromlineno, 7) # XXX - self.assertEqual(try_.finalbody[0].tolineno, 7) - - - def test_with_lineno(self): - astroid = builder.AstroidBuilder().string_build(''' -from __future__ import with_statement -with file("/tmp/pouet") as f: - print (f) -''', __name__, __file__) - with_ = astroid.body[1] - self.assertEqual(with_.fromlineno, 3) - self.assertEqual(with_.tolineno, 4) - self.assertEqual(with_.blockstart_tolineno, 3) - - - -class BuilderTC(unittest.TestCase): - - def setUp(self): - self.builder = builder.AstroidBuilder() - - def test_border_cases(self): - """check that a file with no trailing new line is parseable""" - self.builder.file_build(join(DATA, 'noendingnewline.py'), 'data.noendingnewline') - self.assertRaises(builder.AstroidBuildingException, - self.builder.file_build, join(DATA, 'inexistant.py'), 'whatever') - - def test_inspect_build0(self): - """test astroid tree build from a living object""" - builtin_ast = MANAGER.ast_from_module_name(BUILTINS) - if sys.version_info < (3, 0): - fclass = builtin_ast['file'] - self.assertIn('name', fclass) - self.assertIn('mode', fclass) - self.assertIn('read', fclass) - self.assertTrue(fclass.newstyle) - self.assertTrue(fclass.pytype(), '%s.type' % BUILTINS) - self.assertIsInstance(fclass['read'], nodes.Function) - # check builtin function has args.args == None - dclass = builtin_ast['dict'] - self.assertIsNone(dclass['has_key'].args.args) - # just check type and object are there - builtin_ast.getattr('type') - objectastroid = builtin_ast.getattr('object')[0] - self.assertIsInstance(objectastroid.getattr('__new__')[0], nodes.Function) - # check open file alias - builtin_ast.getattr('open') - # check 'help' is there (defined dynamically by site.py) - builtin_ast.getattr('help') - # check property has __init__ - pclass = builtin_ast['property'] - self.assertIn('__init__', pclass) - self.assertIsInstance(builtin_ast['None'], nodes.Const) - self.assertIsInstance(builtin_ast['True'], nodes.Const) - self.assertIsInstance(builtin_ast['False'], nodes.Const) - if sys.version_info < (3, 0): - self.assertIsInstance(builtin_ast['Exception'], nodes.From) - self.assertIsInstance(builtin_ast['NotImplementedError'], nodes.From) - else: - self.assertIsInstance(builtin_ast['Exception'], nodes.Class) - self.assertIsInstance(builtin_ast['NotImplementedError'], nodes.Class) - - def test_inspect_build1(self): - time_ast = MANAGER.ast_from_module_name('time') - self.assertTrue(time_ast) - self.assertEqual(time_ast['time'].args.defaults, []) - - def test_inspect_build2(self): - """test astroid tree build from a living object""" - try: - from mx import DateTime - except ImportError: - self.skipTest('test skipped: mxDateTime is not available') - else: - dt_ast = self.builder.inspect_build(DateTime) - dt_ast.getattr('DateTime') - # this one is failing since DateTimeType.__module__ = 'builtins' ! - #dt_ast.getattr('DateTimeType') - - def test_inspect_build3(self): - self.builder.inspect_build(unittest) - - def test_inspect_build_instance(self): - """test astroid tree build from a living object""" - if sys.version_info >= (3, 0): - self.skipTest('The module "exceptions" is gone in py3.x') - import exceptions - builtin_ast = self.builder.inspect_build(exceptions) - fclass = builtin_ast['OSError'] - # things like OSError.strerror are now (2.5) data descriptors on the - # class instead of entries in the __dict__ of an instance - container = fclass - self.assertIn('errno', container) - self.assertIn('strerror', container) - self.assertIn('filename', container) - - def test_inspect_build_type_object(self): - builtin_ast = MANAGER.ast_from_module_name(BUILTINS) - - infered = list(builtin_ast.igetattr('object')) - self.assertEqual(len(infered), 1) - infered = infered[0] - self.assertEqual(infered.name, 'object') - infered.as_string() # no crash test - - infered = list(builtin_ast.igetattr('type')) - self.assertEqual(len(infered), 1) - infered = infered[0] - self.assertEqual(infered.name, 'type') - infered.as_string() # no crash test - - def test_inspect_transform_module(self): - # ensure no cached version of the time module - MANAGER._mod_file_cache.pop(('time', None), None) - MANAGER.astroid_cache.pop('time', None) - def transform_time(node): - if node.name == 'time': - node.transformed = True - MANAGER.register_transform(nodes.Module, transform_time) - try: - time_ast = MANAGER.ast_from_module_name('time') - self.assertTrue(getattr(time_ast, 'transformed', False)) - finally: - MANAGER.unregister_transform(nodes.Module, transform_time) - - def test_package_name(self): - """test base properties and method of a astroid module""" - datap = self.builder.file_build(join(DATA, '__init__.py'), 'data') - self.assertEqual(datap.name, 'data') - self.assertEqual(datap.package, 1) - datap = self.builder.file_build(join(DATA, '__init__.py'), 'data.__init__') - self.assertEqual(datap.name, 'data') - self.assertEqual(datap.package, 1) - - def test_yield_parent(self): - """check if we added discard nodes as yield parent (w/ compiler)""" - data = """ -def yiell(): - yield 0 - if noe: - yield more -""" - func = self.builder.string_build(data).body[0] - self.assertIsInstance(func, nodes.Function) - stmt = func.body[0] - self.assertIsInstance(stmt, nodes.Discard) - self.assertIsInstance(stmt.value, nodes.Yield) - self.assertIsInstance(func.body[1].body[0], nodes.Discard) - self.assertIsInstance(func.body[1].body[0].value, nodes.Yield) - - def test_object(self): - obj_ast = self.builder.inspect_build(object) - self.assertIn('__setattr__', obj_ast) - - def test_newstyle_detection(self): - data = ''' -class A: - "old style" - -class B(A): - "old style" - -class C(object): - "new style" - -class D(C): - "new style" - -__metaclass__ = type - -class E(A): - "old style" - -class F: - "new style" -''' - mod_ast = self.builder.string_build(data, __name__, __file__) - if PY3K: - self.assertTrue(mod_ast['A'].newstyle) - self.assertTrue(mod_ast['B'].newstyle) - self.assertTrue(mod_ast['E'].newstyle) - else: - self.assertFalse(mod_ast['A'].newstyle) - self.assertFalse(mod_ast['B'].newstyle) - self.assertFalse(mod_ast['E'].newstyle) - self.assertTrue(mod_ast['C'].newstyle) - self.assertTrue(mod_ast['D'].newstyle) - self.assertTrue(mod_ast['F'].newstyle) - - def test_globals(self): - data = ''' -CSTE = 1 - -def update_global(): - global CSTE - CSTE += 1 - -def global_no_effect(): - global CSTE2 - print (CSTE) -''' - astroid = self.builder.string_build(data, __name__, __file__) - self.assertEqual(len(astroid.getattr('CSTE')), 2) - self.assertIsInstance(astroid.getattr('CSTE')[0], nodes.AssName) - self.assertEqual(astroid.getattr('CSTE')[0].fromlineno, 2) - self.assertEqual(astroid.getattr('CSTE')[1].fromlineno, 6) - self.assertRaises(NotFoundError, - astroid.getattr, 'CSTE2') - self.assertRaises(InferenceError, - partial(next, astroid['global_no_effect'].ilookup('CSTE2'))) - - def test_socket_build(self): - import socket - astroid = self.builder.module_build(socket) - # XXX just check the first one. Actually 3 objects are inferred (look at - # the socket module) but the last one as those attributes dynamically - # set and astroid is missing this. - for fclass in astroid.igetattr('socket'): - #print fclass.root().name, fclass.name, fclass.lineno - self.assertIn('connect', fclass) - self.assertIn('send', fclass) - self.assertIn('close', fclass) - break - - def test_gen_expr_var_scope(self): - data = 'l = list(n for n in range(10))\n' - astroid = self.builder.string_build(data, __name__, __file__) - # n unavailable outside gen expr scope - self.assertNotIn('n', astroid) - # test n is inferable anyway - n = get_name_node(astroid, 'n') - self.assertIsNot(n.scope(), astroid) - self.assertEqual([i.__class__ for i in n.infer()], - [YES.__class__]) - - def test_no_future_imports(self): - mod = test_utils.build_module("import sys") - self.assertEqual(set(), mod.future_imports) - - def test_future_imports(self): - mod = test_utils.build_module("from __future__ import print_function") - self.assertEqual(set(['print_function']), mod.future_imports) - - def test_two_future_imports(self): - mod = test_utils.build_module(""" - from __future__ import print_function - from __future__ import absolute_import - """) - self.assertEqual(set(['print_function', 'absolute_import']), mod.future_imports) - -class FileBuildTC(unittest.TestCase): - module = builder.AstroidBuilder().file_build( - join(DATA, 'module.py'), - '%s.module' % (data.__name__,)) - - def test_module_base_props(self): - """test base properties and method of a astroid module""" - module = self.module - self.assertEqual(module.name, '%s.module' % (data.__name__)) - self.assertEqual(module.doc, "test module for astroid\n") - self.assertEqual(module.fromlineno, 0) - self.assertIsNone(module.parent) - self.assertEqual(module.frame(), module) - self.assertEqual(module.root(), module) - self.assertEqual(module.file, join(abspath(data.__path__[0]), 'module.py')) - self.assertEqual(module.pure_python, 1) - self.assertEqual(module.package, 0) - self.assertFalse(module.is_statement) - self.assertEqual(module.statement(), module) - self.assertEqual(module.statement(), module) - - def test_module_locals(self): - """test the 'locals' dictionary of a astroid module""" - module = self.module - _locals = module.locals - self.assertIs(_locals, module.globals) - keys = sorted(_locals.keys()) - should = ['MY_DICT', 'YO', 'YOUPI', - '__revision__', 'global_access','modutils', 'four_args', - 'os', 'redirect', 'pb', 'LocalsVisitor', 'ASTWalker'] - should.sort() - self.assertEqual(keys, should) - - def test_function_base_props(self): - """test base properties and method of a astroid function""" - module = self.module - function = module['global_access'] - self.assertEqual(function.name, 'global_access') - self.assertEqual(function.doc, 'function test') - self.assertEqual(function.fromlineno, 11) - self.assertTrue(function.parent) - self.assertEqual(function.frame(), function) - self.assertEqual(function.parent.frame(), module) - self.assertEqual(function.root(), module) - self.assertEqual([n.name for n in function.args.args], ['key', 'val']) - self.assertEqual(function.type, 'function') - - def test_function_locals(self): - """test the 'locals' dictionary of a astroid function""" - _locals = self.module['global_access'].locals - self.assertEqual(len(_locals), 4) - keys = sorted(_locals.keys()) - self.assertEqual(keys, ['i', 'key', 'local', 'val']) - - def test_class_base_props(self): - """test base properties and method of a astroid class""" - module = self.module - klass = module['YO'] - self.assertEqual(klass.name, 'YO') - self.assertEqual(klass.doc, 'hehe') - self.assertEqual(klass.fromlineno, 25) - self.assertTrue(klass.parent) - self.assertEqual(klass.frame(), klass) - self.assertEqual(klass.parent.frame(), module) - self.assertEqual(klass.root(), module) - self.assertEqual(klass.basenames, []) - if PY3K: - self.assertTrue(klass.newstyle) - else: - self.assertFalse(klass.newstyle) - - def test_class_locals(self): - """test the 'locals' dictionary of a astroid class""" - module = self.module - klass1 = module['YO'] - locals1 = klass1.locals - keys = sorted(locals1.keys()) - self.assertEqual(keys, ['__init__', 'a']) - klass2 = module['YOUPI'] - locals2 = klass2.locals - keys = locals2.keys() - self.assertEqual(sorted(keys), - ['__init__', 'class_attr', 'class_method', - 'method', 'static_method']) - - def test_class_instance_attrs(self): - module = self.module - klass1 = module['YO'] - klass2 = module['YOUPI'] - self.assertEqual(list(klass1.instance_attrs.keys()), ['yo']) - self.assertEqual(list(klass2.instance_attrs.keys()), ['member']) - - def test_class_basenames(self): - module = self.module - klass1 = module['YO'] - klass2 = module['YOUPI'] - self.assertEqual(klass1.basenames, []) - self.assertEqual(klass2.basenames, ['YO']) - - def test_method_base_props(self): - """test base properties and method of a astroid method""" - klass2 = self.module['YOUPI'] - # "normal" method - method = klass2['method'] - self.assertEqual(method.name, 'method') - self.assertEqual([n.name for n in method.args.args], ['self']) - self.assertEqual(method.doc, 'method test') - self.assertEqual(method.fromlineno, 47) - self.assertEqual(method.type, 'method') - # class method - method = klass2['class_method'] - self.assertEqual([n.name for n in method.args.args], ['cls']) - self.assertEqual(method.type, 'classmethod') - # static method - method = klass2['static_method'] - self.assertEqual(method.args.args, []) - self.assertEqual(method.type, 'staticmethod') - - def test_method_locals(self): - """test the 'locals' dictionary of a astroid method""" - method = self.module['YOUPI']['method'] - _locals = method.locals - keys = sorted(_locals) - if sys.version_info < (3, 0): - self.assertEqual(len(_locals), 5) - self.assertEqual(keys, ['a', 'autre', 'b', 'local', 'self']) - else:# ListComp variables are no more accessible outside - self.assertEqual(len(_locals), 3) - self.assertEqual(keys, ['autre', 'local', 'self']) - - -class ModuleBuildTC(FileBuildTC): - - def setUp(self): - abuilder = builder.AstroidBuilder() - self.module = abuilder.module_build(test_module) - - -class MoreTC(unittest.TestCase): - - def setUp(self): - self.builder = builder.AstroidBuilder() - - def test_infered_build(self): - code = '''class A: pass -A.type = "class" - -def A_ass_type(self): - print (self) -A.ass_type = A_ass_type - ''' - astroid = self.builder.string_build(code) - lclass = list(astroid.igetattr('A')) - self.assertEqual(len(lclass), 1) - lclass = lclass[0] - self.assertIn('ass_type', lclass.locals) - self.assertIn('type', lclass.locals) - - def test_augassign_attr(self): - astroid = self.builder.string_build("""class Counter: - v = 0 - def inc(self): - self.v += 1 - """, __name__, __file__) - # Check self.v += 1 generate AugAssign(AssAttr(...)), not AugAssign(GetAttr(AssName...)) - - def test_dumb_module(self): - astroid = self.builder.string_build("pouet") - - def test_infered_dont_pollute(self): - code = ''' -def func(a=None): - a.custom_attr = 0 -def func2(a={}): - a.custom_attr = 0 - ''' - astroid = self.builder.string_build(code) - nonetype = nodes.const_factory(None) - self.assertNotIn('custom_attr', nonetype.locals) - self.assertNotIn('custom_attr', nonetype.instance_attrs) - nonetype = nodes.const_factory({}) - self.assertNotIn('custom_attr', nonetype.locals) - self.assertNotIn('custom_attr', nonetype.instance_attrs) - - - def test_asstuple(self): - code = 'a, b = range(2)' - astroid = self.builder.string_build(code) - self.assertIn('b', astroid.locals) - code = ''' -def visit_if(self, node): - node.test, body = node.tests[0] -''' - astroid = self.builder.string_build(code) - self.assertIn('body', astroid['visit_if'].locals) - - def test_build_constants(self): - '''test expected values of constants after rebuilding''' - code = ''' -def func(): - return None - return - return 'None' -''' - astroid = self.builder.string_build(code) - none, nothing, chain = [ret.value for ret in astroid.body[0].body] - self.assertIsInstance(none, nodes.Const) - self.assertIsNone(none.value) - self.assertIsNone(nothing) - self.assertIsInstance(chain, nodes.Const) - self.assertEqual(chain.value, 'None') - - - def test_lgc_classproperty(self): - '''test expected values of constants after rebuilding''' - code = ''' -from logilab.common.decorators import classproperty - -class A(object): - @classproperty - def hop(cls): - return None -''' - astroid = self.builder.string_build(code) - self.assertEqual(astroid['A']['hop'].type, 'classmethod') - - -if sys.version_info < (3, 0): - guess_encoding = builder._guess_encoding - - class TestGuessEncoding(unittest.TestCase): - - def testEmacs(self): - e = guess_encoding('# -*- coding: UTF-8 -*-') - self.assertEqual(e, 'UTF-8') - e = guess_encoding('# -*- coding:UTF-8 -*-') - self.assertEqual(e, 'UTF-8') - e = guess_encoding(''' - ### -*- coding: ISO-8859-1 -*- - ''') - self.assertEqual(e, 'ISO-8859-1') - e = guess_encoding(''' - - ### -*- coding: ISO-8859-1 -*- - ''') - self.assertIsNone(e) - - def testVim(self): - e = guess_encoding('# vim:fileencoding=UTF-8') - self.assertEqual(e, 'UTF-8') - e = guess_encoding(''' - ### vim:fileencoding=ISO-8859-1 - ''') - self.assertEqual(e, 'ISO-8859-1') - e = guess_encoding(''' - - ### vim:fileencoding= ISO-8859-1 - ''') - self.assertIsNone(e) - - def test_wrong_coding(self): - # setting "coding" varaible - e = guess_encoding("coding = UTF-8") - self.assertIsNone(e) - # setting a dictionnary entry - e = guess_encoding("coding:UTF-8") - self.assertIsNone(e) - # setting an arguement - e = guess_encoding("def do_something(a_word_with_coding=None):") - self.assertIsNone(e) - - - def testUTF8(self): - e = guess_encoding('\xef\xbb\xbf any UTF-8 data') - self.assertEqual(e, 'UTF-8') - e = guess_encoding(' any UTF-8 data \xef\xbb\xbf') - self.assertIsNone(e) - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_inference.py b/test/unittest_inference.py deleted file mode 100644 index 9b7b472..0000000 --- a/test/unittest_inference.py +++ /dev/null @@ -1,1383 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""tests for the astroid inference capabilities -""" -from os.path import join, dirname, abspath -import sys -from functools import partial -from textwrap import dedent -import unittest - -import six - -from astroid import InferenceError, builder, nodes -from astroid.inference import infer_end as inference_infer_end -from astroid.bases import YES, Instance, BoundMethod, UnboundMethod,\ - path_wrapper, BUILTINS -from astroid.test_utils import require_version - - -def get_name_node(start_from, name, index=0): - return [n for n in start_from.nodes_of_class(nodes.Name) if n.name == name][index] - -def get_node_of_class(start_from, klass): - return next(start_from.nodes_of_class(klass)) - -builder = builder.AstroidBuilder() - -class InferenceUtilsTC(unittest.TestCase): - - def test_path_wrapper(self): - def infer_default(self, *args): - raise InferenceError - infer_default = path_wrapper(infer_default) - infer_end = path_wrapper(inference_infer_end) - self.assertRaises(InferenceError, - partial(next, infer_default(1))) - self.assertEqual(next(infer_end(1)), 1) - -if sys.version_info < (3, 0): - EXC_MODULE = 'exceptions' -else: - EXC_MODULE = BUILTINS - - -class InferenceTC(unittest.TestCase): - - CODE = ''' - -class C(object): - "new style" - attr = 4 - - def meth1(self, arg1, optarg=0): - var = object() - print ("yo", arg1, optarg) - self.iattr = "hop" - return var - - def meth2(self): - self.meth1(*self.meth3) - - def meth3(self, d=attr): - b = self.attr - c = self.iattr - return b, c - -ex = Exception("msg") -v = C().meth1(1) -m_unbound = C.meth1 -m_bound = C().meth1 -a, b, c = ex, 1, "bonjour" -[d, e, f] = [ex, 1.0, ("bonjour", v)] -g, h = f -i, (j, k) = "glup", f - -a, b= b, a # Gasp ! -''' - - astroid = builder.string_build(CODE, __name__, __file__) - - def test_module_inference(self): - infered = self.astroid.infer() - obj = next(infered) - self.assertEqual(obj.name, __name__) - self.assertEqual(obj.root().name, __name__) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_class_inference(self): - infered = self.astroid['C'].infer() - obj = next(infered) - self.assertEqual(obj.name, 'C') - self.assertEqual(obj.root().name, __name__) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_function_inference(self): - infered = self.astroid['C']['meth1'].infer() - obj = next(infered) - self.assertEqual(obj.name, 'meth1') - self.assertEqual(obj.root().name, __name__) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_builtin_name_inference(self): - infered = self.astroid['C']['meth1']['var'].infer() - var = next(infered) - self.assertEqual(var.name, 'object') - self.assertEqual(var.root().name, BUILTINS) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_tupleassign_name_inference(self): - infered = self.astroid['a'].infer() - exc = next(infered) - self.assertIsInstance(exc, Instance) - self.assertEqual(exc.name, 'Exception') - self.assertEqual(exc.root().name, EXC_MODULE) - self.assertRaises(StopIteration, partial(next, infered)) - infered = self.astroid['b'].infer() - const = next(infered) - self.assertIsInstance(const, nodes.Const) - self.assertEqual(const.value, 1) - self.assertRaises(StopIteration, partial(next, infered)) - infered = self.astroid['c'].infer() - const = next(infered) - self.assertIsInstance(const, nodes.Const) - self.assertEqual(const.value, "bonjour") - self.assertRaises(StopIteration, partial(next, infered)) - - def test_listassign_name_inference(self): - infered = self.astroid['d'].infer() - exc = next(infered) - self.assertIsInstance(exc, Instance) - self.assertEqual(exc.name, 'Exception') - self.assertEqual(exc.root().name, EXC_MODULE) - self.assertRaises(StopIteration, partial(next, infered)) - infered = self.astroid['e'].infer() - const = next(infered) - self.assertIsInstance(const, nodes.Const) - self.assertEqual(const.value, 1.0) - self.assertRaises(StopIteration, partial(next, infered)) - infered = self.astroid['f'].infer() - const = next(infered) - self.assertIsInstance(const, nodes.Tuple) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_advanced_tupleassign_name_inference1(self): - infered = self.astroid['g'].infer() - const = next(infered) - self.assertIsInstance(const, nodes.Const) - self.assertEqual(const.value, "bonjour") - self.assertRaises(StopIteration, partial(next, infered)) - infered = self.astroid['h'].infer() - var = next(infered) - self.assertEqual(var.name, 'object') - self.assertEqual(var.root().name, BUILTINS) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_advanced_tupleassign_name_inference2(self): - infered = self.astroid['i'].infer() - const = next(infered) - self.assertIsInstance(const, nodes.Const) - self.assertEqual(const.value, u"glup") - self.assertRaises(StopIteration, partial(next, infered)) - infered = self.astroid['j'].infer() - const = next(infered) - self.assertIsInstance(const, nodes.Const) - self.assertEqual(const.value, "bonjour") - self.assertRaises(StopIteration, partial(next, infered)) - infered = self.astroid['k'].infer() - var = next(infered) - self.assertEqual(var.name, 'object') - self.assertEqual(var.root().name, BUILTINS) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_swap_assign_inference(self): - infered = self.astroid.locals['a'][1].infer() - const = next(infered) - self.assertIsInstance(const, nodes.Const) - self.assertEqual(const.value, 1) - self.assertRaises(StopIteration, partial(next, infered)) - infered = self.astroid.locals['b'][1].infer() - exc = next(infered) - self.assertIsInstance(exc, Instance) - self.assertEqual(exc.name, 'Exception') - self.assertEqual(exc.root().name, EXC_MODULE) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_getattr_inference1(self): - infered = self.astroid['ex'].infer() - exc = next(infered) - self.assertIsInstance(exc, Instance) - self.assertEqual(exc.name, 'Exception') - self.assertEqual(exc.root().name, EXC_MODULE) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_getattr_inference2(self): - infered = get_node_of_class(self.astroid['C']['meth2'], nodes.Getattr).infer() - meth1 = next(infered) - self.assertEqual(meth1.name, 'meth1') - self.assertEqual(meth1.root().name, __name__) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_getattr_inference3(self): - infered = self.astroid['C']['meth3']['b'].infer() - const = next(infered) - self.assertIsInstance(const, nodes.Const) - self.assertEqual(const.value, 4) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_getattr_inference4(self): - infered = self.astroid['C']['meth3']['c'].infer() - const = next(infered) - self.assertIsInstance(const, nodes.Const) - self.assertEqual(const.value, "hop") - self.assertRaises(StopIteration, partial(next, infered)) - - def test_callfunc_inference(self): - infered = self.astroid['v'].infer() - meth1 = next(infered) - self.assertIsInstance(meth1, Instance) - self.assertEqual(meth1.name, 'object') - self.assertEqual(meth1.root().name, BUILTINS) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_unbound_method_inference(self): - infered = self.astroid['m_unbound'].infer() - meth1 = next(infered) - self.assertIsInstance(meth1, UnboundMethod) - self.assertEqual(meth1.name, 'meth1') - self.assertEqual(meth1.parent.frame().name, 'C') - self.assertRaises(StopIteration, partial(next, infered)) - - def test_bound_method_inference(self): - infered = self.astroid['m_bound'].infer() - meth1 = next(infered) - self.assertIsInstance(meth1, BoundMethod) - self.assertEqual(meth1.name, 'meth1') - self.assertEqual(meth1.parent.frame().name, 'C') - self.assertRaises(StopIteration, partial(next, infered)) - - def test_args_default_inference1(self): - optarg = get_name_node(self.astroid['C']['meth1'], 'optarg') - infered = optarg.infer() - obj1 = next(infered) - self.assertIsInstance(obj1, nodes.Const) - self.assertEqual(obj1.value, 0) - obj1 = next(infered) - self.assertIs(obj1, YES, obj1) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_args_default_inference2(self): - infered = self.astroid['C']['meth3'].ilookup('d') - obj1 = next(infered) - self.assertIsInstance(obj1, nodes.Const) - self.assertEqual(obj1.value, 4) - obj1 = next(infered) - self.assertIs(obj1, YES, obj1) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_inference_restrictions(self): - infered = get_name_node(self.astroid['C']['meth1'], 'arg1').infer() - obj1 = next(infered) - self.assertIs(obj1, YES, obj1) - self.assertRaises(StopIteration, partial(next, infered)) - - def test_ancestors_inference(self): - code = ''' -class A: - pass - -class A(A): - pass - ''' - astroid = builder.string_build(code, __name__, __file__) - a1 = astroid.locals['A'][0] - a2 = astroid.locals['A'][1] - a2_ancestors = list(a2.ancestors()) - self.assertEqual(len(a2_ancestors), 1) - self.assertIs(a2_ancestors[0], a1) - - def test_ancestors_inference2(self): - code = ''' -class A: - pass - -class B(A): pass - -class A(B): - pass - ''' - astroid = builder.string_build(code, __name__, __file__) - a1 = astroid.locals['A'][0] - a2 = astroid.locals['A'][1] - a2_ancestors = list(a2.ancestors()) - self.assertEqual(len(a2_ancestors), 2) - self.assertIs(a2_ancestors[0], astroid.locals['B'][0]) - self.assertIs(a2_ancestors[1], a1) - - - def test_f_arg_f(self): - code = ''' -def f(f=1): - return f - -a = f() - ''' - astroid = builder.string_build(code, __name__, __file__) - a = astroid['a'] - a_infered = a.infered() - self.assertEqual(a_infered[0].value, 1) - self.assertEqual(len(a_infered), 1) - - def test_exc_ancestors(self): - code = ''' -def f(): - raise NotImplementedError - ''' - astroid = builder.string_build(code, __name__, __file__) - error = next(astroid.nodes_of_class(nodes.Name)) - nie = error.infered()[0] - self.assertIsInstance(nie, nodes.Class) - nie_ancestors = [c.name for c in nie.ancestors()] - if sys.version_info < (3, 0): - self.assertEqual(nie_ancestors, ['RuntimeError', 'StandardError', 'Exception', 'BaseException', 'object']) - else: - self.assertEqual(nie_ancestors, ['RuntimeError', 'Exception', 'BaseException', 'object']) - - def test_except_inference(self): - code = ''' -try: - print (hop) -except NameError, ex: - ex1 = ex -except Exception, ex: - ex2 = ex - raise - ''' - if sys.version_info >= (3, 0): - code = code.replace(', ex:', ' as ex:') - astroid = builder.string_build(code, __name__, __file__) - ex1 = astroid['ex1'] - ex1_infer = ex1.infer() - ex1 = next(ex1_infer) - self.assertIsInstance(ex1, Instance) - self.assertEqual(ex1.name, 'NameError') - self.assertRaises(StopIteration, partial(next, ex1_infer)) - ex2 = astroid['ex2'] - ex2_infer = ex2.infer() - ex2 = next(ex2_infer) - self.assertIsInstance(ex2, Instance) - self.assertEqual(ex2.name, 'Exception') - self.assertRaises(StopIteration, partial(next, ex2_infer)) - - def test_del1(self): - code = ''' -del undefined_attr - ''' - delete = builder.string_build(code, __name__, __file__).body[0] - self.assertRaises(InferenceError, delete.infer) - - def test_del2(self): - code = ''' -a = 1 -b = a -del a -c = a -a = 2 -d = a - ''' - astroid = builder.string_build(code, __name__, __file__) - n = astroid['b'] - n_infer = n.infer() - infered = next(n_infer) - self.assertIsInstance(infered, nodes.Const) - self.assertEqual(infered.value, 1) - self.assertRaises(StopIteration, partial(next, n_infer)) - n = astroid['c'] - n_infer = n.infer() - self.assertRaises(InferenceError, partial(next, n_infer)) - n = astroid['d'] - n_infer = n.infer() - infered = next(n_infer) - self.assertIsInstance(infered, nodes.Const) - self.assertEqual(infered.value, 2) - self.assertRaises(StopIteration, partial(next, n_infer)) - - def test_builtin_types(self): - code = ''' -l = [1] -t = (2,) -d = {} -s = '' -s2 = '_' - ''' - astroid = builder.string_build(code, __name__, __file__) - n = astroid['l'] - infered = next(n.infer()) - self.assertIsInstance(infered, nodes.List) - self.assertIsInstance(infered, Instance) - self.assertEqual(infered.getitem(0).value, 1) - self.assertIsInstance(infered._proxied, nodes.Class) - self.assertEqual(infered._proxied.name, 'list') - self.assertIn('append', infered._proxied.locals) - n = astroid['t'] - infered = next(n.infer()) - self.assertIsInstance(infered, nodes.Tuple) - self.assertIsInstance(infered, Instance) - self.assertEqual(infered.getitem(0).value, 2) - self.assertIsInstance(infered._proxied, nodes.Class) - self.assertEqual(infered._proxied.name, 'tuple') - n = astroid['d'] - infered = next(n.infer()) - self.assertIsInstance(infered, nodes.Dict) - self.assertIsInstance(infered, Instance) - self.assertIsInstance(infered._proxied, nodes.Class) - self.assertEqual(infered._proxied.name, 'dict') - self.assertIn('get', infered._proxied.locals) - n = astroid['s'] - infered = next(n.infer()) - self.assertIsInstance(infered, nodes.Const) - self.assertIsInstance(infered, Instance) - self.assertEqual(infered.name, 'str') - self.assertIn('lower', infered._proxied.locals) - n = astroid['s2'] - infered = next(n.infer()) - self.assertEqual(infered.getitem(0).value, '_') - - @require_version('2.7') - def test_builtin_types_py27(self): - code = 's = {1}' - astroid = builder.string_build(code, __name__, __file__) - n = astroid['s'] - infered = next(n.infer()) - self.assertIsInstance(infered, nodes.Set) - self.assertIsInstance(infered, Instance) - self.assertEqual(infered.name, 'set') - self.assertIn('remove', infered._proxied.locals) - - def test_unicode_type(self): - if sys.version_info >= (3, 0): - self.skipTest('unicode removed on py >= 3.0') - code = '''u = u""''' - astroid = builder.string_build(code, __name__, __file__) - n = astroid['u'] - infered = next(n.infer()) - self.assertIsInstance(infered, nodes.Const) - self.assertIsInstance(infered, Instance) - self.assertEqual(infered.name, 'unicode') - self.assertIn('lower', infered._proxied.locals) - - def test_descriptor_are_callable(self): - code = ''' -class A: - statm = staticmethod(open) - clsm = classmethod('whatever') - ''' - astroid = builder.string_build(code, __name__, __file__) - statm = next(astroid['A'].igetattr('statm')) - self.assertTrue(statm.callable()) - clsm = next(astroid['A'].igetattr('clsm')) - self.assertTrue(clsm.callable()) - - def test_bt_ancestor_crash(self): - code = ''' -class Warning(Warning): - pass - ''' - astroid = builder.string_build(code, __name__, __file__) - w = astroid['Warning'] - ancestors = w.ancestors() - ancestor = next(ancestors) - self.assertEqual(ancestor.name, 'Warning') - self.assertEqual(ancestor.root().name, EXC_MODULE) - ancestor = next(ancestors) - self.assertEqual(ancestor.name, 'Exception') - self.assertEqual(ancestor.root().name, EXC_MODULE) - ancestor = next(ancestors) - self.assertEqual(ancestor.name, 'BaseException') - self.assertEqual(ancestor.root().name, EXC_MODULE) - ancestor = next(ancestors) - self.assertEqual(ancestor.name, 'object') - self.assertEqual(ancestor.root().name, BUILTINS) - self.assertRaises(StopIteration, partial(next, ancestors)) - - def test_qqch(self): - code = ''' -from astroid.modutils import load_module_from_name -xxx = load_module_from_name('__pkginfo__') - ''' - astroid = builder.string_build(code, __name__, __file__) - xxx = astroid['xxx'] - self.assertSetEqual(set(n.__class__ for n in xxx.infered()), - set([nodes.Const, YES.__class__])) - - def test_method_argument(self): - code = ''' -class ErudiEntitySchema: - """a entity has a type, a set of subject and or object relations""" - def __init__(self, e_type, **kwargs): - kwargs['e_type'] = e_type.capitalize().encode() - - def meth(self, e_type, *args, **kwargs): - kwargs['e_type'] = e_type.capitalize().encode() - print(args) - ''' - astroid = builder.string_build(code, __name__, __file__) - arg = get_name_node(astroid['ErudiEntitySchema']['__init__'], 'e_type') - self.assertEqual([n.__class__ for n in arg.infer()], - [YES.__class__]) - arg = get_name_node(astroid['ErudiEntitySchema']['__init__'], 'kwargs') - self.assertEqual([n.__class__ for n in arg.infer()], - [nodes.Dict]) - arg = get_name_node(astroid['ErudiEntitySchema']['meth'], 'e_type') - self.assertEqual([n.__class__ for n in arg.infer()], - [YES.__class__]) - arg = get_name_node(astroid['ErudiEntitySchema']['meth'], 'args') - self.assertEqual([n.__class__ for n in arg.infer()], - [nodes.Tuple]) - arg = get_name_node(astroid['ErudiEntitySchema']['meth'], 'kwargs') - self.assertEqual([n.__class__ for n in arg.infer()], - [nodes.Dict]) - - - def test_tuple_then_list(self): - code = ''' -def test_view(rql, vid, tags=()): - tags = list(tags) - tags.append(vid) - ''' - astroid = builder.string_build(code, __name__, __file__) - name = get_name_node(astroid['test_view'], 'tags', -1) - it = name.infer() - tags = next(it) - self.assertEqual(tags.__class__, Instance) - self.assertEqual(tags._proxied.name, 'list') - self.assertRaises(StopIteration, partial(next, it)) - - - - def test_mulassign_inference(self): - code = ''' - -def first_word(line): - """Return the first word of a line""" - - return line.split()[0] - -def last_word(line): - """Return last word of a line""" - - return line.split()[-1] - -def process_line(word_pos): - """Silly function: returns (ok, callable) based on argument. - - For test purpose only. - """ - - if word_pos > 0: - return (True, first_word) - elif word_pos < 0: - return (True, last_word) - else: - return (False, None) - -if __name__ == '__main__': - - line_number = 0 - for a_line in file('test_callable.py'): - tupletest = process_line(line_number) - (ok, fct) = process_line(line_number) - if ok: - fct(a_line) -''' - astroid = builder.string_build(code, __name__, __file__) - self.assertEqual(len(list(astroid['process_line'].infer_call_result( - None))), 3) - self.assertEqual(len(list(astroid['tupletest'].infer())), 3) - values = ['Function(first_word)', 'Function(last_word)', 'Const(NoneType)'] - self.assertEqual([str(infered) - for infered in astroid['fct'].infer()], values) - - def test_float_complex_ambiguity(self): - code = ''' -def no_conjugate_member(magic_flag): - """should not raise E1101 on something.conjugate""" - if magic_flag: - something = 1.0 - else: - something = 1.0j - if isinstance(something, float): - return something - return something.conjugate() - ''' - astroid = builder.string_build(code, __name__, __file__) - self.assertEqual([i.value for i in - astroid['no_conjugate_member'].ilookup('something')], [1.0, 1.0j]) - self.assertEqual([i.value for i in - get_name_node(astroid, 'something', -1).infer()], [1.0, 1.0j]) - - def test_lookup_cond_branches(self): - code = ''' -def no_conjugate_member(magic_flag): - """should not raise E1101 on something.conjugate""" - something = 1.0 - if magic_flag: - something = 1.0j - return something.conjugate() - ''' - astroid = builder.string_build(code, __name__, __file__) - self.assertEqual([i.value for i in - get_name_node(astroid, 'something', -1).infer()], [1.0, 1.0j]) - - - def test_simple_subscript(self): - code = ''' -a = [1, 2, 3][0] -b = (1, 2, 3)[1] -c = (1, 2, 3)[-1] -d = a + b + c -print (d) -e = {'key': 'value'} -f = e['key'] -print (f) - ''' - astroid = builder.string_build(code, __name__, __file__) - self.assertEqual([i.value for i in - get_name_node(astroid, 'a', -1).infer()], [1]) - self.assertEqual([i.value for i in - get_name_node(astroid, 'b', -1).infer()], [2]) - self.assertEqual([i.value for i in - get_name_node(astroid, 'c', -1).infer()], [3]) - self.assertEqual([i.value for i in - get_name_node(astroid, 'd', -1).infer()], [6]) - self.assertEqual([i.value for i in - get_name_node(astroid, 'f', -1).infer()], ['value']) - - #def test_simple_tuple(self): - #"""test case for a simple tuple value""" - ## XXX tuple inference is not implemented ... - #code = """ -#a = (1,) -#b = (22,) -#some = a + b -#""" - #astroid = builder.string_build(code, __name__, __file__) - #self.assertEqual(astroid['some'].infer.next().as_string(), "(1, 22)") - - def test_simple_for(self): - code = ''' -for a in [1, 2, 3]: - print (a) -for b,c in [(1,2), (3,4)]: - print (b) - print (c) - -print ([(d,e) for e,d in ([1,2], [3,4])]) - ''' - astroid = builder.string_build(code, __name__, __file__) - self.assertEqual([i.value for i in - get_name_node(astroid, 'a', -1).infer()], [1, 2, 3]) - self.assertEqual([i.value for i in - get_name_node(astroid, 'b', -1).infer()], [1, 3]) - self.assertEqual([i.value for i in - get_name_node(astroid, 'c', -1).infer()], [2, 4]) - self.assertEqual([i.value for i in - get_name_node(astroid, 'd', -1).infer()], [2, 4]) - self.assertEqual([i.value for i in - get_name_node(astroid, 'e', -1).infer()], [1, 3]) - - - def test_simple_for_genexpr(self): - code = ''' -print ((d,e) for e,d in ([1,2], [3,4])) - ''' - astroid = builder.string_build(code, __name__, __file__) - self.assertEqual([i.value for i in - get_name_node(astroid, 'd', -1).infer()], [2, 4]) - self.assertEqual([i.value for i in - get_name_node(astroid, 'e', -1).infer()], [1, 3]) - - - def test_builtin_help(self): - code = ''' -help() - ''' - # XXX failing since __builtin__.help assignment has - # been moved into a function... - astroid = builder.string_build(code, __name__, __file__) - node = get_name_node(astroid, 'help', -1) - infered = list(node.infer()) - self.assertEqual(len(infered), 1, infered) - self.assertIsInstance(infered[0], Instance) - self.assertEqual(infered[0].name, "_Helper") - - def test_builtin_open(self): - code = ''' -open("toto.txt") - ''' - astroid = builder.string_build(code, __name__, __file__) - node = get_name_node(astroid, 'open', -1) - infered = list(node.infer()) - self.assertEqual(len(infered), 1) - if hasattr(sys, 'pypy_version_info'): - self.assertIsInstance(infered[0], nodes.Class) - self.assertEqual(infered[0].name, 'file') - else: - self.assertIsInstance(infered[0], nodes.Function) - self.assertEqual(infered[0].name, 'open') - - def test_callfunc_context_func(self): - code = ''' -def mirror(arg=None): - return arg - -un = mirror(1) - ''' - astroid = builder.string_build(code, __name__, __file__) - infered = list(astroid.igetattr('un')) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.Const) - self.assertEqual(infered[0].value, 1) - - def test_callfunc_context_lambda(self): - code = ''' -mirror = lambda x=None: x - -un = mirror(1) - ''' - astroid = builder.string_build(code, __name__, __file__) - infered = list(astroid.igetattr('mirror')) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.Lambda) - infered = list(astroid.igetattr('un')) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.Const) - self.assertEqual(infered[0].value, 1) - - def test_factory_method(self): - code = ''' -class Super(object): - @classmethod - def instance(cls): - return cls() - -class Sub(Super): - def method(self): - print ('method called') - -sub = Sub.instance() - ''' - astroid = builder.string_build(code, __name__, __file__) - infered = list(astroid.igetattr('sub')) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], Instance) - self.assertEqual(infered[0]._proxied.name, 'Sub') - - - def test_import_as(self): - code = ''' -import os.path as osp -print (osp.dirname(__file__)) - -from os.path import exists as e -assert e(__file__) - -from new import code as make_code -print (make_code) - ''' - astroid = builder.string_build(code, __name__, __file__) - infered = list(astroid.igetattr('osp')) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.Module) - self.assertEqual(infered[0].name, 'os.path') - infered = list(astroid.igetattr('e')) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.Function) - self.assertEqual(infered[0].name, 'exists') - if sys.version_info >= (3, 0): - self.skipTest(' module has been removed') - infered = list(astroid.igetattr('make_code')) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], Instance) - self.assertEqual(str(infered[0]), - 'Instance of %s.type' % BUILTINS) - - def _test_const_infered(self, node, value): - infered = list(node.infer()) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.Const) - self.assertEqual(infered[0].value, value) - - def test_unary_not(self): - for code in ('a = not (1,); b = not ()', - 'a = not {1:2}; b = not {}'): - astroid = builder.string_build(code, __name__, __file__) - self._test_const_infered(astroid['a'], False) - self._test_const_infered(astroid['b'], True) - - def test_binary_op_int_add(self): - astroid = builder.string_build('a = 1 + 2', __name__, __file__) - self._test_const_infered(astroid['a'], 3) - - def test_binary_op_int_sub(self): - astroid = builder.string_build('a = 1 - 2', __name__, __file__) - self._test_const_infered(astroid['a'], -1) - - def test_binary_op_float_div(self): - astroid = builder.string_build('a = 1 / 2.', __name__, __file__) - self._test_const_infered(astroid['a'], 1 / 2.) - - def test_binary_op_str_mul(self): - astroid = builder.string_build('a = "*" * 40', __name__, __file__) - self._test_const_infered(astroid['a'], "*" * 40) - - def test_binary_op_bitand(self): - astroid = builder.string_build('a = 23&20', __name__, __file__) - self._test_const_infered(astroid['a'], 23&20) - - def test_binary_op_bitor(self): - astroid = builder.string_build('a = 23|8', __name__, __file__) - self._test_const_infered(astroid['a'], 23|8) - - def test_binary_op_bitxor(self): - astroid = builder.string_build('a = 23^9', __name__, __file__) - self._test_const_infered(astroid['a'], 23^9) - - def test_binary_op_shiftright(self): - astroid = builder.string_build('a = 23 >>1', __name__, __file__) - self._test_const_infered(astroid['a'], 23>>1) - - def test_binary_op_shiftleft(self): - astroid = builder.string_build('a = 23 <<1', __name__, __file__) - self._test_const_infered(astroid['a'], 23<<1) - - - def test_binary_op_list_mul(self): - for code in ('a = [[]] * 2', 'a = 2 * [[]]'): - astroid = builder.string_build(code, __name__, __file__) - infered = list(astroid['a'].infer()) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.List) - self.assertEqual(len(infered[0].elts), 2) - self.assertIsInstance(infered[0].elts[0], nodes.List) - self.assertIsInstance(infered[0].elts[1], nodes.List) - - def test_binary_op_list_mul_none(self): - 'test correct handling on list multiplied by None' - astroid = builder.string_build( 'a = [1] * None\nb = [1] * "r"') - infered = astroid['a'].infered() - self.assertEqual(len(infered), 1) - self.assertEqual(infered[0], YES) - infered = astroid['b'].infered() - self.assertEqual(len(infered), 1) - self.assertEqual(infered[0], YES) - - - def test_binary_op_tuple_add(self): - astroid = builder.string_build('a = (1,) + (2,)', __name__, __file__) - infered = list(astroid['a'].infer()) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.Tuple) - self.assertEqual(len(infered[0].elts), 2) - self.assertEqual(infered[0].elts[0].value, 1) - self.assertEqual(infered[0].elts[1].value, 2) - - def test_binary_op_custom_class(self): - code = ''' -class myarray: - def __init__(self, array): - self.array = array - def __mul__(self, x): - return myarray([2,4,6]) - def astype(self): - return "ASTYPE" - -def randint(maximum): - if maximum is not None: - return myarray([1,2,3]) * 2 - else: - return int(5) - -x = randint(1) - ''' - astroid = builder.string_build(code, __name__, __file__) - infered = list(astroid.igetattr('x')) - self.assertEqual(len(infered), 2) - value = [str(v) for v in infered] - # The __name__ trick here makes it work when invoked directly - # (__name__ == '__main__') and through pytest (__name__ == - # 'unittest_inference') - self.assertEqual(value, ['Instance of %s.myarray' % __name__, - 'Instance of %s.int' % BUILTINS]) - - def test_nonregr_lambda_arg(self): - code = ''' -def f(g = lambda: None): - g().x -''' - astroid = builder.string_build(code, __name__, __file__) - callfuncnode = astroid['f'].body[0].value.expr # 'g()' - infered = list(callfuncnode.infer()) - self.assertEqual(len(infered), 2, infered) - infered.remove(YES) - self.assertIsInstance(infered[0], nodes.Const) - self.assertIsNone(infered[0].value) - - def test_nonregr_getitem_empty_tuple(self): - code = ''' -def f(x): - a = ()[x] - ''' - astroid = builder.string_build(code, __name__, __file__) - infered = list(astroid['f'].ilookup('a')) - self.assertEqual(len(infered), 1) - self.assertEqual(infered[0], YES) - - def test_nonregr_instance_attrs(self): - """non regression for instance_attrs infinite loop : pylint / #4""" - - code = """ -class Foo(object): - - def set_42(self): - self.attr = 42 - -class Bar(Foo): - - def __init__(self): - self.attr = 41 - """ - astroid = builder.string_build(code, __name__, __file__) - foo_class = astroid['Foo'] - bar_class = astroid['Bar'] - bar_self = astroid['Bar']['__init__']['self'] - assattr = bar_class.instance_attrs['attr'][0] - self.assertEqual(len(foo_class.instance_attrs['attr']), 1) - self.assertEqual(len(bar_class.instance_attrs['attr']), 1) - self.assertEqual(bar_class.instance_attrs, {'attr': [assattr]}) - # call 'instance_attr' via 'Instance.getattr' to trigger the bug: - instance = bar_self.infered()[0] - _attr = instance.getattr('attr') - self.assertEqual(len(bar_class.instance_attrs['attr']), 1) - self.assertEqual(len(foo_class.instance_attrs['attr']), 1) - self.assertEqual(bar_class.instance_attrs, {'attr': [assattr]}) - - def test_python25_generator_exit(self): - sys.stderr = six.StringIO() - data = "b = {}[str(0)+''].a" - astroid = builder.string_build(data, __name__, __file__) - list(astroid['b'].infer()) - output = sys.stderr.getvalue() - # I have no idea how to test for this in another way... - self.assertNotIn("RuntimeError", output, "Exception exceptions.RuntimeError: 'generator ignored GeneratorExit' in ignored") - sys.stderr = sys.__stderr__ - - def test_python25_relative_import(self): - data = "from ...logilab.common import date; print (date)" - # !! FIXME also this relative import would not work 'in real' (no __init__.py in test/) - # the test works since we pretend we have a package by passing the full modname - astroid = builder.string_build(data, 'astroid.test.unittest_inference', __file__) - infered = next(get_name_node(astroid, 'date').infer()) - self.assertIsInstance(infered, nodes.Module) - self.assertEqual(infered.name, 'logilab.common.date') - - def test_python25_no_relative_import(self): - fname = join(abspath(dirname(__file__)), 'regrtest_data', 'package', 'absimport.py') - astroid = builder.file_build(fname, 'absimport') - self.assertTrue(astroid.absolute_import_activated(), True) - infered = next(get_name_node(astroid, 'import_package_subpackage_module').infer()) - # failed to import since absolute_import is activated - self.assertIs(infered, YES) - - def test_nonregr_absolute_import(self): - fname = join(abspath(dirname(__file__)), 'regrtest_data', 'absimp', 'string.py') - astroid = builder.file_build(fname, 'absimp.string') - self.assertTrue(astroid.absolute_import_activated(), True) - infered = next(get_name_node(astroid, 'string').infer()) - self.assertIsInstance(infered, nodes.Module) - self.assertEqual(infered.name, 'string') - self.assertIn('ascii_letters', infered.locals) - - def test_mechanize_open(self): - try: - import mechanize - except ImportError: - self.skipTest('require mechanize installed') - data = '''from mechanize import Browser -print (Browser) -b = Browser() -''' - astroid = builder.string_build(data, __name__, __file__) - browser = next(get_name_node(astroid, 'Browser').infer()) - self.assertIsInstance(browser, nodes.Class) - bopen = list(browser.igetattr('open')) - self.skipTest('the commit said: "huum, see that later"') - self.assertEqual(len(bopen), 1) - self.assertIsInstance(bopen[0], nodes.Function) - self.assertTrue(bopen[0].callable()) - b = next(get_name_node(astroid, 'b').infer()) - self.assertIsInstance(b, Instance) - bopen = list(b.igetattr('open')) - self.assertEqual(len(bopen), 1) - self.assertIsInstance(bopen[0], BoundMethod) - self.assertTrue(bopen[0].callable()) - - def test_property(self): - code = ''' -from smtplib import SMTP -class SendMailController(object): - - @property - def smtp(self): - return SMTP(mailhost, port) - - @property - def me(self): - return self - -my_smtp = SendMailController().smtp -my_me = SendMailController().me -''' - decorators = set(['%s.property' % BUILTINS]) - astroid = builder.string_build(code, __name__, __file__) - self.assertEqual(astroid['SendMailController']['smtp'].decoratornames(), - decorators) - propinfered = list(astroid.body[2].value.infer()) - self.assertEqual(len(propinfered), 1) - propinfered = propinfered[0] - self.assertIsInstance(propinfered, Instance) - self.assertEqual(propinfered.name, 'SMTP') - self.assertEqual(propinfered.root().name, 'smtplib') - self.assertEqual(astroid['SendMailController']['me'].decoratornames(), - decorators) - propinfered = list(astroid.body[3].value.infer()) - self.assertEqual(len(propinfered), 1) - propinfered = propinfered[0] - self.assertIsInstance(propinfered, Instance) - self.assertEqual(propinfered.name, 'SendMailController') - self.assertEqual(propinfered.root().name, __name__) - - - def test_im_func_unwrap(self): - code = ''' -class EnvBasedTC: - def pactions(self): - pass -pactions = EnvBasedTC.pactions.im_func -print (pactions) - -class EnvBasedTC2: - pactions = EnvBasedTC.pactions.im_func - print (pactions) - -''' - astroid = builder.string_build(code, __name__, __file__) - pactions = get_name_node(astroid, 'pactions') - infered = list(pactions.infer()) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.Function) - pactions = get_name_node(astroid['EnvBasedTC2'], 'pactions') - infered = list(pactions.infer()) - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.Function) - - def test_augassign(self): - code = ''' -a = 1 -a += 2 -print (a) -''' - astroid = builder.string_build(code, __name__, __file__) - infered = list(get_name_node(astroid, 'a').infer()) - - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], nodes.Const) - self.assertEqual(infered[0].value, 3) - - def test_nonregr_func_arg(self): - code = ''' -def foo(self, bar): - def baz(): - pass - def qux(): - return baz - spam = bar(None, qux) - print (spam) -''' - astroid = builder.string_build(code, __name__, __file__) - infered = list(get_name_node(astroid['foo'], 'spam').infer()) - self.assertEqual(len(infered), 1) - self.assertIs(infered[0], YES) - - def test_nonregr_func_global(self): - code = ''' -active_application = None - -def get_active_application(): - global active_application - return active_application - -class Application(object): - def __init__(self): - global active_application - active_application = self - -class DataManager(object): - def __init__(self, app=None): - self.app = get_active_application() - def test(self): - p = self.app - print (p) - ''' - astroid = builder.string_build(code, __name__, __file__) - infered = list(Instance(astroid['DataManager']).igetattr('app')) - self.assertEqual(len(infered), 2, infered) # None / Instance(Application) - infered = list(get_name_node(astroid['DataManager']['test'], 'p').infer()) - self.assertEqual(len(infered), 2, infered) - for node in infered: - if isinstance(node, Instance) and node.name == 'Application': - break - else: - self.fail('expected to find an instance of Application in %s' % infered) - - def test_list_inference(self): - """#20464""" - code = ''' -import optparse - -A = [] -B = [] - -def test(): - xyz = [ - "foobar=%s" % options.ca, - ] + A + B - - if options.bind is not None: - xyz.append("bind=%s" % options.bind) - return xyz - -def main(): - global options - - parser = optparse.OptionParser() - (options, args) = parser.parse_args() - -Z = test() - ''' - astroid = builder.string_build(code, __name__, __file__) - infered = list(astroid['Z'].infer()) - self.assertEqual(len(infered), 1, infered) - self.assertIsInstance(infered[0], Instance) - 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() - ''' - astroid = builder.string_build(code, __name__, __file__) - self.assertRaises(InferenceError, list, astroid['NewTest'].igetattr('arg')) - n = next(astroid['n'].infer()) - infered = list(n.igetattr('arg')) - self.assertEqual(len(infered), 1, infered) - - - def test_two_parents_from_same_module(self): - module = 'data_py3' if sys.version[0] == 3 else 'data' - code = ''' -from %s import nonregr -class Xxx(nonregr.Aaa, nonregr.Ccc): - "doc" - ''' % (module,) - astroid = builder.string_build(code, __name__, __file__) - parents = list(astroid['Xxx'].ancestors()) - self.assertEqual(len(parents), 3, parents) # Aaa, Ccc, object - - def test_pluggable_inference(self): - code = ''' -from collections import namedtuple -A = namedtuple('A', ['a', 'b']) -B = namedtuple('B', 'a b') - ''' - astroid = builder.string_build(code, __name__, __file__) - aclass = astroid['A'].infered()[0] - self.assertIsInstance(aclass, nodes.Class) - self.assertIn('a', aclass.instance_attrs) - self.assertIn('b', aclass.instance_attrs) - bclass = astroid['B'].infered()[0] - self.assertIsInstance(bclass, nodes.Class) - self.assertIn('a', bclass.instance_attrs) - self.assertIn('b', bclass.instance_attrs) - - def test_infer_arguments(self): - code = ''' -class A(object): - def first(self, arg1, arg2): - return arg1 - @classmethod - def method(cls, arg1, arg2): - return arg2 - @classmethod - def empty(cls): - return 2 - @staticmethod - def static(arg1, arg2): - return arg1 - def empty_method(self): - return [] -x = A().first(1, []) -y = A.method(1, []) -z = A.static(1, []) -empty = A.empty() -empty_list = A().empty_method() - ''' - astroid = builder.string_build(code, __name__, __file__) - int_node = astroid['x'].infered()[0] - self.assertIsInstance(int_node, nodes.Const) - self.assertEqual(int_node.value, 1) - list_node = astroid['y'].infered()[0] - self.assertIsInstance(list_node, nodes.List) - int_node = astroid['z'].infered()[0] - self.assertIsInstance(int_node, nodes.Const) - self.assertEqual(int_node.value, 1) - empty = astroid['empty'].infered()[0] - self.assertIsInstance(empty, nodes.Const) - self.assertEqual(empty.value, 2) - empty_list = astroid['empty_list'].infered()[0] - self.assertIsInstance(empty_list, nodes.List) - - def test_infer_variable_arguments(self): - code = ''' -def test(*args, **kwargs): - vararg = args - kwarg = kwargs - ''' - astroid = builder.string_build(code, __name__, __file__) - func = astroid['test'] - vararg = func.body[0].value - kwarg = func.body[1].value - - kwarg_infered = kwarg.infered()[0] - self.assertIsInstance(kwarg_infered, nodes.Dict) - self.assertIs(kwarg_infered.parent, func.args) - - vararg_infered = vararg.infered()[0] - self.assertIsInstance(vararg_infered, nodes.Tuple) - self.assertIs(vararg_infered.parent, func.args) - - def test_infer_nested(self): - code = dedent(""" - def nested(): - from threading import Thread - - class NestedThread(Thread): - def __init__(self): - Thread.__init__(self) - """) - # Test that inferring Thread.__init__ looks up in - # the nested scope. - astroid = builder.string_build(code, __name__, __file__) - callfunc = next(astroid.nodes_of_class(nodes.CallFunc)) - func = callfunc.func - infered = func.infered()[0] - self.assertIsInstance(infered, UnboundMethod) - - def test_instance_binary_operations(self): - code = dedent(""" - class A(object): - def __mul__(self, other): - return 42 - a = A() - b = A() - sub = a - b - mul = a * b - """) - astroid = builder.string_build(code, __name__, __file__) - sub = astroid['sub'].infered()[0] - mul = astroid['mul'].infered()[0] - self.assertIs(sub, YES) - self.assertIsInstance(mul, nodes.Const) - self.assertEqual(mul.value, 42) - - def test_instance_binary_operations_parent(self): - code = dedent(""" - class A(object): - def __mul__(self, other): - return 42 - class B(A): - pass - a = B() - b = B() - sub = a - b - mul = a * b - """) - astroid = builder.string_build(code, __name__, __file__) - sub = astroid['sub'].infered()[0] - mul = astroid['mul'].infered()[0] - self.assertIs(sub, YES) - self.assertIsInstance(mul, nodes.Const) - self.assertEqual(mul.value, 42) - - def test_instance_binary_operations_multiple_methods(self): - code = dedent(""" - class A(object): - def __mul__(self, other): - return 42 - class B(A): - def __mul__(self, other): - return [42] - a = B() - b = B() - sub = a - b - mul = a * b - """) - astroid = builder.string_build(code, __name__, __file__) - sub = astroid['sub'].infered()[0] - mul = astroid['mul'].infered()[0] - self.assertIs(sub, YES) - self.assertIsInstance(mul, nodes.List) - self.assertIsInstance(mul.elts[0], nodes.Const) - self.assertEqual(mul.elts[0].value, 42) - - def test_infer_call_result_crash(self): - # Test for issue 11. - code = dedent(""" - class A(object): - def __mul__(self, other): - return type.__new__() - - a = A() - b = A() - c = a * b - """) - astroid = builder.string_build(code, __name__, __file__) - node = astroid['c'] - self.assertEqual(node.infered(), [YES]) - - def test_infer_empty_nodes(self): - # Should not crash when trying to infer EmptyNodes. - node = nodes.EmptyNode() - self.assertEqual(node.infered(), [YES]) - - def test_infinite_loop_for_decorators(self): - # Issue https://bitbucket.org/logilab/astroid/issue/50 - # A decorator that returns itself leads to an infinite loop. - code = dedent(""" - def decorator(): - def wrapper(): - return decorator() - return wrapper - - @decorator() - def do_a_thing(): - pass""") - astroid = builder.string_build(code, __name__, __file__) - node = astroid['do_a_thing'] - self.assertEqual(node.type, 'function') - - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_inspector.py b/test/unittest_inspector.py deleted file mode 100644 index 5d5eaa2..0000000 --- a/test/unittest_inspector.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). -# http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# 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. -""" - for the visitors.diadefs module -""" - -import sys -from os.path import join, abspath, dirname -import unittest - - -from astroid import nodes, inspector -from astroid.bases import Instance, YES - -from astroid.manager import AstroidManager, _silent_no_wrap - -MANAGER = AstroidManager() - -def astroid_wrapper(func, modname): - return func(modname) - - -DATA2 = join(dirname(abspath(__file__)), 'data2') - - -class LinkerTC(unittest.TestCase): - - def setUp(self): - self.project = MANAGER.project_from_files([DATA2], astroid_wrapper) - self.linker = inspector.Linker(self.project) - self.linker.visit(self.project) - - def test_class_implements(self): - klass = self.project.get_module('data2.clientmodule_test')['Ancestor'] - self.assertTrue(hasattr(klass, 'implements')) - self.assertEqual(len(klass.implements), 1) - self.assertTrue(isinstance(klass.implements[0], nodes.Class)) - self.assertEqual(klass.implements[0].name, "Interface") - klass = self.project.get_module('data2.clientmodule_test')['Specialization'] - self.assertTrue(hasattr(klass, 'implements')) - self.assertEqual(len(klass.implements), 0) - - def test_locals_assignment_resolution(self): - klass = self.project.get_module('data2.clientmodule_test')['Specialization'] - self.assertTrue(hasattr(klass, 'locals_type')) - type_dict = klass.locals_type - self.assertEqual(len(type_dict), 2) - keys = sorted(type_dict.keys()) - self.assertEqual(keys, ['TYPE', 'top']) - self.assertEqual(len(type_dict['TYPE']), 1) - self.assertEqual(type_dict['TYPE'][0].value, 'final class') - self.assertEqual(len(type_dict['top']), 1) - self.assertEqual(type_dict['top'][0].value, 'class') - - def test_instance_attrs_resolution(self): - klass = self.project.get_module('data2.clientmodule_test')['Specialization'] - self.assertTrue(hasattr(klass, 'instance_attrs_type')) - type_dict = klass.instance_attrs_type - self.assertEqual(len(type_dict), 3) - keys = sorted(type_dict.keys()) - self.assertEqual(keys, ['_id', 'relation', 'toto']) - self.assertTrue(isinstance(type_dict['relation'][0], Instance), type_dict['relation']) - self.assertEqual(type_dict['relation'][0].name, 'DoNothing') - self.assertTrue(isinstance(type_dict['toto'][0], Instance), type_dict['toto']) - self.assertEqual(type_dict['toto'][0].name, 'Toto') - self.assertIs(type_dict['_id'][0], YES) - - -class LinkerTC2(LinkerTC): - - def setUp(self): - self.project = MANAGER.project_from_files([DATA2], func_wrapper=_silent_no_wrap) - self.linker = inspector.Linker(self.project) - self.linker.visit(self.project) - -__all__ = ('LinkerTC', 'LinkerTC2') - - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_lookup.py b/test/unittest_lookup.py deleted file mode 100644 index 2ca818a..0000000 --- a/test/unittest_lookup.py +++ /dev/null @@ -1,369 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""tests for the astroid variable lookup capabilities -""" -import sys -from os.path import join, abspath, dirname -from functools import partial -import unittest - -from astroid import builder, nodes, scoped_nodes, \ - InferenceError, NotFoundError, UnresolvableName -from astroid.scoped_nodes import builtin_lookup, Function -from astroid.bases import YES -from unittest_inference import get_name_node -from astroid.test_utils import require_version - -builder = builder.AstroidBuilder() -PY3K = sys.version_info >= (3, 0) - -if PY3K: - DATA = join(dirname(abspath(__file__)), 'data_py3') - import data_py3 as data -else: - DATA = join(dirname(abspath(__file__)), 'data') - import data - -MODULE = builder.file_build(join(DATA, 'module.py'), 'data.module') -MODULE2 = builder.file_build(join(DATA, 'module2.py'), 'data.module2') -NONREGR = builder.file_build(join(DATA, 'nonregr.py'), 'data.nonregr') - -class LookupTC(unittest.TestCase): - - def test_limit(self): - code = ''' -l = [a - for a,b in list] - -a = 1 -b = a -a = None - -def func(): - c = 1 - ''' - astroid = builder.string_build(code, __name__, __file__) - # a & b - a = next(astroid.nodes_of_class(nodes.Name)) - self.assertEqual(a.lineno, 2) - if sys.version_info < (3, 0): - self.assertEqual(len(astroid.lookup('b')[1]), 2) - self.assertEqual(len(astroid.lookup('a')[1]), 3) - b = astroid.locals['b'][1] - else: - self.assertEqual(len(astroid.lookup('b')[1]), 1) - self.assertEqual(len(astroid.lookup('a')[1]), 2) - b = astroid.locals['b'][0] - stmts = a.lookup('a')[1] - self.assertEqual(len(stmts), 1) - self.assertEqual(b.lineno, 6) - b_infer = b.infer() - b_value = next(b_infer) - self.assertEqual(b_value.value, 1) - # c - self.assertRaises(StopIteration, partial(next, b_infer)) - func = astroid.locals['func'][0] - self.assertEqual(len(func.lookup('c')[1]), 1) - - def test_module(self): - astroid = builder.string_build('pass', __name__, __file__) - # built-in objects - none = next(astroid.ilookup('None')) - self.assertIsNone(none.value) - obj = next(astroid.ilookup('object')) - self.assertIsInstance(obj, nodes.Class) - self.assertEqual(obj.name, 'object') - self.assertRaises(InferenceError, partial(next, astroid.ilookup('YOAA'))) - - # XXX - self.assertEqual(len(list(NONREGR.ilookup('enumerate'))), 2) - - def test_class_ancestor_name(self): - code = ''' -class A: - pass - -class A(A): - pass - ''' - astroid = builder.string_build(code, __name__, __file__) - cls1 = astroid.locals['A'][0] - cls2 = astroid.locals['A'][1] - name = next(cls2.nodes_of_class(nodes.Name)) - self.assertEqual(next(name.infer()), cls1) - - ### backport those test to inline code - def test_method(self): - method = MODULE['YOUPI']['method'] - my_dict = next(method.ilookup('MY_DICT')) - self.assertTrue(isinstance(my_dict, nodes.Dict), my_dict) - none = next(method.ilookup('None')) - self.assertIsNone(none.value) - self.assertRaises(InferenceError, partial(next, method.ilookup('YOAA'))) - - - def test_function_argument_with_default(self): - make_class = MODULE2['make_class'] - base = next(make_class.ilookup('base')) - self.assertTrue(isinstance(base, nodes.Class), base.__class__) - self.assertEqual(base.name, 'YO') - self.assertEqual(base.root().name, '%s.module' % (data.__name__,)) - - - def test_class(self): - klass = MODULE['YOUPI'] - my_dict = next(klass.ilookup('MY_DICT')) - self.assertIsInstance(my_dict, nodes.Dict) - none = next(klass.ilookup('None')) - self.assertIsNone(none.value) - obj = next(klass.ilookup('object')) - self.assertIsInstance(obj, nodes.Class) - self.assertEqual(obj.name, 'object') - self.assertRaises(InferenceError, partial(next, klass.ilookup('YOAA'))) - - - def test_inner_classes(self): - ddd = list(NONREGR['Ccc'].ilookup('Ddd')) - self.assertEqual(ddd[0].name, 'Ddd') - - - def test_loopvar_hiding(self): - astroid = builder.string_build(""" -x = 10 -for x in range(5): - print (x) - -if x > 0: - print ('#' * x) - """, __name__, __file__) - xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'x'] - # inside the loop, only one possible assignment - self.assertEqual(len(xnames[0].lookup('x')[1]), 1) - # outside the loop, two possible assignments - self.assertEqual(len(xnames[1].lookup('x')[1]), 2) - self.assertEqual(len(xnames[2].lookup('x')[1]), 2) - - def test_list_comps(self): - astroid = builder.string_build(""" -print ([ i for i in range(10) ]) -print ([ i for i in range(10) ]) -print ( list( i for i in range(10) ) ) - """, __name__, __file__) - xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'i'] - self.assertEqual(len(xnames[0].lookup('i')[1]), 1) - self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) - self.assertEqual(len(xnames[1].lookup('i')[1]), 1) - self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) - self.assertEqual(len(xnames[2].lookup('i')[1]), 1) - self.assertEqual(xnames[2].lookup('i')[1][0].lineno, 4) - - def test_list_comp_target(self): - """test the list comprehension target""" - astroid = builder.string_build(""" -ten = [ var for var in range(10) ] -var - """) - var = astroid.body[1].value - if sys.version_info < (3, 0): - self.assertEqual(var.infered(), [YES]) - else: - self.assertRaises(UnresolvableName, var.infered) - - @require_version('2.7') - def test_dict_comps(self): - astroid = builder.string_build(""" -print ({ i: j for i in range(10) for j in range(10) }) -print ({ i: j for i in range(10) for j in range(10) }) - """, __name__, __file__) - xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'i'] - self.assertEqual(len(xnames[0].lookup('i')[1]), 1) - self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) - self.assertEqual(len(xnames[1].lookup('i')[1]), 1) - self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) - - xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'j'] - self.assertEqual(len(xnames[0].lookup('i')[1]), 1) - self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) - self.assertEqual(len(xnames[1].lookup('i')[1]), 1) - self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) - - @require_version('2.7') - def test_set_comps(self): - astroid = builder.string_build(""" -print ({ i for i in range(10) }) -print ({ i for i in range(10) }) - """, __name__, __file__) - xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'i'] - self.assertEqual(len(xnames[0].lookup('i')[1]), 1) - self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2) - self.assertEqual(len(xnames[1].lookup('i')[1]), 1) - self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3) - - @require_version('2.7') - def test_set_comp_closure(self): - astroid = builder.string_build(""" -ten = { var for var in range(10) } -var - """) - var = astroid.body[1].value - self.assertRaises(UnresolvableName, var.infered) - - def test_generator_attributes(self): - tree = builder.string_build(""" -def count(): - "test" - yield 0 - -iterer = count() -num = iterer.next() - """) - next = tree.body[2].value.func # Getattr - gener = next.expr.infered()[0] # Generator - if sys.version_info < (3, 0): - self.assertIsInstance(gener.getattr('next')[0], Function) - else: - self.assertIsInstance(gener.getattr('__next__')[0], Function) - self.assertIsInstance(gener.getattr('send')[0], Function) - self.assertIsInstance(gener.getattr('throw')[0], Function) - self.assertIsInstance(gener.getattr('close')[0], Function) - - def test_explicit___name__(self): - code = ''' -class Pouet: - __name__ = "pouet" -p1 = Pouet() - -class PouetPouet(Pouet): pass -p2 = Pouet() - -class NoName: pass -p3 = NoName() -''' - astroid = builder.string_build(code, __name__, __file__) - p1 = next(astroid['p1'].infer()) - self.assertTrue(p1.getattr('__name__')) - p2 = next(astroid['p2'].infer()) - self.assertTrue(p2.getattr('__name__')) - self.assertTrue(astroid['NoName'].getattr('__name__')) - p3 = next(astroid['p3'].infer()) - self.assertRaises(NotFoundError, p3.getattr, '__name__') - - - def test_function_module_special(self): - astroid = builder.string_build(''' -def initialize(linter): - """initialize linter with checkers in this package """ - package_load(linter, __path__[0]) - ''', 'data.__init__', 'data/__init__.py') - path = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == '__path__'][0] - self.assertEqual(len(path.lookup('__path__')[1]), 1) - - - def test_builtin_lookup(self): - self.assertEqual(builtin_lookup('__dict__')[1], ()) - intstmts = builtin_lookup('int')[1] - self.assertEqual(len(intstmts), 1) - self.assertIsInstance(intstmts[0], nodes.Class) - self.assertEqual(intstmts[0].name, 'int') - self.assertIs(intstmts[0], nodes.const_factory(1)._proxied) - - - def test_decorator_arguments_lookup(self): - code = ''' -def decorator(value): - def wrapper(function): - return function - return wrapper - -class foo: - member = 10 - - @decorator(member) #This will cause pylint to complain - def test(self): - pass - ''' - astroid = builder.string_build(code, __name__, __file__) - member = get_name_node(astroid['foo'], 'member') - it = member.infer() - obj = next(it) - self.assertIsInstance(obj, nodes.Const) - self.assertEqual(obj.value, 10) - self.assertRaises(StopIteration, partial(next, it)) - - - def test_inner_decorator_member_lookup(self): - code = ''' -class FileA: - def decorator(bla): - return bla - - @decorator - def funcA(): - return 4 - ''' - astroid = builder.string_build(code, __name__, __file__) - decname = get_name_node(astroid['FileA'], 'decorator') - it = decname.infer() - obj = next(it) - self.assertIsInstance(obj, nodes.Function) - self.assertRaises(StopIteration, partial(next, it)) - - - def test_static_method_lookup(self): - code = ''' -class FileA: - @staticmethod - def funcA(): - return 4 - - -class Test: - FileA = [1,2,3] - - def __init__(self): - print (FileA.funcA()) - ''' - astroid = builder.string_build(code, __name__, __file__) - it = astroid['Test']['__init__'].ilookup('FileA') - obj = next(it) - self.assertIsInstance(obj, nodes.Class) - self.assertRaises(StopIteration, partial(next, it)) - - - def test_global_delete(self): - code = ''' -def run2(): - f = Frobble() - -class Frobble: - pass -Frobble.mumble = True - -del Frobble - -def run1(): - f = Frobble() -''' - astroid = builder.string_build(code, __name__, __file__) - stmts = astroid['run2'].lookup('Frobbel')[1] - self.assertEqual(len(stmts), 0) - stmts = astroid['run1'].lookup('Frobbel')[1] - self.assertEqual(len(stmts), 0) - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_manager.py b/test/unittest_manager.py deleted file mode 100644 index 4e0abcd..0000000 --- a/test/unittest_manager.py +++ /dev/null @@ -1,231 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -import unittest - -import sys -from os.path import join, abspath, dirname -from astroid.manager import AstroidManager, _silent_no_wrap -from astroid.bases import BUILTINS -from astroid.exceptions import AstroidBuildingException - -PY3K = sys.version_info >= (3, 0) - -if PY3K: - DATA = join(dirname(abspath(__file__)), 'data_py3') -else: - DATA = join(dirname(abspath(__file__)), 'data') - - -class AstroidManagerTC(unittest.TestCase): - def setUp(self): - self.manager = AstroidManager() - self.manager.clear_cache() # take care of borg - - def test_ast_from_file(self): - """check if the method return a good astroid object""" - import unittest - filepath = unittest.__file__ - astroid = self.manager.ast_from_file(filepath) - self.assertEqual(astroid.name, 'unittest') - self.assertIn('unittest', self.manager.astroid_cache) - - def test_ast_from_file_cache(self): - """check if the cache works""" - import unittest - filepath = unittest.__file__ - self.manager.ast_from_file(filepath) - astroid = self.manager.ast_from_file('unhandledName', 'unittest') - self.assertEqual(astroid.name, 'unittest') - self.assertIn('unittest', self.manager.astroid_cache) - - def test_ast_from_file_astro_builder(self): - """check if the source is at True, AstroidBuilder build a good astroid""" - import unittest - filepath = unittest.__file__ - astroid = self.manager.ast_from_file(filepath, None, True, True) - self.assertEqual(astroid.name, 'unittest') - self.assertIn('unittest', self.manager.astroid_cache) - - def test_ast_from_file_name_astro_builder_exception(self): - """check if an exception is thrown if we give a wrong filepath""" - self.assertRaises(AstroidBuildingException, self.manager.ast_from_file, 'unhandledName') - - def test_do_not_expose_main(self): - obj = self.manager.ast_from_module_name('__main__') - self.assertEqual(obj.name, '__main__') - self.assertEqual(obj.items(), []) - - def test_ast_from_module_name(self): - """check if the ast_from_module_name method return a good astroid""" - astroid = self.manager.ast_from_module_name('unittest') - self.assertEqual(astroid.name, 'unittest') - self.assertIn('unittest', self.manager.astroid_cache) - - def test_ast_from_module_name_not_python_source(self): - """check if the ast_from_module_name method return a good astroid with a no python source module""" - astroid = self.manager.ast_from_module_name('time') - self.assertEqual(astroid.name, 'time') - self.assertIn('time', self.manager.astroid_cache) - self.assertEqual(astroid.pure_python, False) - - def test_ast_from_module_name_astro_builder_exception(self): - """check if the method raise an exception if we give a wrong module""" - self.assertRaises(AstroidBuildingException, self.manager.ast_from_module_name, 'unhandledModule') - - def _test_ast_from_zip(self, archive): - origpath = sys.path[:] - sys.modules.pop('mypypa', None) - archive_path = join(DATA, archive) - sys.path.insert(0, archive_path) - try: - module = self.manager.ast_from_module_name('mypypa') - self.assertEqual(module.name, 'mypypa') - end = join(archive, 'mypypa') - self.assertTrue(module.file.endswith(end), - "%s doesn't endswith %s" % (module.file, end)) - finally: - # remove the module, else after importing egg, we don't get the zip - if 'mypypa' in self.manager.astroid_cache: - del self.manager.astroid_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_ast_from_module_name_egg(self): - self._test_ast_from_zip('MyPyPa-0.1.0-py2.5.egg') - - def test_ast_from_module_name_zip(self): - self._test_ast_from_zip('MyPyPa-0.1.0-py2.5.zip') - - def test_zip_import_data(self): - """check if zip_import_data works""" - filepath = join(DATA, 'MyPyPa-0.1.0-py2.5.zip/mypypa') - astroid = self.manager.zip_import_data(filepath) - self.assertEqual(astroid.name, 'mypypa') - - def test_zip_import_data_without_zipimport(self): - """check if zip_import_data return None without zipimport""" - self.assertEqual(self.manager.zip_import_data('path'), None) - - def test_file_from_module(self): - """check if the unittest filepath is equals to the result of the method""" - import unittest - if PY3K: - unittest_file = unittest.__file__ - else: - unittest_file = unittest.__file__[:-1] - self.assertEqual(unittest_file, - self.manager.file_from_module_name('unittest', None)) - - def test_file_from_module_name_astro_building_exception(self): - """check if the method launch a exception with a wrong module name""" - self.assertRaises(AstroidBuildingException, self.manager.file_from_module_name, 'unhandledModule', None) - - def test_ast_from_module(self): - import unittest - astroid = self.manager.ast_from_module(unittest) - self.assertEqual(astroid.pure_python, True) - import time - astroid = self.manager.ast_from_module(time) - self.assertEqual(astroid.pure_python, False) - - def test_ast_from_module_cache(self): - """check if the module is in the cache manager""" - import unittest - astroid = self.manager.ast_from_module(unittest) - self.assertEqual(astroid.name, 'unittest') - self.assertIn('unittest', self.manager.astroid_cache) - - def test_ast_from_class(self): - astroid = self.manager.ast_from_class(int) - self.assertEqual(astroid.name, 'int') - self.assertEqual(astroid.parent.frame().name, BUILTINS) - - astroid = self.manager.ast_from_class(object) - self.assertEqual(astroid.name, 'object') - self.assertEqual(astroid.parent.frame().name, BUILTINS) - self.assertIn('__setattr__', astroid) - - def test_ast_from_class_with_module(self): - """check if the method works with the module name""" - astroid = self.manager.ast_from_class(int, int.__module__) - self.assertEqual(astroid.name, 'int') - self.assertEqual(astroid.parent.frame().name, BUILTINS) - - astroid = self.manager.ast_from_class(object, object.__module__) - self.assertEqual(astroid.name, 'object') - self.assertEqual(astroid.parent.frame().name, BUILTINS) - self.assertIn('__setattr__', astroid) - - def test_ast_from_class_attr_error(self): - """give a wrong class at the ast_from_class method""" - self.assertRaises(AstroidBuildingException, self.manager.ast_from_class, None) - - def test_from_directory(self): - obj = self.manager.project_from_files([DATA], _silent_no_wrap, 'data') - self.assertEqual(obj.name, 'data') - 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 = [ - 'data', - 'data.SSL1', - 'data.SSL1.Connection1', - 'data.absimport', - 'data.all', - 'data.appl', - 'data.appl.myConnection', - 'data.email', - 'data.find_test', - 'data.find_test.module', - 'data.find_test.module2', - 'data.find_test.noendingnewline', - 'data.find_test.nonregr', - 'data.format', - 'data.lmfp', - 'data.lmfp.foo', - 'data.module', - 'data.module1abs', - 'data.module1abs.core', - 'data.module2', - 'data.noendingnewline', - 'data.nonregr', - 'data.notall'] - if PY3K: - expected = [e.replace('data', 'data_py3') for e in expected] - self.assertListEqual(sorted(k for k in obj.keys()), expected) - -class BorgAstroidManagerTC(unittest.TestCase): - - def test_borg(self): - """test that the AstroidManager is really a borg, i.e. that two different - instances has same cache""" - first_manager = AstroidManager() - built = first_manager.ast_from_module_name(BUILTINS) - - second_manager = AstroidManager() - second_built = first_manager.ast_from_module_name(BUILTINS) - self.assertIs(built, second_built) - - -if __name__ == '__main__': - unittest.main() - - diff --git a/test/unittest_modutils.py b/test/unittest_modutils.py deleted file mode 100644 index 98955c5..0000000 --- a/test/unittest_modutils.py +++ /dev/null @@ -1,280 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -""" -unit tests for module modutils (module manipulation utilities) -""" - -import sys -import unittest - -try: - __file__ -except NameError: - __file__ = sys.argv[0] - -from os import path, getcwd, sep -from astroid import modutils - -sys.path.insert(0, path.dirname(__file__)) -DATADIR = path.abspath(path.normpath(path.join(path.dirname(__file__), 'data'))) - - -class ModuleFileTC(unittest.TestCase): - package = "mypypa" - - def tearDown(self): - super(ModuleFileTC, self).tearDown() - for k in list(sys.path_importer_cache.keys()): - if 'MyPyPa' in k: - del sys.path_importer_cache[k] - - def test_find_zipped_module(self): - mtype, mfile = modutils._module_file([self.package], [path.join(DATADIR, 'MyPyPa-0.1.0-py2.5.zip')]) - self.assertEqual(mtype, modutils.ZIPFILE) - self.assertEqual(mfile.split(sep)[-4:], ["test", "data", "MyPyPa-0.1.0-py2.5.zip", self.package]) - - def test_find_egg_module(self): - mtype, mfile = modutils._module_file([self.package], [path.join(DATADIR, 'MyPyPa-0.1.0-py2.5.egg')]) - self.assertEqual(mtype, modutils.ZIPFILE) - self.assertEqual(mfile.split(sep)[-4:], ["test", "data", "MyPyPa-0.1.0-py2.5.egg", self.package]) - - -class load_module_from_name_tc(unittest.TestCase): - """ load a python module from it's name """ - - def test_knownValues_load_module_from_name_1(self): - self.assertEqual(modutils.load_module_from_name('sys'), sys) - - def test_knownValues_load_module_from_name_2(self): - self.assertEqual(modutils.load_module_from_name('os.path'), path) - - def test_raise_load_module_from_name_1(self): - self.assertRaises(ImportError, - modutils.load_module_from_name, 'os.path', use_sys=0) - - -class get_module_part_tc(unittest.TestCase): - """given a dotted name return the module part of the name""" - - def test_knownValues_get_module_part_1(self): - self.assertEqual(modutils.get_module_part('astroid.modutils'), - 'astroid.modutils') - - def test_knownValues_get_module_part_2(self): - self.assertEqual(modutils.get_module_part('astroid.modutils.get_module_part'), - 'astroid.modutils') - - def test_knownValues_get_module_part_3(self): - """relative import from given file""" - self.assertEqual(modutils.get_module_part('node_classes.AssName', - modutils.__file__), 'node_classes') - - def test_knownValues_get_compiled_module_part(self): - self.assertEqual(modutils.get_module_part('math.log10'), 'math') - self.assertEqual(modutils.get_module_part('math.log10', __file__), 'math') - - def test_knownValues_get_builtin_module_part(self): - self.assertEqual(modutils.get_module_part('sys.path'), 'sys') - self.assertEqual(modutils.get_module_part('sys.path', '__file__'), 'sys') - - def test_get_module_part_exception(self): - self.assertRaises(ImportError, modutils.get_module_part, 'unknown.module', - modutils.__file__) - - -class modpath_from_file_tc(unittest.TestCase): - """ given an absolute file path return the python module's path as a list """ - - def test_knownValues_modpath_from_file_1(self): - self.assertEqual(modutils.modpath_from_file(modutils.__file__), - ['astroid', 'modutils']) - - def test_knownValues_modpath_from_file_2(self): - self.assertEqual(modutils.modpath_from_file('unittest_modutils.py', - {getcwd(): 'arbitrary.pkg'}), - ['arbitrary', 'pkg', 'unittest_modutils']) - - def test_raise_modpath_from_file_Exception(self): - self.assertRaises(Exception, modutils.modpath_from_file, '/turlututu') - - -class load_module_from_path_tc(unittest.TestCase): - - def test_do_not_load_twice(self): - sys.path.insert(0, DATADIR) - foo = modutils.load_module_from_modpath(['lmfp', 'foo']) - lmfp = modutils.load_module_from_modpath(['lmfp']) - self.assertEqual(len(sys.just_once), 1) - sys.path.pop(0) - del sys.just_once - - -class file_from_modpath_tc(unittest.TestCase): - """given a mod path (i.e. splited module / package name), return the - corresponding file, giving priority to source file over precompiled file - if it exists""" - - def test_site_packages(self): - self.assertEqual(path.realpath(modutils.file_from_modpath(['astroid', 'modutils'])), - path.realpath(modutils.__file__.replace('.pyc', '.py'))) - - def test_std_lib(self): - from os import path - self.assertEqual(path.realpath(modutils.file_from_modpath(['os', 'path']).replace('.pyc', '.py')), - path.realpath(path.__file__.replace('.pyc', '.py'))) - - def test_xmlplus(self): - try: - # don't fail if pyxml isn't installed - from xml.dom import ext - except ImportError: - pass - else: - self.assertEqual(path.realpath(modutils.file_from_modpath(['xml', 'dom', 'ext']).replace('.pyc', '.py')), - path.realpath(ext.__file__.replace('.pyc', '.py'))) - - def test_builtin(self): - self.assertEqual(modutils.file_from_modpath(['sys']), - None) - - - def test_unexisting(self): - self.assertRaises(ImportError, modutils.file_from_modpath, ['turlututu']) - - def test_unicode_in_package_init(self): - # file_from_modpath should not crash when reading an __init__ - # file with unicode characters. - sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), - 'regrtest_data')) - self.addCleanup(sys.path.pop, 0) - - modutils.file_from_modpath(["unicode_package", "core"]) - - -class get_source_file_tc(unittest.TestCase): - - def test(self): - from os import path - self.assertEqual(modutils.get_source_file(path.__file__), - path.normpath(path.__file__.replace('.pyc', '.py'))) - - def test_raise(self): - self.assertRaises(modutils.NoSourceFile, modutils.get_source_file, 'whatever') - - -class is_standard_module_tc(unittest.TestCase): - """ - return true if the module may be considered as a module from the standard - library - """ - - def test_builtins(self): - if sys.version_info < (3, 0): - self.assertEqual(modutils.is_standard_module('__builtin__'), True) - self.assertEqual(modutils.is_standard_module('builtins'), False) - else: - self.assertEqual(modutils.is_standard_module('__builtin__'), False) - self.assertEqual(modutils.is_standard_module('builtins'), True) - - def test_builtin(self): - self.assertEqual(modutils.is_standard_module('sys'), True) - - def test_nonstandard(self): - self.assertEqual(modutils.is_standard_module('logilab'), False) - - def test_unknown(self): - self.assertEqual(modutils.is_standard_module('unknown'), False) - - def test_builtin(self): - self.assertEqual(modutils.is_standard_module('marshal'), True) - - def test_4(self): - import astroid - if sys.version_info > (3, 0): - skip = sys.platform.startswith('win') or '.tox' in astroid.__file__ - if skip: - self.skipTest('imp module has a broken behaviour in Python 3 on ' - 'Windows, returning the module path with different ' - 'case than it should be.') - self.assertEqual(modutils.is_standard_module('hashlib'), True) - self.assertEqual(modutils.is_standard_module('pickle'), True) - self.assertEqual(modutils.is_standard_module('email'), True) - self.assertEqual(modutils.is_standard_module('io'), sys.version_info >= (2, 6)) - self.assertEqual(modutils.is_standard_module('StringIO'), sys.version_info < (3, 0)) - - def test_custom_path(self): - if DATADIR.startswith(modutils.EXT_LIB_DIR): - self.skipTest('known breakage of is_standard_module on installed package') - self.assertEqual(modutils.is_standard_module('data.module', (DATADIR,)), True) - self.assertEqual(modutils.is_standard_module('data.module', (path.abspath(DATADIR),)), True) - - def test_failing_edge_cases(self): - from logilab import common - # using a subpackage/submodule path as std_path argument - self.assertEqual(modutils.is_standard_module('logilab.common', common.__path__), False) - # using a module + object name as modname argument - self.assertEqual(modutils.is_standard_module('sys.path'), True) - # this is because only the first package/module is considered - self.assertEqual(modutils.is_standard_module('sys.whatever'), True) - self.assertEqual(modutils.is_standard_module('logilab.whatever', common.__path__), False) - - -class is_relative_tc(unittest.TestCase): - - - def test_knownValues_is_relative_1(self): - import astroid - self.assertEqual(modutils.is_relative('modutils', astroid.__path__[0]), - True) - - def test_knownValues_is_relative_2(self): - from logilab.common import tree - self.assertEqual(modutils.is_relative('modutils', tree.__file__), - True) - - def test_knownValues_is_relative_3(self): - import astroid - self.assertEqual(modutils.is_relative('astroid', astroid.__path__[0]), - False) - - -class get_module_files_tc(unittest.TestCase): - - def test_knownValues_get_module_files_1(self): # XXXFIXME: TOWRITE - """given a directory return a list of all available python module's files, even - in subdirectories - """ - import data - modules = sorted(modutils.get_module_files(path.join(DATADIR, 'find_test'), - data.__path__[0])) - self.assertEqual(modules, - [path.join(DATADIR, 'find_test', x) for x in ['__init__.py', 'module.py', 'module2.py', 'noendingnewline.py', 'nonregr.py']]) - - def test_load_module_set_attribute(self): - import logilab.common.fileutils - import logilab - del logilab.common.fileutils - del sys.modules['logilab.common.fileutils'] - m = modutils.load_module_from_modpath(['logilab', 'common', 'fileutils']) - self.assertTrue( hasattr(logilab, 'common') ) - self.assertTrue( hasattr(logilab.common, 'fileutils') ) - self.assertTrue( m is logilab.common.fileutils ) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_nodes.py b/test/unittest_nodes.py deleted file mode 100644 index e9c688c..0000000 --- a/test/unittest_nodes.py +++ /dev/null @@ -1,441 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""tests for specific behaviour of astroid nodes -""" -from os.path import join, abspath, dirname -import sys -import unittest - -from astroid.node_classes import unpack_infer -from astroid.bases import BUILTINS, YES, InferenceContext -from astroid.exceptions import AstroidBuildingException, NotFoundError -from astroid import builder, nodes -from astroid.test_utils import require_version - -PY3K = sys.version_info >= (3, 0) - -if PY3K: - from data_py3 import module as test_module - DATA = join(dirname(abspath(__file__)), 'data_py3') -else: - from data import module as test_module - DATA = join(dirname(abspath(__file__)), 'data') - -abuilder = builder.AstroidBuilder() - -class AsString(unittest.TestCase): - - def test_tuple_as_string(self): - def build(string): - return abuilder.string_build(string).body[0].value - - self.assertEqual(build('1,').as_string(), '(1, )') - self.assertEqual(build('1, 2, 3').as_string(), '(1, 2, 3)') - self.assertEqual(build('(1, )').as_string(), '(1, )') - self.assertEqual(build('1, 2, 3').as_string(), '(1, 2, 3)') - - def test_varargs_kwargs_as_string(self): - ast = abuilder.string_build( 'raise_string(*args, **kwargs)').body[0] - self.assertEqual(ast.as_string(), '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(MODULE.as_string(), data) - - def test_module2_as_string(self): - """check as_string on a whole module prepared to be returned identically - """ - data = open(join(DATA, 'module2.py')).read() - self.assertMultiLineEqual(MODULE2.as_string(), data) - - @require_version('2.7') - def test_2_7_as_string(self): - """check as_string for python syntax >= 2.7""" - 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(ast.as_string(), code) - - @require_version('3.0') - def test_3k_as_string(self): - """check as_string for python 3k syntax""" - 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(ast.as_string(), code) - - -class _NodeTC(unittest.TestCase): - """test transformation of If Node""" - CODE = None - @property - def astroid(self): - try: - return self.__class__.__dict__['CODE_Astroid'] - except KeyError: - astroid = abuilder.string_build(self.CODE) - self.__class__.CODE_Astroid = astroid - return astroid - - -class IfNodeTC(_NodeTC): - """test transformation of If Node""" - CODE = """ -if 0: - print() - -if True: - print() -else: - pass - -if "": - print() -elif []: - raise - -if 1: - print() -elif True: - print() -elif func(): - pass -else: - raise - """ - - def test_if_elif_else_node(self): - """test transformation for If node""" - self.assertEqual(len(self.astroid.body), 4) - for stmt in self.astroid.body: - self.assertIsInstance( stmt, nodes.If) - self.assertFalse(self.astroid.body[0].orelse) # simple If - self.assertIsInstance(self.astroid.body[1].orelse[0], nodes.Pass) # If / else - self.assertIsInstance(self.astroid.body[2].orelse[0], nodes.If) # If / elif - self.assertIsInstance(self.astroid.body[3].orelse[0].orelse[0], nodes.If) - - def test_block_range(self): - # XXX ensure expected values - self.assertEqual(self.astroid.block_range(1), (0, 22)) - self.assertEqual(self.astroid.block_range(10), (0, 22)) # XXX (10, 22) ? - self.assertEqual(self.astroid.body[1].block_range(5), (5, 6)) - self.assertEqual(self.astroid.body[1].block_range(6), (6, 6)) - self.assertEqual(self.astroid.body[1].orelse[0].block_range(7), (7, 8)) - self.assertEqual(self.astroid.body[1].orelse[0].block_range(8), (8, 8)) - - -class TryExceptNodeTC(_NodeTC): - CODE = """ -try: - print ('pouet') -except IOError: - pass -except UnicodeError: - print() -else: - print() - """ - def test_block_range(self): - # XXX ensure expected values - self.assertEqual(self.astroid.body[0].block_range(1), (1, 8)) - self.assertEqual(self.astroid.body[0].block_range(2), (2, 2)) - self.assertEqual(self.astroid.body[0].block_range(3), (3, 8)) - self.assertEqual(self.astroid.body[0].block_range(4), (4, 4)) - self.assertEqual(self.astroid.body[0].block_range(5), (5, 5)) - self.assertEqual(self.astroid.body[0].block_range(6), (6, 6)) - self.assertEqual(self.astroid.body[0].block_range(7), (7, 7)) - self.assertEqual(self.astroid.body[0].block_range(8), (8, 8)) - - -class TryFinallyNodeTC(_NodeTC): - CODE = """ -try: - print ('pouet') -finally: - print ('pouet') - """ - def test_block_range(self): - # XXX ensure expected values - self.assertEqual(self.astroid.body[0].block_range(1), (1, 4)) - self.assertEqual(self.astroid.body[0].block_range(2), (2, 2)) - self.assertEqual(self.astroid.body[0].block_range(3), (3, 4)) - self.assertEqual(self.astroid.body[0].block_range(4), (4, 4)) - - -class TryFinally25NodeTC(_NodeTC): - CODE = """ -try: - print('pouet') -except Exception: - print ('oops') -finally: - print ('pouet') - """ - def test_block_range(self): - # XXX ensure expected values - self.assertEqual(self.astroid.body[0].block_range(1), (1, 6)) - self.assertEqual(self.astroid.body[0].block_range(2), (2, 2)) - self.assertEqual(self.astroid.body[0].block_range(3), (3, 4)) - self.assertEqual(self.astroid.body[0].block_range(4), (4, 4)) - self.assertEqual(self.astroid.body[0].block_range(5), (5, 5)) - self.assertEqual(self.astroid.body[0].block_range(6), (6, 6)) - - -class TryExcept2xNodeTC(_NodeTC): - CODE = """ -try: - hello -except AttributeError, (retval, desc): - pass - """ - def test_tuple_attribute(self): - if sys.version_info >= (3, 0): - self.skipTest('syntax removed from py3.x') - handler = self.astroid.body[0].handlers[0] - self.assertIsInstance(handler.name, nodes.Tuple) - - -MODULE = abuilder.module_build(test_module) -MODULE2 = abuilder.file_build(join(DATA, 'module2.py'), 'data.module2') - - -class ImportNodeTC(unittest.TestCase): - - def test_import_self_resolve(self): - myos = next(MODULE2.igetattr('myos')) - self.assertTrue(isinstance(myos, nodes.Module), myos) - self.assertEqual(myos.name, 'os') - self.assertEqual(myos.qname(), 'os') - self.assertEqual(myos.pytype(), '%s.module' % BUILTINS) - - def test_from_self_resolve(self): - pb = next(MODULE.igetattr('pb')) - self.assertTrue(isinstance(pb, nodes.Class), pb) - self.assertEqual(pb.root().name, 'logilab.common.shellutils') - self.assertEqual(pb.qname(), 'logilab.common.shellutils.ProgressBar') - if pb.newstyle: - self.assertEqual(pb.pytype(), '%s.type' % BUILTINS) - else: - self.assertEqual(pb.pytype(), '%s.classobj' % BUILTINS) - abspath = next(MODULE2.igetattr('abspath')) - self.assertTrue(isinstance(abspath, nodes.Function), abspath) - self.assertEqual(abspath.root().name, 'os.path') - self.assertEqual(abspath.qname(), 'os.path.abspath') - self.assertEqual(abspath.pytype(), '%s.function' % BUILTINS) - - def test_real_name(self): - from_ = MODULE['pb'] - self.assertEqual(from_.real_name('pb'), 'ProgressBar') - imp_ = MODULE['os'] - self.assertEqual(imp_.real_name('os'), 'os') - self.assertRaises(NotFoundError, imp_.real_name, 'os.path') - imp_ = MODULE['pb'] - self.assertEqual(imp_.real_name('pb'), 'ProgressBar') - self.assertRaises(NotFoundError, imp_.real_name, 'ProgressBar') - imp_ = MODULE2['YO'] - self.assertEqual(imp_.real_name('YO'), 'YO') - self.assertRaises(NotFoundError, imp_.real_name, 'data') - - def test_as_string(self): - ast = MODULE['modutils'] - self.assertEqual(ast.as_string(), "from astroid import modutils") - ast = MODULE['pb'] - self.assertEqual(ast.as_string(), "from logilab.common.shellutils import ProgressBar as pb") - ast = MODULE['os'] - self.assertEqual(ast.as_string(), "import os.path") - code = """from . import here -from .. import door -from .store import bread -from ..cave import wine\n\n""" - ast = abuilder.string_build(code) - self.assertMultiLineEqual(ast.as_string(), code) - - def test_bad_import_inference(self): - # Explication of bug - '''When we import PickleError from nonexistent, a call to the infer - method of this From node will be made by unpack_infer. - inference.infer_from will try to import this module, which will fail and - raise a InferenceException (by mixins.do_import_module). The infer_name - will catch this exception and yield and YES instead. - ''' - - code = '''try: - from pickle import PickleError -except ImportError: - from nonexistent import PickleError - -try: - pass -except PickleError: - pass - ''' - - astroid = abuilder.string_build(code) - from_node = astroid.body[1].handlers[0].body[0] - handler_type = astroid.body[1].handlers[0].type - - excs = list(unpack_infer(handler_type)) - - def test_absolute_import(self): - astroid = abuilder.file_build(join(DATA, 'absimport.py')) - ctx = InferenceContext() - # will fail if absolute import failed - next(astroid['message'].infer(ctx, lookupname='message')) - m = next(astroid['email'].infer(ctx, lookupname='email')) - self.assertFalse(m.file.startswith(join(DATA, 'email.py'))) - - def test_more_absolute_import(self): - sys.path.insert(0, join(DATA, 'moreabsimport')) - try: - astroid = abuilder.file_build(join(DATA, 'module1abs/__init__.py')) - self.assertIn('sys', astroid.locals) - finally: - sys.path.pop(0) - - -class CmpNodeTC(unittest.TestCase): - def test_as_string(self): - ast = abuilder.string_build("a == 2").body[0] - self.assertEqual(ast.as_string(), "a == 2") - - -class ConstNodeTC(unittest.TestCase): - - def _test(self, value): - node = nodes.const_factory(value) - self.assertIsInstance(node._proxied, nodes.Class) - self.assertEqual(node._proxied.name, value.__class__.__name__) - self.assertIs(node.value, value) - self.assertTrue(node._proxied.parent) - self.assertEqual(node._proxied.root().name, value.__class__.__module__) - - def test_none(self): - self._test(None) - - def test_bool(self): - self._test(True) - - def test_int(self): - self._test(1) - - def test_float(self): - self._test(1.0) - - def test_complex(self): - self._test(1.0j) - - def test_str(self): - self._test('a') - - def test_unicode(self): - self._test(u'a') - - -class NameNodeTC(unittest.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 AstroidBuildingException - 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(unittest.TestCase): - def test_linenumbering(self): - ast = abuilder.string_build(''' -def func(a, - b): pass -x = lambda x: None - ''') - self.assertEqual(ast['func'].args.fromlineno, 2) - self.assertFalse(ast['func'].args.is_statement) - xlambda = next(ast['x'].infer()) - self.assertEqual(xlambda.args.fromlineno, 4) - self.assertEqual(xlambda.args.tolineno, 4) - self.assertFalse(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(unittest.TestCase): - def test(self): - for code in ('a[0]', 'a[1:3]', 'a[:-1:step]', 'a[:,newaxis]', - 'a[newaxis,:]', 'del L[::2]', 'del A[1]', 'del Br[:]'): - ast = abuilder.string_build(code).body[0] - self.assertEqual(ast.as_string(), code) - - def test_slice_and_subscripts(self): - code = """a[:1] = bord[2:] -a[:1] = bord[2:] -del bree[3:d] -bord[2:] -del av[d::f], a[df:] -a[:1] = bord[2:] -del SRC[::1,newaxis,1:] -tous[vals] = 1010 -del thousand[key] -del a[::2], a[:-1:step] -del Fee.form[left:] -aout.vals = miles.of_stuff -del (ccok, (name.thing, foo.attrib.value)), Fee.form[left:] -if all[1] == bord[0:]: - pass\n\n""" - ast = abuilder.string_build(code) - self.assertEqual(ast.as_string(), code) - -class EllipsisNodeTC(unittest.TestCase): - def test(self): - ast = abuilder.string_build('a[...]').body[0] - self.assertEqual(ast.as_string(), 'a[...]') - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_python3.py b/test/unittest_python3.py deleted file mode 100644 index e150293..0000000 --- a/test/unittest_python3.py +++ /dev/null @@ -1,220 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -from textwrap import dedent -import unittest - -from astroid.node_classes import Assign, Discard, YieldFrom, Name, Const -from astroid.manager import AstroidManager -from astroid.builder import AstroidBuilder -from astroid.scoped_nodes import Class, Function -from astroid.test_utils import require_version - - -class Python3TC(unittest.TestCase): - def setUp(self): - self.manager = AstroidManager() - self.manager.clear_cache() # take care of borg - self.builder = AstroidBuilder(self.manager) - - @require_version('3.0') - def test_starred_notation(self): - astroid = self.builder.string_build("*a, b = [1, 2, 3]", 'test', 'test') - - # Get the star node - node = next(next(next(astroid.get_children()).get_children()).get_children()) - - self.assertTrue(isinstance(node.ass_type(), Assign)) - - @require_version('3.3') - def test_yield_from(self): - body = dedent(""" - def func(): - yield from iter([1, 2]) - """) - astroid = self.builder.string_build(body) - func = astroid.body[0] - self.assertIsInstance(func, Function) - yieldfrom_stmt = func.body[0] - - self.assertIsInstance(yieldfrom_stmt, Discard) - self.assertIsInstance(yieldfrom_stmt.value, YieldFrom) - self.assertEqual(yieldfrom_stmt.as_string(), - 'yield from iter([1, 2])') - - @require_version('3.3') - def test_yield_from_is_generator(self): - body = dedent(""" - def func(): - yield from iter([1, 2]) - """) - astroid = self.builder.string_build(body) - func = astroid.body[0] - self.assertIsInstance(func, Function) - self.assertTrue(func.is_generator()) - - @require_version('3.3') - def test_yield_from_as_string(self): - body = dedent(""" - def func(): - yield from iter([1, 2]) - value = yield from other() - """) - astroid = self.builder.string_build(body) - func = astroid.body[0] - self.assertEqual(func.as_string().strip(), body.strip()) - - # metaclass tests - - @require_version('3.0') - def test_simple_metaclass(self): - astroid = self.builder.string_build("class Test(metaclass=type): pass") - klass = astroid.body[0] - - metaclass = klass.metaclass() - self.assertIsInstance(metaclass, Class) - self.assertEqual(metaclass.name, 'type') - - @require_version('3.0') - def test_metaclass_error(self): - astroid = self.builder.string_build("class Test(metaclass=typ): pass") - klass = astroid.body[0] - self.assertFalse(klass.metaclass()) - - @require_version('3.0') - def test_metaclass_imported(self): - astroid = self.builder.string_build(dedent(""" - from abc import ABCMeta - class Test(metaclass=ABCMeta): pass""")) - klass = astroid.body[1] - - metaclass = klass.metaclass() - self.assertIsInstance(metaclass, Class) - self.assertEqual(metaclass.name, 'ABCMeta') - - @require_version('3.0') - def test_as_string(self): - body = dedent(""" - from abc import ABCMeta - class Test(metaclass=ABCMeta): pass""") - astroid = self.builder.string_build(body) - klass = astroid.body[1] - - self.assertEqual(klass.as_string(), - '\n\nclass Test(metaclass=ABCMeta):\n pass\n') - - @require_version('3.0') - def test_old_syntax_works(self): - astroid = self.builder.string_build(dedent(""" - class Test: - __metaclass__ = type - class SubTest(Test): pass - """)) - klass = astroid['SubTest'] - metaclass = klass.metaclass() - self.assertIsNone(metaclass) - - @require_version('3.0') - def test_metaclass_yes_leak(self): - astroid = self.builder.string_build(dedent(""" - # notice `ab` instead of `abc` - from ab import ABCMeta - - class Meta(metaclass=ABCMeta): pass - """)) - klass = astroid['Meta'] - self.assertIsNone(klass.metaclass()) - - @require_version('3.0') - def test_parent_metaclass(self): - astroid = self.builder.string_build(dedent(""" - from abc import ABCMeta - class Test(metaclass=ABCMeta): pass - class SubTest(Test): pass - """)) - klass = astroid['SubTest'] - self.assertTrue(klass.newstyle) - metaclass = klass.metaclass() - self.assertIsInstance(metaclass, Class) - self.assertEqual(metaclass.name, 'ABCMeta') - - @require_version('3.0') - def test_metaclass_ancestors(self): - astroid = self.builder.string_build(dedent(""" - from abc import ABCMeta - - class FirstMeta(metaclass=ABCMeta): pass - class SecondMeta(metaclass=type): - pass - - class Simple: - pass - - class FirstImpl(FirstMeta): pass - class SecondImpl(FirstImpl): pass - class ThirdImpl(Simple, SecondMeta): - pass - """)) - classes = { - 'ABCMeta': ('FirstImpl', 'SecondImpl'), - 'type': ('ThirdImpl', ) - } - for metaclass, names in classes.items(): - for name in names: - impl = astroid[name] - meta = impl.metaclass() - self.assertIsInstance(meta, Class) - self.assertEqual(meta.name, metaclass) - - @require_version('3.0') - def test_annotation_support(self): - astroid = self.builder.string_build(dedent(""" - def test(a: int, b: str, c: None, d, e, - *args: float, **kwargs: int)->int: - pass - """)) - func = astroid['test'] - self.assertIsInstance(func.args.varargannotation, Name) - self.assertEqual(func.args.varargannotation.name, 'float') - self.assertIsInstance(func.args.kwargannotation, Name) - self.assertEqual(func.args.kwargannotation.name, 'int') - self.assertIsInstance(func.returns, Name) - self.assertEqual(func.returns.name, 'int') - arguments = func.args - self.assertIsInstance(arguments.annotations[0], Name) - self.assertEqual(arguments.annotations[0].name, 'int') - self.assertIsInstance(arguments.annotations[1], Name) - self.assertEqual(arguments.annotations[1].name, 'str') - self.assertIsInstance(arguments.annotations[2], Const) - self.assertIsNone(arguments.annotations[2].value) - self.assertIsNone(arguments.annotations[3]) - self.assertIsNone(arguments.annotations[4]) - - astroid = self.builder.string_build(dedent(""" - def test(a: int=1, b: str=2): - pass - """)) - func = astroid['test'] - self.assertIsInstance(func.args.annotations[0], Name) - self.assertEqual(func.args.annotations[0].name, 'int') - self.assertIsInstance(func.args.annotations[1], Name) - self.assertEqual(func.args.annotations[1].name, 'str') - self.assertIsNone(func.returns) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_raw_building.py b/test/unittest_raw_building.py deleted file mode 100644 index 17057d5..0000000 --- a/test/unittest_raw_building.py +++ /dev/null @@ -1,48 +0,0 @@ -import unittest - -from astroid.raw_building import (attach_dummy_node, build_module, build_class, build_function, build_from_import) - -class RawBuildingTC(unittest.TestCase): - - def test_attach_dummy_node(self): - node = build_module('MyModule') - dummy = attach_dummy_node(node, 'DummyNode') - self.assertEqual(1, len(list(node.get_children()))) - - def test_build_module(self): - node = build_module('MyModule') - self.assertEqual(node.name, 'MyModule') - self.assertEqual(node.pure_python, False) - self.assertEqual(node.package, False) - self.assertEqual(node.parent, None) - - def test_build_class(self): - node = build_class('MyClass') - self.assertEqual(node.name, 'MyClass') - self.assertEqual(node.doc, None) - - def test_build_function(self): - node = build_function('MyFunction') - self.assertEqual(node.name, 'MyFunction') - self.assertEqual(node.doc, None) - - def test_build_function_args(self): - args = ['myArgs1', 'myArgs2'] - node = build_function('MyFunction', args) - self.assertEqual('myArgs1', node.args.args[0].name) - self.assertEqual('myArgs2', node.args.args[1].name) - self.assertEqual(2, len(node.args.args)) - - def test_build_function_defaults(self): - defaults = [ 'defaults1', 'defaults2' ] - node = build_function('MyFunction', None, defaults) - self.assertEqual(2, len(node.args.defaults)) - - def test_build_from_import(self): - names = ['exceptions, inference, inspector'] - node = build_from_import('astroid', names) - self.assertEqual(len(names), len(node.names)) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_regrtest.py b/test/unittest_regrtest.py deleted file mode 100644 index f40991f..0000000 --- a/test/unittest_regrtest.py +++ /dev/null @@ -1,224 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -from os.path import join, abspath, dirname -import sys -import unittest - -from astroid import ResolveError, MANAGER, Instance, nodes, YES, InferenceError -from astroid.builder import AstroidBuilder -from astroid.raw_building import build_module -from astroid.manager import AstroidManager -from astroid.test_utils import require_version - - -class NonRegressionTC(unittest.TestCase): - - def setUp(self): - sys.path.insert(0, join(dirname(abspath(__file__)), 'regrtest_data')) - - def tearDown(self): - # Since we may have created a brainless manager, leading - # to a new cache builtin module and proxy classes in the constants, - # clear out the global manager cache. - MANAGER.clear_cache() - sys.path.pop(0) - - def brainless_manager(self): - manager = AstroidManager() - # avoid caching into the AstroidManager borg since we get problems - # with other tests : - manager.__dict__ = {} - manager.astroid_cache = {} - manager._mod_file_cache = {} - manager.transforms = {} - manager.clear_cache() # trigger proper bootstraping - return manager - - def test_module_path(self): - man = self.brainless_manager() - mod = man.ast_from_module_name('package.import_package_subpackage_module') - package = next(mod.igetattr('package')) - self.assertEqual(package.name, 'package') - subpackage = next(package.igetattr('subpackage')) - self.assertIsInstance(subpackage, nodes.Module) - self.assertTrue(subpackage.package) - self.assertEqual(subpackage.name, 'package.subpackage') - module = next(subpackage.igetattr('module')) - self.assertEqual(module.name, 'package.subpackage.module') - - - def test_package_sidepackage(self): - manager = self.brainless_manager() - assert 'package.sidepackage' not in MANAGER.astroid_cache - package = manager.ast_from_module_name('absimp') - self.assertIsInstance(package, nodes.Module) - self.assertTrue(package.package) - subpackage = next(package.getattr('sidepackage')[0].infer()) - self.assertIsInstance(subpackage, nodes.Module) - self.assertTrue(subpackage.package) - self.assertEqual(subpackage.name, 'absimp.sidepackage') - - - def test_living_property(self): - builder = AstroidBuilder() - builder._done = {} - builder._module = sys.modules[__name__] - builder.object_build(build_module('module_name', ''), Whatever) - - - def test_new_style_class_detection(self): - try: - import pygtk - except ImportError: - self.skipTest('test skipped: pygtk is not available') - # XXX may fail on some pygtk version, because objects in - # gobject._gobject have __module__ set to gobject :( - builder = AstroidBuilder() - data = """ -import pygtk -pygtk.require("2.6") -import gobject - -class A(gobject.GObject): - pass -""" - astroid = builder.string_build(data, __name__, __file__) - a = astroid['A'] - self.assertTrue(a.newstyle) - - - def test_pylint_config_attr(self): - try: - from pylint import lint - except ImportError: - self.skipTest('pylint not available') - mod = MANAGER.ast_from_module_name('pylint.lint') - pylinter = mod['PyLinter'] - expect = ['OptionsManagerMixIn', 'object', 'MessagesHandlerMixIn', - 'ReportsHandlerMixIn', 'BaseTokenChecker', 'BaseChecker', - 'OptionsProviderMixIn'] - self.assertListEqual([c.name for c in pylinter.ancestors()], - expect) - self.assertTrue(list(Instance(pylinter).getattr('config'))) - infered = list(Instance(pylinter).igetattr('config')) - self.assertEqual(len(infered), 1) - self.assertEqual(infered[0].root().name, 'optparse') - self.assertEqual(infered[0].name, 'Values') - - def test_numpy_crash(self): - """test don't crash on numpy""" - #a crash occured somewhere in the past, and an - # InferenceError instead of a crash was better, but now we even infer! - try: - import numpy - except ImportError: - self.skipTest('test skipped: numpy is not available') - builder = AstroidBuilder() - data = """ -from numpy import multiply - -multiply(1, 2, 3) -""" - astroid = builder.string_build(data, __name__, __file__) - callfunc = astroid.body[1].value.func - infered = callfunc.infered() - self.assertEqual(len(infered), 1) - self.assertIsInstance(infered[0], Instance) - - @require_version('3.0') - def test_nameconstant(self): - # used to fail for Python 3.4 - builder = AstroidBuilder() - astroid = builder.string_build("def test(x=True): pass") - default = astroid.body[0].args.args[0] - self.assertEqual(default.name, 'x') - self.assertEqual(next(default.infer()).value, True) - - @require_version('2.7') - def test_with_infer_assnames(self): - builder = AstroidBuilder() - data = """ -with open('a.txt') as stream, open('b.txt'): - stream.read() -""" - astroid = builder.string_build(data, __name__, __file__) - # Used to crash due to the fact that the second - # context manager didn't use an assignment name. - list(astroid.nodes_of_class(nodes.CallFunc))[-1].infered() - - def test_recursion_regression_issue25(self): - builder = AstroidBuilder() - data = """ -import recursion as base - -_real_Base = base.Base - -class Derived(_real_Base): - pass - -def run(): - base.Base = Derived -""" - astroid = builder.string_build(data, __name__, __file__) - # Used to crash in _is_metaclass, due to wrong - # ancestors chain - classes = astroid.nodes_of_class(nodes.Class) - for klass in classes: - # triggers the _is_metaclass call - klass.type - - def test_decorator_callchain_issue42(self): - builder = AstroidBuilder() - data = """ - -def test(): - def factory(func): - def newfunc(): - func() - return newfunc - return factory - -@test() -def crash(): - pass -""" - astroid = builder.string_build(data, __name__, __file__) - self.assertEqual(astroid['crash'].type, 'function') - - def test_filter_stmts_scoping(self): - builder = AstroidBuilder() - data = """ -def test(): - compiler = int() - class B(compiler.__class__): - pass - compiler = B() - return compiler -""" - astroid = builder.string_build(data, __name__, __file__) - test = astroid['test'] - result = next(test.infer_call_result(astroid)) - self.assertIsInstance(result, Instance) - base = next(result._proxied.bases[0].infer()) - self.assertEqual(base.name, 'int') - -class Whatever(object): - a = property(lambda x: x, lambda x: x) - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_scoped_nodes.py b/test/unittest_scoped_nodes.py deleted file mode 100644 index 824e35f..0000000 --- a/test/unittest_scoped_nodes.py +++ /dev/null @@ -1,1008 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""tests for specific behaviour of astroid scoped nodes (i.e. module, class and -function) -""" -import sys -from os.path import join, abspath, dirname -from functools import partial -from textwrap import dedent -import unittest - -from astroid import YES, builder, nodes, scoped_nodes, \ - InferenceError, NotFoundError, NoDefault -from astroid.bases import BUILTINS, Instance, BoundMethod, UnboundMethod -from astroid.test_utils import extract_node, require_version - -abuilder = builder.AstroidBuilder() -PY3K = sys.version_info >= (3, 0) - -if PY3K: - DATA = join(dirname(abspath(__file__)), 'data_py3') - import data_py3 as data -else: - DATA = join(dirname(abspath(__file__)), 'data') - import data - -REGRTEST_DATA = join(dirname(abspath(__file__)), 'regrtest_data') -MODULE = abuilder.file_build(join(DATA, 'module.py'), 'data.module') -MODULE2 = abuilder.file_build(join(DATA, 'module2.py'), 'data.module2') -NONREGR = abuilder.file_build(join(DATA, 'nonregr.py'), 'data.nonregr') - -PACK = abuilder.file_build(join(DATA, '__init__.py'), 'data') - -def _test_dict_interface(self, node, test_attr): - self.assertIs(node[test_attr], node[test_attr]) - self.assertIn(test_attr, node) - node.keys() - node.values() - node.items() - iter(node) - - -class ModuleNodeTC(unittest.TestCase): - - def test_special_attributes(self): - self.assertEqual(len(MODULE.getattr('__name__')), 1) - self.assertIsInstance(MODULE.getattr('__name__')[0], nodes.Const) - self.assertEqual(MODULE.getattr('__name__')[0].value, 'data.module') - self.assertEqual(len(MODULE.getattr('__doc__')), 1) - self.assertIsInstance(MODULE.getattr('__doc__')[0], nodes.Const) - self.assertEqual(MODULE.getattr('__doc__')[0].value, 'test module for astroid\n') - self.assertEqual(len(MODULE.getattr('__file__')), 1) - self.assertIsInstance(MODULE.getattr('__file__')[0], nodes.Const) - self.assertEqual(MODULE.getattr('__file__')[0].value, join(DATA, 'module.py')) - self.assertEqual(len(MODULE.getattr('__dict__')), 1) - self.assertIsInstance(MODULE.getattr('__dict__')[0], nodes.Dict) - self.assertRaises(NotFoundError, MODULE.getattr, '__path__') - self.assertEqual(len(PACK.getattr('__path__')), 1) - self.assertIsInstance(PACK.getattr('__path__')[0], nodes.List) - - def test_dict_interface(self): - _test_dict_interface(self, MODULE, 'YO') - - def test_getattr(self): - yo = MODULE.getattr('YO')[0] - self.assertIsInstance(yo, nodes.Class) - self.assertEqual(yo.name, 'YO') - red = next(MODULE.igetattr('redirect')) - self.assertIsInstance(red, nodes.Function) - self.assertEqual(red.name, 'four_args') - pb = next(MODULE.igetattr('pb')) - self.assertIsInstance(pb, nodes.Class) - self.assertEqual(pb.name, 'ProgressBar') - # resolve packageredirection - sys.path.insert(1, DATA) - mod = abuilder.file_build(join(DATA, 'appl/myConnection.py'), - 'appl.myConnection') - try: - ssl = next(mod.igetattr('SSL1')) - cnx = next(ssl.igetattr('Connection')) - self.assertEqual(cnx.__class__, nodes.Class) - self.assertEqual(cnx.name, 'Connection') - self.assertEqual(cnx.root().name, 'SSL1.Connection1') - finally: - del sys.path[1] - self.assertEqual(len(NONREGR.getattr('enumerate')), 2) - # raise ResolveError - self.assertRaises(InferenceError, MODULE.igetattr, 'YOAA') - - def test_wildard_import_names(self): - m = abuilder.file_build(join(DATA, 'all.py'), 'all') - self.assertEqual(m.wildcard_import_names(), ['Aaa', '_bla', 'name']) - m = abuilder.file_build(join(DATA, 'notall.py'), 'notall') - res = sorted(m.wildcard_import_names()) - self.assertEqual(res, ['Aaa', 'func', 'name', 'other']) - - m = abuilder.string_build(dedent(''' - from missing import tzop - trop = "test" - __all__ = (trop, "test1", tzop, 42) - ''')) - res = sorted(m.wildcard_import_names()) - self.assertEqual(res, ["test", "test1"]) - - m = abuilder.string_build(dedent(''' - test = tzop = 42 - __all__ = ('test', ) + ('tzop', ) - ''')) - res = sorted(m.wildcard_import_names()) - self.assertEqual(res, ['test', 'tzop']) - - def test_module_getattr(self): - data = ''' -appli = application -appli += 2 -del appli - ''' - astroid = abuilder.string_build(data, __name__, __file__) - # test del statement not returned by getattr - self.assertEqual(len(astroid.getattr('appli')), 2, - astroid.getattr('appli')) - - def test_relative_to_absolute_name(self): - # package - mod = nodes.Module('very.multi.package', 'doc') - mod.package = True - modname = mod.relative_to_absolute_name('utils', 1) - self.assertEqual(modname, 'very.multi.package.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.package.utils') - modname = mod.relative_to_absolute_name('', 1) - self.assertEqual(modname, 'very.multi.package') - # non package - mod = nodes.Module('very.multi.module', 'doc') - 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') - modname = mod.relative_to_absolute_name('', 1) - self.assertEqual(modname, 'very.multi') - - def test_import_1(self): - data = '''from . import subpackage''' - astroid = abuilder.string_build(data, 'package', join(REGRTEST_DATA, 'package', '__init__.py')) - sys.path.insert(1, REGRTEST_DATA) - try: - m = astroid.import_module('', level=1) - self.assertEqual(m.name, 'package') - infered = list(astroid.igetattr('subpackage')) - self.assertEqual(len(infered), 1) - self.assertEqual(infered[0].name, 'package.subpackage') - finally: - del sys.path[1] - - - def test_import_2(self): - data = '''from . import subpackage as pouet''' - astroid = abuilder.string_build(data, 'package', join(dirname(abspath(__file__)), 'regrtest_data', 'package', '__init__.py')) - sys.path.insert(1, REGRTEST_DATA) - try: - m = astroid.import_module('', level=1) - self.assertEqual(m.name, 'package') - infered = list(astroid.igetattr('pouet')) - self.assertEqual(len(infered), 1) - self.assertEqual(infered[0].name, 'package.subpackage') - finally: - del sys.path[1] - - - def test_file_stream_in_memory(self): - data = '''irrelevant_variable is irrelevant''' - astroid = abuilder.string_build(data, 'in_memory') - self.assertEqual(astroid.file_stream.read().decode(), data) - - def test_file_stream_physical(self): - path = join(DATA, 'all.py') - astroid = abuilder.file_build(path, 'all') - with open(path, 'rb') as file_io: - self.assertEqual(astroid.file_stream.read(), file_io.read()) - - -class FunctionNodeTC(unittest.TestCase): - - def test_special_attributes(self): - func = MODULE2['make_class'] - self.assertEqual(len(func.getattr('__name__')), 1) - self.assertIsInstance(func.getattr('__name__')[0], nodes.Const) - self.assertEqual(func.getattr('__name__')[0].value, 'make_class') - self.assertEqual(len(func.getattr('__doc__')), 1) - self.assertIsInstance(func.getattr('__doc__')[0], nodes.Const) - self.assertEqual(func.getattr('__doc__')[0].value, 'check base is correctly resolved to Concrete0') - self.assertEqual(len(MODULE.getattr('__dict__')), 1) - self.assertIsInstance(MODULE.getattr('__dict__')[0], nodes.Dict) - - def test_dict_interface(self): - _test_dict_interface(self, MODULE['global_access'], 'local') - - def test_default_value(self): - func = MODULE2['make_class'] - self.assertIsInstance(func.args.default_value('base'), nodes.Getattr) - self.assertRaises(NoDefault, func.args.default_value, 'args') - self.assertRaises(NoDefault, func.args.default_value, 'kwargs') - self.assertRaises(NoDefault, func.args.default_value, 'any') - #self.assertIsInstance(func.mularg_class('args'), nodes.Tuple) - #self.assertIsInstance(func.mularg_class('kwargs'), nodes.Dict) - #self.assertIsNone(func.mularg_class('base')) - - def test_navigation(self): - function = MODULE['global_access'] - self.assertEqual(function.statement(), function) - l_sibling = function.previous_sibling() - # check taking parent if child is not a stmt - self.assertIsInstance(l_sibling, nodes.Assign) - child = function.args.args[0] - self.assertIs(l_sibling, child.previous_sibling()) - r_sibling = function.next_sibling() - self.assertIsInstance(r_sibling, nodes.Class) - self.assertEqual(r_sibling.name, 'YO') - self.assertIs(r_sibling, child.next_sibling()) - last = r_sibling.next_sibling().next_sibling().next_sibling() - self.assertIsInstance(last, nodes.Assign) - self.assertIsNone(last.next_sibling()) - first = l_sibling.previous_sibling().previous_sibling().previous_sibling().previous_sibling().previous_sibling() - self.assertIsNone(first.previous_sibling()) - - def test_nested_args(self): - if sys.version_info >= (3, 0): - self.skipTest("nested args has been removed in py3.x") - code = ''' -def nested_args(a, (b, c, d)): - "nested arguments test" - ''' - tree = abuilder.string_build(code) - func = tree['nested_args'] - self.assertEqual(sorted(func.locals), ['a', 'b', 'c', 'd']) - self.assertEqual(func.args.format_args(), 'a, (b, c, d)') - - def test_four_args(self): - func = MODULE['four_args'] - #self.assertEqual(func.args.args, ['a', ('b', 'c', 'd')]) - local = sorted(func.keys()) - self.assertEqual(local, ['a', 'b', 'c', 'd']) - self.assertEqual(func.type, 'function') - - def test_format_args(self): - func = MODULE2['make_class'] - self.assertEqual(func.args.format_args(), - 'any, base=%s.module.YO, *args, **kwargs' % (data.__name__,)) - func = MODULE['four_args'] - self.assertEqual(func.args.format_args(), 'a, b, c, d') - - def test_is_generator(self): - self.assertTrue(MODULE2['generator'].is_generator()) - self.assertFalse(MODULE2['not_a_generator'].is_generator()) - self.assertFalse(MODULE2['make_class'].is_generator()) - - def test_is_abstract(self): - method = MODULE2['AbstractClass']['to_override'] - self.assertTrue(method.is_abstract(pass_is_abstract=False)) - self.assertEqual(method.qname(), 'data.module2.AbstractClass.to_override') - self.assertEqual(method.pytype(), '%s.instancemethod' % BUILTINS) - method = MODULE2['AbstractClass']['return_something'] - self.assertFalse(method.is_abstract(pass_is_abstract=False)) - # non regression : test raise "string" doesn't cause an exception in is_abstract - func = MODULE2['raise_string'] - self.assertFalse(func.is_abstract(pass_is_abstract=False)) - - def test_is_abstract_decorated(self): - methods = extract_node(""" - import abc - - class Klass(object): - @abc.abstractproperty - def prop(self): #@ - pass - - @abc.abstractmethod - def method1(self): #@ - pass - - some_other_decorator = lambda x: x - @some_other_decorator - def method2(self): #@ - pass - - """) - self.assertTrue(methods[0].is_abstract(pass_is_abstract=False)) - self.assertTrue(methods[1].is_abstract(pass_is_abstract=False)) - self.assertFalse(methods[2].is_abstract(pass_is_abstract=False)) - -## def test_raises(self): -## method = MODULE2['AbstractClass']['to_override'] -## self.assertEqual([str(term) for term in method.raises()], -## ["CallFunc(Name('NotImplementedError'), [], None, None)"] ) - -## def test_returns(self): -## method = MODULE2['AbstractClass']['return_something'] -## # use string comp since Node doesn't handle __cmp__ -## self.assertEqual([str(term) for term in method.returns()], -## ["Const('toto')", "Const(None)"]) - - def test_lambda_pytype(self): - data = ''' -def f(): - g = lambda: None - ''' - astroid = abuilder.string_build(data, __name__, __file__) - g = list(astroid['f'].ilookup('g'))[0] - self.assertEqual(g.pytype(), '%s.function' % BUILTINS) - - def test_lambda_qname(self): - astroid = abuilder.string_build(''' -lmbd = lambda: None -''', __name__, __file__) - self.assertEqual('%s.' % __name__, astroid['lmbd'].parent.value.qname()) - - def test_is_method(self): - data = ''' -class A: - def meth1(self): - return 1 - @classmethod - def meth2(cls): - return 2 - @staticmethod - def meth3(): - return 3 - -def function(): - return 0 - -@staticmethod -def sfunction(): - return -1 - ''' - astroid = abuilder.string_build(data, __name__, __file__) - self.assertTrue(astroid['A']['meth1'].is_method()) - self.assertTrue(astroid['A']['meth2'].is_method()) - self.assertTrue(astroid['A']['meth3'].is_method()) - self.assertFalse(astroid['function'].is_method()) - self.assertFalse(astroid['sfunction'].is_method()) - - def test_argnames(self): - if sys.version_info < (3, 0): - code = 'def f(a, (b, c), *args, **kwargs): pass' - else: - code = 'def f(a, b, c, *args, **kwargs): pass' - astroid = abuilder.string_build(code, __name__, __file__) - self.assertEqual(astroid['f'].argnames(), ['a', 'b', 'c', 'args', 'kwargs']) - - def test_return_nothing(self): - """test infered value on a function with empty return""" - data = ''' -def func(): - return - -a = func() -''' - astroid = abuilder.string_build(data, __name__, __file__) - call = astroid.body[1].value - func_vals = call.infered() - self.assertEqual(len(func_vals), 1) - self.assertIsInstance(func_vals[0], nodes.Const) - self.assertIsNone(func_vals[0].value) - - def test_func_instance_attr(self): - """test instance attributes for functions""" - data= """ -def test(): - print(test.bar) - -test.bar = 1 -test() - """ - astroid = abuilder.string_build(data, 'mod', __file__) - func = astroid.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) - - def test_type_builtin_descriptor_subclasses(self): - astroid = abuilder.string_build(dedent(""" - class classonlymethod(classmethod): - pass - class staticonlymethod(staticmethod): - pass - - class Node: - @classonlymethod - def clsmethod_subclass(cls): - pass - @classmethod - def clsmethod(cls): - pass - @staticonlymethod - def staticmethod_subclass(cls): - pass - @staticmethod - def stcmethod(cls): - pass - """)) - node = astroid.locals['Node'][0] - self.assertEqual(node.locals['clsmethod_subclass'][0].type, - 'classmethod') - self.assertEqual(node.locals['clsmethod'][0].type, - 'classmethod') - self.assertEqual(node.locals['staticmethod_subclass'][0].type, - 'staticmethod') - self.assertEqual(node.locals['stcmethod'][0].type, - 'staticmethod') - - def test_decorator_builtin_descriptors(self): - astroid = abuilder.string_build(dedent(""" - def static_decorator(platform=None, order=50): - def wrapper(f): - f.cgm_module = True - f.cgm_module_order = order - f.cgm_module_platform = platform - return staticmethod(f) - return wrapper - - def classmethod_decorator(platform=None): - def wrapper(f): - f.platform = platform - return classmethod(f) - return wrapper - - class SomeClass(object): - @static_decorator() - def static(node, cfg): - pass - @classmethod_decorator() - def classmethod(cls): - pass - @static_decorator - def not_so_static(node): - pass - @classmethod_decorator - def not_so_classmethod(node): - pass - - """)) - node = astroid.locals['SomeClass'][0] - self.assertEqual(node.locals['static'][0].type, - 'staticmethod') - self.assertEqual(node.locals['classmethod'][0].type, - 'classmethod') - self.assertEqual(node.locals['not_so_static'][0].type, - 'method') - self.assertEqual(node.locals['not_so_classmethod'][0].type, - 'method') - - -class ClassNodeTC(unittest.TestCase): - - def test_dict_interface(self): - _test_dict_interface(self, MODULE['YOUPI'], 'method') - - def test_cls_special_attributes_1(self): - cls = MODULE['YO'] - self.assertEqual(len(cls.getattr('__bases__')), 1) - self.assertEqual(len(cls.getattr('__name__')), 1) - self.assertIsInstance(cls.getattr('__name__')[0], nodes.Const) - self.assertEqual(cls.getattr('__name__')[0].value, 'YO') - self.assertEqual(len(cls.getattr('__doc__')), 1) - self.assertIsInstance(cls.getattr('__doc__')[0], nodes.Const) - self.assertEqual(cls.getattr('__doc__')[0].value, 'hehe') - self.assertEqual(len(cls.getattr('__module__')), 1) - self.assertIsInstance(cls.getattr('__module__')[0], nodes.Const) - self.assertEqual(cls.getattr('__module__')[0].value, 'data.module') - self.assertEqual(len(cls.getattr('__dict__')), 1) - if not cls.newstyle: - self.assertRaises(NotFoundError, cls.getattr, '__mro__') - for cls in (nodes.List._proxied, nodes.Const(1)._proxied): - self.assertEqual(len(cls.getattr('__bases__')), 1) - self.assertEqual(len(cls.getattr('__name__')), 1) - self.assertEqual(len(cls.getattr('__doc__')), 1, (cls, cls.getattr('__doc__'))) - self.assertEqual(cls.getattr('__doc__')[0].value, cls.doc) - self.assertEqual(len(cls.getattr('__module__')), 1) - self.assertEqual(len(cls.getattr('__dict__')), 1) - self.assertEqual(len(cls.getattr('__mro__')), 1) - - def test_cls_special_attributes_2(self): - astroid = abuilder.string_build(''' -class A: pass -class B: pass - -A.__bases__ += (B,) -''', __name__, __file__) - self.assertEqual(len(astroid['A'].getattr('__bases__')), 2) - self.assertIsInstance(astroid['A'].getattr('__bases__')[0], nodes.Tuple) - self.assertIsInstance(astroid['A'].getattr('__bases__')[1], nodes.AssAttr) - - def test_instance_special_attributes(self): - for inst in (Instance(MODULE['YO']), nodes.List(), nodes.Const(1)): - self.assertRaises(NotFoundError, inst.getattr, '__mro__') - self.assertRaises(NotFoundError, inst.getattr, '__bases__') - self.assertRaises(NotFoundError, inst.getattr, '__name__') - self.assertEqual(len(inst.getattr('__dict__')), 1) - self.assertEqual(len(inst.getattr('__doc__')), 1) - - def test_navigation(self): - klass = MODULE['YO'] - self.assertEqual(klass.statement(), klass) - l_sibling = klass.previous_sibling() - self.assertTrue(isinstance(l_sibling, nodes.Function), l_sibling) - self.assertEqual(l_sibling.name, 'global_access') - r_sibling = klass.next_sibling() - self.assertIsInstance(r_sibling, nodes.Class) - self.assertEqual(r_sibling.name, 'YOUPI') - - def test_local_attr_ancestors(self): - klass2 = MODULE['YOUPI'] - it = klass2.local_attr_ancestors('__init__') - anc_klass = next(it) - self.assertIsInstance(anc_klass, nodes.Class) - self.assertEqual(anc_klass.name, 'YO') - self.assertRaises(StopIteration, partial(next, it)) - it = klass2.local_attr_ancestors('method') - self.assertRaises(StopIteration, partial(next, it)) - - def test_instance_attr_ancestors(self): - klass2 = MODULE['YOUPI'] - it = klass2.instance_attr_ancestors('yo') - anc_klass = next(it) - self.assertIsInstance(anc_klass, nodes.Class) - self.assertEqual(anc_klass.name, 'YO') - self.assertRaises(StopIteration, partial(next, it)) - klass2 = MODULE['YOUPI'] - it = klass2.instance_attr_ancestors('member') - self.assertRaises(StopIteration, partial(next, it)) - - def test_methods(self): - klass2 = MODULE['YOUPI'] - methods = sorted([m.name for m in klass2.methods()]) - self.assertEqual(methods, ['__init__', 'class_method', - 'method', 'static_method']) - methods = [m.name for m in klass2.mymethods()] - methods.sort() - self.assertEqual(methods, ['__init__', 'class_method', - 'method', 'static_method']) - klass2 = MODULE2['Specialization'] - methods = [m.name for m in klass2.mymethods()] - methods.sort() - self.assertEqual(methods, []) - method_locals = klass2.local_attr('method') - self.assertEqual(len(method_locals), 1) - self.assertEqual(method_locals[0].name, 'method') - self.assertRaises(NotFoundError, klass2.local_attr, 'nonexistant') - methods = [m.name for m in klass2.methods()] - methods.sort() - self.assertEqual(methods, ['__init__', 'class_method', - 'method', 'static_method']) - - #def test_rhs(self): - # my_dict = MODULE['MY_DICT'] - # self.assertIsInstance(my_dict.rhs(), nodes.Dict) - # a = MODULE['YO']['a'] - # value = a.rhs() - # self.assertIsInstance(value, nodes.Const) - # self.assertEqual(value.value, 1) - - def test_ancestors(self): - klass = MODULE['YOUPI'] - ancs = [a.name for a in klass.ancestors()] - self.assertEqual(ancs, ['YO']) - klass = MODULE2['Specialization'] - ancs = [a.name for a in klass.ancestors()] - self.assertEqual(ancs, ['YOUPI', 'YO']) - - def test_type(self): - klass = MODULE['YOUPI'] - self.assertEqual(klass.type, 'class') - klass = MODULE2['Metaclass'] - self.assertEqual(klass.type, 'metaclass') - klass = MODULE2['MyException'] - self.assertEqual(klass.type, 'exception') - klass = MODULE2['MyIFace'] - self.assertEqual(klass.type, 'interface') - klass = MODULE2['MyError'] - self.assertEqual(klass.type, 'exception') - # the following class used to be detected as a metaclass - # after the fix which used instance._proxied in .ancestors(), - # when in fact it is a normal class - klass = MODULE2['NotMetaclass'] - self.assertEqual(klass.type, 'class') - - def test_interfaces(self): - for klass, interfaces in (('Concrete0', ['MyIFace']), - ('Concrete1', ['MyIFace', 'AnotherIFace']), - ('Concrete2', ['MyIFace', 'AnotherIFace']), - ('Concrete23', ['MyIFace', 'AnotherIFace'])): - klass = MODULE2[klass] - self.assertEqual([i.name for i in klass.interfaces()], - interfaces) - - def test_concat_interfaces(self): - astroid = abuilder.string_build(''' -class IMachin: pass - -class Correct2: - """docstring""" - __implements__ = (IMachin,) - -class BadArgument: - """docstring""" - __implements__ = (IMachin,) - -class InterfaceCanNowBeFound: - """docstring""" - __implements__ = BadArgument.__implements__ + Correct2.__implements__ - - ''') - self.assertEqual([i.name for i in astroid['InterfaceCanNowBeFound'].interfaces()], - ['IMachin']) - - def test_inner_classes(self): - eee = NONREGR['Ccc']['Eee'] - self.assertEqual([n.name for n in eee.ancestors()], ['Ddd', 'Aaa', 'object']) - - - def test_classmethod_attributes(self): - data = ''' -class WebAppObject(object): - def registered(cls, application): - cls.appli = application - cls.schema = application.schema - cls.config = application.config - return cls - registered = classmethod(registered) - ''' - astroid = abuilder.string_build(data, __name__, __file__) - cls = astroid['WebAppObject'] - self.assertEqual(sorted(cls.locals.keys()), - ['appli', 'config', 'registered', 'schema']) - - - def test_class_getattr(self): - data = ''' -class WebAppObject(object): - appli = application - appli += 2 - del self.appli - ''' - astroid = abuilder.string_build(data, __name__, __file__) - cls = astroid['WebAppObject'] - # test del statement not returned by getattr - self.assertEqual(len(cls.getattr('appli')), 2) - - - def test_instance_getattr(self): - data = ''' -class WebAppObject(object): - def __init__(self, application): - self.appli = application - self.appli += 2 - del self.appli - ''' - astroid = abuilder.string_build(data, __name__, __file__) - inst = Instance(astroid['WebAppObject']) - # test del statement not returned by getattr - self.assertEqual(len(inst.getattr('appli')), 2) - - - def test_instance_getattr_with_class_attr(self): - data = ''' -class Parent: - aa = 1 - cc = 1 - -class Klass(Parent): - aa = 0 - bb = 0 - - def incr(self, val): - self.cc = self.aa - if val > self.aa: - val = self.aa - if val < self.bb: - val = self.bb - self.aa += val - ''' - astroid = abuilder.string_build(data, __name__, __file__) - inst = Instance(astroid['Klass']) - self.assertEqual(len(inst.getattr('aa')), 3, inst.getattr('aa')) - self.assertEqual(len(inst.getattr('bb')), 1, inst.getattr('bb')) - self.assertEqual(len(inst.getattr('cc')), 2, inst.getattr('cc')) - - - def test_getattr_method_transform(self): - data = ''' -class Clazz(object): - - def m1(self, value): - self.value = value - m2 = m1 - -def func(arg1, arg2): - "function that will be used as a method" - return arg1.value + arg2 - -Clazz.m3 = func -inst = Clazz() -inst.m4 = func - ''' - astroid = abuilder.string_build(data, __name__, __file__) - cls = astroid['Clazz'] - # test del statement not returned by getattr - for method in ('m1', 'm2', 'm3'): - inferred = list(cls.igetattr(method)) - self.assertEqual(len(inferred), 1) - self.assertIsInstance(inferred[0], UnboundMethod) - inferred = list(Instance(cls).igetattr(method)) - self.assertEqual(len(inferred), 1) - self.assertIsInstance(inferred[0], BoundMethod) - inferred = list(Instance(cls).igetattr('m4')) - self.assertEqual(len(inferred), 1) - self.assertIsInstance(inferred[0], nodes.Function) - - def test_getattr_from_grandpa(self): - data = ''' -class Future: - attr = 1 - -class Present(Future): - pass - -class Past(Present): - pass -''' - astroid = abuilder.string_build(data) - past = astroid['Past'] - attr = past.getattr('attr') - self.assertEqual(len(attr), 1) - attr1 = attr[0] - self.assertIsInstance(attr1, nodes.AssName) - self.assertEqual(attr1.name, 'attr') - - def test_function_with_decorator_lineno(self): - data = ''' -@f(a=2, - b=3) -def g1(x): - print(x) - -@f(a=2, - b=3) -def g2(): - pass -''' - astroid = abuilder.string_build(data) - self.assertEqual(astroid['g1'].fromlineno, 4) - self.assertEqual(astroid['g1'].tolineno, 5) - self.assertEqual(astroid['g2'].fromlineno, 9) - self.assertEqual(astroid['g2'].tolineno, 10) - - def test_simple_metaclass(self): - if PY3K: - self.skipTest('__metaclass__ syntax is python2-specific') - astroid = abuilder.string_build(dedent(""" - class Test(object): - __metaclass__ = type - """)) - klass = astroid['Test'] - - metaclass = klass.metaclass() - self.assertIsInstance(metaclass, scoped_nodes.Class) - self.assertEqual(metaclass.name, 'type') - - def test_metaclass_error(self): - astroid = abuilder.string_build(dedent(""" - class Test(object): - __metaclass__ = typ - """)) - klass = astroid['Test'] - self.assertFalse(klass.metaclass()) - - def test_metaclass_imported(self): - if PY3K: - self.skipTest('__metaclass__ syntax is python2-specific') - astroid = abuilder.string_build(dedent(""" - from abc import ABCMeta - class Test(object): - __metaclass__ = ABCMeta - """)) - klass = astroid['Test'] - - metaclass = klass.metaclass() - self.assertIsInstance(metaclass, scoped_nodes.Class) - self.assertEqual(metaclass.name, 'ABCMeta') - - def test_metaclass_yes_leak(self): - astroid = abuilder.string_build(dedent(""" - # notice `ab` instead of `abc` - from ab import ABCMeta - - class Meta(object): - __metaclass__ = ABCMeta - """)) - klass = astroid['Meta'] - self.assertIsNone(klass.metaclass()) - - def test_newstyle_and_metaclass_good(self): - if PY3K: - self.skipTest('__metaclass__ syntax is python2-specific') - astroid = abuilder.string_build(dedent(""" - from abc import ABCMeta - class Test: - __metaclass__ = ABCMeta - """)) - klass = astroid['Test'] - self.assertTrue(klass.newstyle) - self.assertEqual(klass.metaclass().name, 'ABCMeta') - astroid = abuilder.string_build(dedent(""" - from abc import ABCMeta - __metaclass__ = ABCMeta - class Test: - pass - """)) - klass = astroid['Test'] - self.assertTrue(klass.newstyle) - self.assertEqual(klass.metaclass().name, 'ABCMeta') - - def test_nested_metaclass(self): - if PY3K: - self.skipTest('__metaclass__ syntax is python2-specific') - astroid = abuilder.string_build(dedent(""" - from abc import ABCMeta - class A(object): - __metaclass__ = ABCMeta - class B: pass - - __metaclass__ = ABCMeta - class C: - __metaclass__ = type - class D: pass - """)) - a = astroid['A'] - b = a.locals['B'][0] - c = astroid['C'] - d = c.locals['D'][0] - self.assertEqual(a.metaclass().name, 'ABCMeta') - self.assertFalse(b.newstyle) - self.assertIsNone(b.metaclass()) - self.assertEqual(c.metaclass().name, 'type') - self.assertEqual(d.metaclass().name, 'ABCMeta') - - def test_parent_metaclass(self): - if PY3K: - self.skipTest('__metaclass__ syntax is python2-specific') - astroid = abuilder.string_build(dedent(""" - from abc import ABCMeta - class Test: - __metaclass__ = ABCMeta - class SubTest(Test): pass - """)) - klass = astroid['SubTest'] - self.assertTrue(klass.newstyle) - metaclass = klass.metaclass() - self.assertIsInstance(metaclass, scoped_nodes.Class) - self.assertEqual(metaclass.name, 'ABCMeta') - - def test_metaclass_ancestors(self): - if PY3K: - self.skipTest('__metaclass__ syntax is python2-specific') - astroid = abuilder.string_build(dedent(""" - from abc import ABCMeta - - class FirstMeta(object): - __metaclass__ = ABCMeta - - class SecondMeta(object): - __metaclass__ = type - - class Simple(object): - pass - - class FirstImpl(FirstMeta): pass - class SecondImpl(FirstImpl): pass - class ThirdImpl(Simple, SecondMeta): - pass - """)) - classes = { - 'ABCMeta': ('FirstImpl', 'SecondImpl'), - 'type': ('ThirdImpl', ) - } - for metaclass, names in classes.items(): - for name in names: - impl = astroid[name] - meta = impl.metaclass() - self.assertIsInstance(meta, nodes.Class) - self.assertEqual(meta.name, metaclass) - - def test_metaclass_type(self): - klass = extract_node(""" - def with_metaclass(meta, base=object): - return meta("NewBase", (base, ), {}) - - class ClassWithMeta(with_metaclass(type)): #@ - pass - """) - self.assertEqual( - ['NewBase', 'object'], - [base.name for base in klass.ancestors()]) - - def test_nonregr_infer_callresult(self): - astroid = abuilder.string_build(dedent(""" - class Delegate(object): - def __get__(self, obj, cls): - return getattr(obj._subject, self.attribute) - - class CompositeBuilder(object): - __call__ = Delegate() - - builder = CompositeBuilder(result, composite) - tgts = builder() - """)) - instance = astroid['tgts'] - # used to raise "'_Yes' object is not iterable", see - # https://bitbucket.org/logilab/astroid/issue/17 - self.assertEqual(list(instance.infer()), [YES]) - - def test_slots(self): - astroid = abuilder.string_build(dedent(""" - from collections import deque - from textwrap import dedent - - class First(object): - __slots__ = ("a", "b", 1) - class Second(object): - __slots__ = "a" - class Third(object): - __slots__ = deque(["a", "b", "c"]) - class Fourth(object): - __slots__ = {"a": "a", "b": "b"} - class Fifth(object): - __slots__ = list - class Sixth(object): - __slots__ = "" - class Seventh(object): - __slots__ = dedent.__name__ - class Eight(object): - __slots__ = ("parens") - """)) - first = astroid['First'] - first_slots = first.slots() - self.assertEqual(len(first_slots), 2) - self.assertIsInstance(first_slots[0], nodes.Const) - self.assertIsInstance(first_slots[1], nodes.Const) - self.assertEqual(first_slots[0].value, "a") - self.assertEqual(first_slots[1].value, "b") - - second_slots = astroid['Second'].slots() - self.assertEqual(len(second_slots), 1) - self.assertIsInstance(second_slots[0], nodes.Const) - self.assertEqual(second_slots[0].value, "a") - - third_slots = astroid['Third'].slots() - self.assertEqual(third_slots, []) - - fourth_slots = astroid['Fourth'].slots() - self.assertEqual(len(fourth_slots), 2) - self.assertIsInstance(fourth_slots[0], nodes.Const) - self.assertIsInstance(fourth_slots[1], nodes.Const) - self.assertEqual(fourth_slots[0].value, "a") - self.assertEqual(fourth_slots[1].value, "b") - - fifth_slots = astroid['Fifth'].slots() - self.assertEqual(fifth_slots, []) - - sixth_slots = astroid['Sixth'].slots() - self.assertEqual(sixth_slots, []) - - seventh_slots = astroid['Seventh'].slots() - self.assertEqual(len(seventh_slots), 0) - - eight_slots = astroid['Eight'].slots() - self.assertEqual(len(eight_slots), 1) - self.assertIsInstance(eight_slots[0], nodes.Const) - self.assertEqual(eight_slots[0].value, "parens") - - -__all__ = ('ModuleNodeTC', 'ImportNodeTC', 'FunctionNodeTC', 'ClassNodeTC') - -if __name__ == '__main__': - unittest.main() diff --git a/test/unittest_utils.py b/test/unittest_utils.py deleted file mode 100644 index 999f7ed..0000000 --- a/test/unittest_utils.py +++ /dev/null @@ -1,102 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -import unittest - -from astroid import builder, nodes -from astroid.node_classes import are_exclusive - -builder = builder.AstroidBuilder() - -class AreExclusiveTC(unittest.TestCase): - def test_not_exclusive(self): - astroid = builder.string_build(""" -x = 10 -for x in range(5): - print (x) - -if x > 0: - print ('#' * x) - """, __name__, __file__) - xass1 = astroid.locals['x'][0] - assert xass1.lineno == 2 - xnames = [n for n in astroid.nodes_of_class(nodes.Name) if n.name == 'x'] - assert len(xnames) == 3 - assert xnames[1].lineno == 6 - self.assertEqual(are_exclusive(xass1, xnames[1]), False) - self.assertEqual(are_exclusive(xass1, xnames[2]), False) - - def test_if(self): - astroid = builder.string_build(''' - -if 1: - a = 1 - a = 2 -elif 2: - a = 12 - a = 13 -else: - a = 3 - a = 4 - ''') - a1 = astroid.locals['a'][0] - a2 = astroid.locals['a'][1] - a3 = astroid.locals['a'][2] - a4 = astroid.locals['a'][3] - a5 = astroid.locals['a'][4] - a6 = astroid.locals['a'][5] - self.assertEqual(are_exclusive(a1, a2), False) - self.assertEqual(are_exclusive(a1, a3), True) - self.assertEqual(are_exclusive(a1, a5), True) - self.assertEqual(are_exclusive(a3, a5), True) - self.assertEqual(are_exclusive(a3, a4), False) - self.assertEqual(are_exclusive(a5, a6), False) - - def test_try_except(self): - astroid = builder.string_build(''' -try: - def exclusive_func2(): - "docstring" -except TypeError: - def exclusive_func2(): - "docstring" -except: - def exclusive_func2(): - "docstring" -else: - def exclusive_func2(): - "this one redefine the one defined line 42" - - ''') - f1 = astroid.locals['exclusive_func2'][0] - f2 = astroid.locals['exclusive_func2'][1] - f3 = astroid.locals['exclusive_func2'][2] - f4 = astroid.locals['exclusive_func2'][3] - self.assertEqual(are_exclusive(f1, f2), True) - self.assertEqual(are_exclusive(f1, f3), True) - self.assertEqual(are_exclusive(f1, f4), False) - self.assertEqual(are_exclusive(f2, f4), True) - self.assertEqual(are_exclusive(f3, f4), True) - self.assertEqual(are_exclusive(f3, f2), True) - - self.assertEqual(are_exclusive(f2, f1), True) - self.assertEqual(are_exclusive(f4, f1), False) - self.assertEqual(are_exclusive(f4, f2), True) - -if __name__ == '__main__': - unittest.main() - diff --git a/test_utils.py b/test_utils.py deleted file mode 100644 index 7ae9361..0000000 --- a/test_utils.py +++ /dev/null @@ -1,213 +0,0 @@ -"""Utility functions for test code that uses astroid ASTs as input.""" -import functools -import sys -import textwrap - -from astroid import nodes -from astroid import builder -# The name of the transient function that is used to -# wrap expressions to be extracted when calling -# extract_node. -_TRANSIENT_FUNCTION = '__' - -# The comment used to select a statement to be extracted -# when calling extract_node. -_STATEMENT_SELECTOR = '#@' - - -def _extract_expressions(node): - """Find expressions in a call to _TRANSIENT_FUNCTION and extract them. - - The function walks the AST recursively to search for expressions that - are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an - expression, it completely removes the function call node from the tree, - replacing it by the wrapped expression inside the parent. - - :param node: An astroid node. - :type node: astroid.bases.NodeNG - :yields: The sequence of wrapped expressions on the modified tree - expression can be found. - """ - if (isinstance(node, nodes.CallFunc) - and isinstance(node.func, nodes.Name) - and node.func.name == _TRANSIENT_FUNCTION): - real_expr = node.args[0] - real_expr.parent = node.parent - # Search for node in all _astng_fields (the fields checked when - # get_children is called) of its parent. Some of those fields may - # be lists or tuples, in which case the elements need to be checked. - # When we find it, replace it by real_expr, so that the AST looks - # like no call to _TRANSIENT_FUNCTION ever took place. - for name in node.parent._astroid_fields: - child = getattr(node.parent, name) - if isinstance(child, (list, tuple)): - for idx, compound_child in enumerate(child): - if compound_child is node: - child[idx] = real_expr - elif child is node: - setattr(node.parent, name, real_expr) - yield real_expr - else: - for child in node.get_children(): - for result in _extract_expressions(child): - yield result - - -def _find_statement_by_line(node, line): - """Extracts the statement on a specific line from an AST. - - If the line number of node matches line, it will be returned; - otherwise its children are iterated and the function is called - recursively. - - :param node: An astroid node. - :type node: astroid.bases.NodeNG - :param line: The line number of the statement to extract. - :type line: int - :returns: The statement on the line, or None if no statement for the line - can be found. - :rtype: astroid.bases.NodeNG or None - """ - if isinstance(node, (nodes.Class, nodes.Function)): - # This is an inaccuracy in the AST: the nodes that can be - # decorated do not carry explicit information on which line - # the actual definition (class/def), but .fromline seems to - # be close enough. - node_line = node.fromlineno - else: - node_line = node.lineno - - if node_line == line: - return node - - for child in node.get_children(): - result = _find_statement_by_line(child, line) - if result: - return result - - return None - -def extract_node(code, module_name=''): - """Parses some Python code as a module and extracts a designated AST node. - - Statements: - To extract one or more statement nodes, append #@ to the end of the line - - Examples: - >>> def x(): - >>> def y(): - >>> return 1 #@ - - The return statement will be extracted. - - >>> class X(object): - >>> def meth(self): #@ - >>> pass - - The funcion object 'meth' will be extracted. - - Expressions: - To extract arbitrary expressions, surround them with the fake - function call __(...). After parsing, the surrounded expression - will be returned and the whole AST (accessible via the returned - node's parent attribute) will look like the function call was - never there in the first place. - - Examples: - >>> a = __(1) - - The const node will be extracted. - - >>> def x(d=__(foo.bar)): pass - - The node containing the default argument will be extracted. - - >>> def foo(a, b): - >>> return 0 < __(len(a)) < b - - The node containing the function call 'len' will be extracted. - - If no statements or expressions are selected, the last toplevel - statement will be returned. - - If the selected statement is a discard statement, (i.e. an expression - turned into a statement), the wrapped expression is returned instead. - - For convenience, singleton lists are unpacked. - - :param str code: A piece of Python code that is parsed as - a module. Will be passed through textwrap.dedent first. - :param str module_name: The name of the module. - :returns: The designated node from the parse tree, or a list of nodes. - :rtype: astroid.bases.NodeNG, or a list of nodes. - """ - def _extract(node): - if isinstance(node, nodes.Discard): - return node.value - else: - return node - - requested_lines = [] - for idx, line in enumerate(code.splitlines()): - if line.strip().endswith(_STATEMENT_SELECTOR): - requested_lines.append(idx + 1) - - tree = build_module(code, module_name=module_name) - extracted = [] - if requested_lines: - for line in requested_lines: - extracted.append(_find_statement_by_line(tree, line)) - else: - # Modifies the tree. - extracted = list(_extract_expressions(tree)) - - if not extracted: - extracted.append(tree.body[-1]) - - extracted = [_extract(node) for node in extracted] - if len(extracted) == 1: - return extracted[0] - else: - return extracted - - -def build_module(code, module_name=''): - """Parses a string module with a builder. - :param code: The code for the module. - :type code: str - :param module_name: The name for the module - :type module_name: str - :returns: The module AST. - :rtype: astroid.bases.NodeNG - """ - code = textwrap.dedent(code) - return builder.AstroidBuilder(None).string_build(code, modname=module_name) - - -def require_version(minver=None, maxver=None): - """ Compare version of python interpreter to the given one. Skip the test - if older. - """ - def parse(string, default=None): - string = string or default - try: - return tuple(int(v) for v in string.split('.')) - except ValueError: - raise ValueError('%s is not a correct version : should be X.Y[.Z].' % version) - - def check_require_version(f): - current = sys.version_info[:3] - if parse(minver, "0") < current <= parse(maxver, "4"): - return f - else: - str_version = '.'.join(str(v) for v in sys.version_info) - @functools.wraps(f) - def new_f(self, *args, **kwargs): - if minver is not None: - self.skipTest('Needs Python > %s. Current version is %s.' % (minver, str_version)) - elif maxver is not None: - self.skipTest('Needs Python <= %s. Current version is %s.' % (maxver, str_version)) - return new_f - - - return check_require_version diff --git a/tox.ini b/tox.ini index 3a0244f..f35e0d5 100644 --- a/tox.ini +++ b/tox.ini @@ -6,5 +6,5 @@ envlist = py27, py33 deps = logilab-common six -changedir = {envsitepackagesdir}/astroid/test +changedir = {envsitepackagesdir}/astroid/tests commands = python -Wi -m unittest discover -v -p "unittest*.py" diff --git a/utils.py b/utils.py deleted file mode 100644 index ae72a92..0000000 --- a/utils.py +++ /dev/null @@ -1,239 +0,0 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of astroid. -# -# astroid 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. -# -# astroid 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 astroid. If not, see . -"""this module contains some utilities to navigate in the tree or to -extract information from it -""" -from __future__ import print_function - -__docformat__ = "restructuredtext en" - -from astroid.exceptions import AstroidBuildingException -from astroid.builder import parse - - -class ASTWalker(object): - """a walker visiting a tree in preorder, calling on the handler: - - * visit_ on entering a node, where class name is the class of - the node in lower case - - * leave_ on leaving a node, where class name is the class of - the node in lower case - """ - - def __init__(self, handler): - self.handler = handler - self._cache = {} - - def walk(self, node, _done=None): - """walk on the tree from , getting callbacks from handler""" - if _done is None: - _done = set() - if node in _done: - raise AssertionError((id(node), node, node.parent)) - _done.add(node) - 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 - - def get_callbacks(self, node): - """get callbacks from handler for the visited node""" - klass = node.__class__ - methods = self._cache.get(klass) - if methods is None: - handler = self.handler - kid = klass.__name__.lower() - e_method = getattr(handler, 'visit_%s' % kid, - getattr(handler, 'visit_default', None)) - l_method = getattr(handler, 'leave_%s' % kid, - getattr(handler, 'leave_default', None)) - self._cache[klass] = (e_method, l_method) - else: - e_method, l_method = methods - return e_method, l_method - - def visit(self, node): - """walk on the tree from , getting callbacks from handler""" - method = self.get_callbacks(node)[0] - if method is not None: - method(node) - - def leave(self, node): - """walk on the tree from , getting callbacks from handler""" - method = self.get_callbacks(node)[1] - if method is not None: - method(node) - - -class LocalsVisitor(ASTWalker): - """visit a project by traversing the locals dictionary""" - def __init__(self): - ASTWalker.__init__(self, self) - self._visited = {} - - def visit(self, node): - """launch the visit starting from the given node""" - if node in self._visited: - return - self._visited[node] = 1 # FIXME: use set ? - methods = self.get_callbacks(node) - if methods[0] is not None: - methods[0](node) - if 'locals' in node.__dict__: # skip Instance and other proxy - for local_node in node.values(): - self.visit(local_node) - if methods[1] is not None: - return methods[1](node) - - -def _check_children(node): - """a helper function to check children - parent relations""" - for child in node.get_children(): - ok = False - if child is None: - print("Hm, child of %s is None" % node) - continue - if not hasattr(child, 'parent'): - print(" ERROR: %s has child %s %x with no parent" % ( - node, child, id(child))) - elif not child.parent: - print(" ERROR: %s has child %s %x with parent %r" % ( - node, child, id(child), child.parent)) - elif child.parent is not node: - print(" ERROR: %s %x has child %s %x with wrong parent %s" % ( - node, id(node), child, id(child), child.parent)) - else: - ok = True - if not ok: - print("lines;", node.lineno, child.lineno) - print("of module", node.root(), node.root().name) - raise AstroidBuildingException - _check_children(child) - - -class TreeTester(object): - '''A helper class to see _ast tree and compare with astroid tree - - indent: string for tree indent representation - lineno: bool to tell if we should print the line numbers - - >>> tester = TreeTester('print') - >>> print tester.native_tree_repr() - - - . body = [ - . - . . nl = True - . ] - >>> print tester.astroid_tree_repr() - Module() - body = [ - Print() - dest = - values = [ - ] - ] - ''' - - indent = '. ' - lineno = False - - def __init__(self, sourcecode): - self._string = '' - self.sourcecode = sourcecode - self._ast_node = None - self.build_ast() - - def build_ast(self): - """build the _ast tree from the source code""" - self._ast_node = parse(self.sourcecode) - - def native_tree_repr(self, node=None, indent=''): - """get a nice representation of the _ast tree""" - self._string = '' - if node is None: - node = self._ast_node - self._native_repr_tree(node, indent) - return self._string - - - def _native_repr_tree(self, node, indent, _done=None): - """recursive method for the native tree representation""" - from _ast import Load as _Load, Store as _Store, Del as _Del - from _ast import AST as Node - if _done is None: - _done = set() - if node in _done: - self._string += '\nloop in tree: %r (%s)' % ( - node, getattr(node, 'lineno', None)) - return - _done.add(node) - self._string += '\n' + indent + '<%s>' % node.__class__.__name__ - indent += self.indent - if not hasattr(node, '__dict__'): - self._string += '\n' + self.indent + " ** node has no __dict__ " + str(node) - return - node_dict = node.__dict__ - if hasattr(node, '_attributes'): - for a in node._attributes: - attr = node_dict[a] - if attr is None: - continue - if a in ("lineno", "col_offset") and not self.lineno: - continue - self._string += '\n' + indent + a + " = " + repr(attr) - for field in node._fields or (): - attr = node_dict[field] - if attr is None: - continue - if isinstance(attr, list): - if not attr: - continue - self._string += '\n' + indent + field + ' = [' - for elt in attr: - self._native_repr_tree(elt, indent, _done) - self._string += '\n' + indent + ']' - continue - if isinstance(attr, (_Load, _Store, _Del)): - continue - if isinstance(attr, Node): - self._string += '\n' + indent + field + " = " - self._native_repr_tree(attr, indent, _done) - else: - self._string += '\n' + indent + field + " = " + repr(attr) - - - def build_astroid_tree(self): - """build astroid tree from the _ast tree - """ - from astroid.builder import AstroidBuilder - tree = AstroidBuilder().string_build(self.sourcecode) - return tree - - def astroid_tree_repr(self, ids=False): - """build the astroid tree and return a nice tree representation""" - mod = self.build_astroid_tree() - return mod.repr_tree(ids) - - -__all__ = ('LocalsVisitor', 'ASTWalker',) - -- cgit v1.2.1